連載 / / 凵 Language—O リスト 1 冫明リングバッフア・ルーチンのヘッダファイル このプログラムは、サーバーの chargen ポートから ー 1 Ⅱ sl —lsocket gcc ー 0 pump pump. c bucket. c —lthread \ コンパイルのガ去は次のとおりです。 使っています。 無艮文字列発生装置 TCP ポート 19 chargen サービスを void put—waterful—bucket ( ) ; void put—empty—bucket ( ) ; bucket—t get-waterful—bucket ( ) ; bucket—t get—empty-bucket ( ) ; typedef struct bucket *bucket—t ; struct bucket *next ; char data [BUCKETSIZE] ; 10 Ⅱ g Size; struct bucket { #define BUCKETSIZE 16384 #define NUMBUCKETS 5 リスト 2 冫明リングバッフア・ルーチン (bucket. c) 16KB すつのデータを受け取ってリングバッフアを回転 put—waterful—bucket ( ) * 水バケッ管理 get—waterful—bucket ( ) put—empty—bucket ( ) * 空バケッ管理 get_empty_bucket ( ) * BUCKETSIZE ーバケツの最大容量 NUMBUCKETS バケツの総数 * 汎用リングバッフア・ルーチン struct bucket P001 [NUMBUCKETS] ; #include く synch. h> #include "bucket . " bucket—t wfull—q, bucket—t fq—tail , mutex—t fq—lock, cond—t fq—cv , eq— void init—buckets ( ) int Ⅱ ; empty—q; eq—tail ; eq—lock; CV ; → 2 つのノヾケッキュー →バケッキューの末尾 排他ロック →条件変数 empty-q = & P001 [ 0 ] ; 114 wfull—q = fq_tail = NULL; eq-tail = & P001 CNUMBUCKETS-I] ; P001 CNUMBUCKETS—I] . next = NULL ; P001 [ Ⅱ ] . next = & P001 [ n + 1 ] ; for (n = 0 ; Ⅱく NUMBUCKETS—I; n + + ) させながら、 AC で強缶鮗了させられるまで延々と リオド ) を表示し続けます。 これではあまりにもおもしろみに欠けると不満に思う 方は、 pump プログラムを拡張して [start] コマンド や [pause] コマンドを追加してみてはいかがでしよう か ? メインスレッド main() は、 Producer スレッド pumpthread() と Consumer スレッド sinkthread() を 起動したあとは pause() でひたすら眠っているだけなの で、この部分を変更して stdin からコマンドを読んで両ス レッドの振舞いを外部から制御できるように匿すれはよ いのです。 SunOS マルチスレッド・プログラミングの手軽な手引 きとしては、『 Sun Technical Bulletin 』 1994 年 2 月号 に掲載されている、「マルチスレッドアプリケーションの 言と開発」が、初じ、者か最初に読む資料としてお手ごろ ( ピ だと思います。 UNIX MAGAZINE 1994.9
連載 / Li 社 Language—O リスト 3 伊題プログラム (pump ・ c) リングパッフアのサンプル・プログラム * pump ・ c * Usage : pump く chargen—server—address> #include #include #include #include #include #include #include #ifndef #define #endif #ifndef #define #endif く sys/types . h> く sys/socket. h> く netinet/in. h> く thread . 五 > く signal . h> く stdio . h> く netdb . h> INADDR_NONE Oxffffffff IPPORT_CHARGEN 19 I PP ORT_ CHARGEN I NADDR_NONE #include "bucket . h" int prodtid, constid, /*ARGUSED*/ VOid * pumpthread(arg) void *arg ; int Ⅱ ; bucket—t p ; do { dataso ; main(argc , argv) int argc ; char *argv ロ ; int r ; struct hostent *hp , struct sockaddr_in saddr ; struct in_addr remote ; unsigned 10 Ⅱ g ipaddr ; if (argc ! = 2 ) { fprintf (stderr, "Usage : pump く server>\n") ; exit(l); if ((hp = gethostbyname(argv[l] ) ) ! = NULL) remote * (struct in—addr *)hp—>h—addr ; else if ( (ipaddr inet-addr(argv [ 1 ] ) ) ! = INADDR_NONE) remote . s—addr = ipaddr ; else { fprintf (stderr , "%s is unknown\n" , argv[l] ) ; exit(l); saddr. sin—family = AF—INET; saddr . sin_addr remote ; saddr. sin—port = htons (IPPORT-CHARGEN) ; socket (PF—INET , SOCK_STREAM , 0 ) ; dataso r = connect(dataso, &saddr, sizeof (saddr)) ; if (r く 0 ) { perror("connect : " ) ; exit(l); init-buckets ( ) ; p = get—empty-bucket ( ) ; n = read(dataso, p—>data, BUCKETSIZE) ; if (n く = 0 ) break; p—>size = Ⅱ ; put—waterful-bucket (p) ; } while ( 1 ) ; fprintf (stderr, 'socket down?\n") ; return 0 ; /*ARGUSED*/ VOid * sinkthread(arg) void *arg; bucket—t p ; do { p = get-waterful-bucket ( ) ; write(2, put—empty—bucket (p) ; 116 exit(l); shutdown (dataso cleanup() /*NOTREACHED*/ } while ( 1 ) ; r r thr—create (NULL , 0 , pumpthread , NULL , THR_ SUSPENDED ー THR_BOUND , &prodt i d) ; perror ( "pump thread : " ) ; exit(l); thr—create (NULL , 0 , sinkthread , NULL , THR_SUSPENDED ー THR_BOUND , &constid) ; if (r ! = の { perror("sink thread: " ) ; exit(l); UNIX MAGAZINE / *NOTREATECHED* / pause ( ) ; thr—continue(constid) ; thr-continue (prodtid) ; signa1(SIGINT, cleanup) ; 1994.9
連載 / Li Language bucket_t get—waterful—bucket ( ) struct bucket_t p ; mutex—lock (&fq—lock) ; mutex—unlock(&eq—lock) ; eq—tail = p; eq—tail—>next = p; else { cond—signal(&eq—cv) ; empty—q = eq—tail = p ; if (empty—q = = NULL) { mutex—lock(&eq—lock) ; p—>next = NULL ; bucket—t p ; put—empty—bucket (p) void return p ; mutex-unlock (&eq-lock) ; empty-q = empty—q—>next ; P = emptY—q; cond—vait (&eq—cv , &eq—lock) ; while (empty—q = = NULL) mutex—lock(&eq—lock) ; bucket—t p ; get—empty-bucket ( ) bucket_t mutex—unlock(&fq—lock) ; fq—tail = p; fq—tail—>next = p; else { cond—signal(&fq—cv) ; wfull—q = fq—tail = p; if (wfull—q = = NULL) { mutex—lock(&fq—lock) ; p—>next = NULL ; bucket—t p ; put—waterful—bucket (p) void return p ; mutex—unlock(&fq—lock) ; wfull—q = wfull—q—>next ; p = wfull—q; cond—wait (&fq—cv , &fq—lock) ; while (wfull—q = = NULL) 水バケッキューは空つば ? キューの先頭バケツをとる キューを 1 つ進める →水バケツを返す →水ノヾケツを待って休眠する・・・・・ [ a ] →水バケッキューは空つは。 ? →水バケツをキューへ置く →水バケツを待って休眠している 空バケツをキューに置く →空バケッキューは空っ : ま ? 空バケツを返す キューを 1 っ進める キューの先頭バケツをとる 空バケツを待って休眠する・・・・・ [ b ] →空バケッキューは空つほ ? →追加する キューの末尾へ水バケツを →スレッドを起こす・・・・・・ [ a ] →追加する キューの末尾へ水バケツを →スレッドを起こす・・・・・・ [ b ] →水バケツを待って休眠している UNIX MAG AZIN E 1994.9 115
連載 / / Li Language— 図 1 スレッドとリングパッファ Da Source 図 3 スレッド間同期窈士組み G b variables 土 n セ土セ土ー mutex lock; cond t cv; Producer Th read .. CO れ作 0 ~ ア 0 ル Thread A WaterfuI Bucket Queue Em pty Bucket Queue mutex lock(&lock) ー while (condition ! = OK) cond wait (&CV ′ &lock);< mutex unlock(&lock); SLEE P...zzz Thread B Consumer Th re ad mutex lock(&lock); cond 土セ土 on デ OK; cond signal (&cv) mutex unlock(&lock); Da Sink WAKEUP!! 図 2 2 つの待ち行列 empty_q のホインタ ( X サーバーへのイメージ転送オーバーヘッド を縮小するため ) が配置されていました。 図 1 に示したように リングバッファ機構には 2 つの キューがあります。空のバケッカ嗹なる線形リスト ( 図 2 の上段 ) と、データありバケツが連なる糸卿「彡リスト ( 図 2 の 下段 ) です。バケツはつねにキューの末尾に追加され、キ ューの頁から順番に取り去られます。 2 つのキューは先 頭と末尾か互い違いに結び合わされて全体としてリンク構 造をなし、バケツは 2 つのキューを交壤こ循環します。 スレッド間同期の仕組み 次に条件変数によるスレッド間同期の仕組みをすこし 掘り下げて解説しましよう。図 3 を見てください。この 図では、 2 つのスレッドが条件変数同期プリミテイプ cond-wait() と cond-signal() を使って、実行の一時停 止と再開をおこなう様子か示されています。 A は条件成立 を待って f 和民するスレッド、 B がその眠りを覚ますスレッ スレッド間同期を司るのは大域変数 condition ( ここ では整委りです。 condition はスレッド間で共有されま す。現実のコードでは condition は共有される資源その もの ( たぶん資源へのポインタ ) を表すこともありますし、 この bucket 構造体ではデータの↑褓嬾頁域か精造体内部 たんに停止の有無を孑ミするフラグやカウンタだったりも に静的にとってありますが、実際にこれを応用して使う場 します。図 3 では、整委大域変数 condition はスレッ 合は外部↑褓内領域へのポインタであってもよいでしよう。 ド A の実行継続可能性を指示するフラグとして使われて srekcah のネットワーク・プログラムで ( 尉冓造体内部には います。 データ領域はなく、代わりに SystemV 共有メモリ領域へ この図に示したように 一般にスレッド間同期操作を SIze data[ ] S IZe data[ ] S IZe data[ ] Qu 0 たな 0U0 イ wful l_q SIze data[ ] SIze data[ ] SIZe data[ ] Producer はリフト山麓駅、 Consumer はリフト項上駅 で、データストリームは山を登るスキーヤーというわけ です。 キューは片カ旬線形リストで表現できます ( 図 2 ) 。デー タの褓内単位 ( バケッ ) はとりあえす以下のような C 構造 体で定義することにしましよう。 struct bucket { 10 Ⅱ g SIZe ; char data [BUCKETSIZE] ; struct bucket *next ; 0 112 UNIX MAGAZINE 1994.9
連載 / UN Ⅸの道具箱ー① 表 2 gs が出力できるビットマップ・フォーマット ドライノ銘 フォーマット名 A plain bit bucket device bit Monochrome MS Windows BMP file format bmpmono 4bit (EGA/VGA) BMP file format bmp16 8bit ( 256C010r ) BMP file format bmp256 24bit BMP file format bmp16m Monochrome GIF file format gifmono 8bit C010r GIF file format gif8 Monochrome P CX file format pcxmono 8bit gray scale PCX file format pcxgray OIder color PCX file format (EGA/VGA, 16C010r pcx16 Newer C010r PCX file format ( 256C010r pcx256 PortabIe Bit map (plain format) pbm Portable Bitmap (raw format) pbmraw PortabIe Graymap (plain format) pgm Portable Graymap (raw format) pgmraw Portable Pixmap (plain format) PortabIe Pixmap (raw format) ppmraw TIFF/F ()3 FAX) tiffg3 図 1 gs を使って PS ファイルを変換 gs にはさまざまな使い方がありますが、一殳のユーサー にとっては、、 PS ファイルを画面に表示する " のが一番の 0 目的だと思います。そのためには、 gs を起動するときに、 引数として画面に表示したいファイル ( もちろん PS ファ イル ) を指定します。すると、 Ghostscript というウイン 〇 ドウカ鯛いて、ファイルの中身・・・・・・ではなく、ファイル の中身を翻駅した (interpreted) 結果が表示されます。 この場合、 gs の仕事の流れはだいたい次のようになり ます。 1. PS ファイルを読み込む。 2. PS ファイルの内容を翻孺尺する。 3. X ウインドウ上に表示するためにその制御コード ( 表示 するためのコード ) を生成する。 % gs 4. 画面 (X ウインドウー E) に表示する。 done . lnitializing ・ lnstalling Kanj i fonts . done . Ghostscript 2.6.1 ( 5 / 28 / 93 ) gs は X ウインドウ上に PS ファイルを表示するだけで Copyright (C) 1990 ー 1993 Aladdin Enterprises , なく、はかのテパイスに対するコードに翻訳することもで Men10 Park, CA. A11 rights reserved. Ghostscript comes with NO WARRANTY: see きます。具イ勺には、 the file COPYING for details . GS> ■ 1. 各重のプリンタに対する缶剏コード 2. さまざまなビットマップ・フォーマット と出力されて、ウインドウカけば、 gs がインストール されています。終了させるに ( お c 力い D を入力します 4 。 か挙げられます。 1 は表 1 の各種プリンタに対応していま す。 2 に関しては、表 2 に示したフォーマットへの変換が 3 サイトによって違いますが、 / us ツ 1 。 cal / bin / などにあることが多い でしよう。 可能です。勺には図 1 のようにして、 PS をさまざま 4 どちらを入力するかて働きカ嗹いますが、ここではその差異までは紹介し な形式に変換し、多種多様なデバイスやファイル・フォー ません。 み 込 読 printer PS 出力 丸を描け bitmap サ・仕事人 Ghostscript Ghostscript のなかで、実際にインタープリタとして 働くのは、 gs というプログラムです 3 。皆さんがお使いの 計算機で gs と入力してみてください。画面に 103 UNIX MAGAZINE 1994.9