f ck システムコール ファイルに関するロックであれは、専用の flock システ ムコールを用いることができます。 flock システムコール は、 2 つの引数をとります。最初の引数はロックの対象と なるファイルを指定します。ただし、。 pen システムコー ルによりオープンしておき、そのファイルを示すファイ ル・ディスクリフタを指定します。 ファイルをロックするのに、オープンしてから利用す るのはおかしいと感じるかもしれません。あるプロセスが ロックされてしまったら、別のプロセスはオープンできな いのではと思えます。 しかし、 UNIX カ甘是供しているロックは、アドバイサ、 リー・ロック " と呼はれるものです。このロックでは、す でにロックがかかっているファイルをオープンすることを 禁止しません。それどころか、ファイルを読み書きするこ とも自山にできます 1 。このロックは、ファイルを利用し ようとするプロセスすべてがロックについて知っていて、 ほかのプロセスがロックをかけていないときにのみ使用す るという紳士協定のもとに用いられるのです。 引数に戻ります。 2 番目の引数には、どのようなロック をかけるのかを指定します。 LOCK-UN : ロックを解除します。 LOCK-NB : ロック操作でプロックしないようにしま LOCK-EX : 排他ロックをかけます。 LOCK-SH : 共有ロックをかけます。 す。 122 す。 1 もちろん、パーミッションなどのはかの条 f 牛を濯たしている必要があリま クは同時にいくつかけられていてもかまいません。 いようにするためです。したがって、読出しのためのロッ するのは、読み出しているあいだに書き換えられたりしな はれることもあります。ファイルを読み出すためにロック これらのロックは、読出しロックと書込みロックと呼 孑非他ロックはかけることができません。 ません。さらに、共有ロックが 1 つでもかかっていると、 一方、排他ロックは 1 つのプロセスしかかけることができ 共有ロックは複数のプロセスがかけることができます。 これに対し、書込みのほうは、ほかのプロセスか書き込 んでいるときはもちろんのこと、読み込んでいるプロセス がある場合にもできません。そのため、排他ロックはほか にロックをかけているプロセスがない状態でしか利用でき ないのです。 LOCK-SH や LOCK-EX を指定する場合、ロックを かけられるときにはすぐにシステムコールは終了し、戻り 値として 0 カされます。しかし、はかのプロセスがロッ クしているためにロックカ功、けられない場合はロックがか けられるようになるまでプロックします。そオ LJ リ汐 ) 理由 ( たとえは、ロック用に指定したファイル・ディスクリフ タか麒っているなど ) でロックがかけられない場合は、シ ステムコールは一 1 を返し、エラーの理由が errn 。変数に オ褓内されます。 しかし、ロックの不鶤頁として LOCK-NB を論理和て結 んで指定した場合には、ロックがかけられなかったときの 動作が変わります。通常はプロックしてしまうのですが、 この場合にはプロックせすにエラーとして扱います。ただ し、この場合には errno 変数には EWOULDBLOCK かオ褓内されているため、通常のエラーとは区別することが 可能です。 flock システムコールを用いてさきはどの例を記述する と、次のようになります。 #include く stdio . h> #include く unistd . h> #include く fcntl . h> #define LOCK int main() int int "/tmp/lock" fd; pid; pid = getpid() ; if ( ()d = open(LOCK, O_WRONLY IO_CREAT, 0666 ) ) く 0 ) { fprintf (stderr, "Can't open lock file %s\n" , LOCK) ; exit(l); if (flock(fd, LOCK—EX) く 0 ) exit(l); printf ( " 危険領域に入る (%d)\n" s1eep(3) ; , pid) ; UNIX MAGAZINE 1998.11
にかかわる情報をその構造体に埋めています。 l-start は ( 2445 ) 危険領域 A から出る ( 2446 ) 危険領域 B から出る 開始位置です。これは l-whence に指定した位置からの相 ( 2447 ) 危険領域 B に入る 対位置で示します。 l-whence には lseek で用いるのと同 ( 2444 ) 危険領域 A に入る ( 2444 ) 危険領域 A から出る 様な値 (SEEK-SET 、 SEEK-CUR 、 SEEK-END) を ( 2447 ) 危険領域 B から出る 指定します。 1 」 e11 はロックをかける領域の長さを示しま [ 3 ] + Done [ 2 ] + Done す。この例のように 0 を用いた場合には、ファイルの終端 [ 1 ] + Done までを表します。この例では、 l-whence が SEEK-SET で、 lstart が 0 ですからファイルのう頁から領域が始ま 危ド頁域 A と危険領域 B の 2 つの領域に対して、それ り、凵 en が 0 ですからファイルの終端まで、つまりファ ぞれ 1 つのプロセスだけが入っていることが分かるでしょ イル全体がロックの対象となっています。 うか。さらに述べるなら、同時に 2 つのプロセスが処理を l-type にはロックの不鶤頁を指定します。ここて指定し している時点もあるということですが、お分かりいただけ ているのは排他 ( 書込み ) ロックです。ほかの値としては、 るでしようか。 共有 ( 読出し ) ロックの FA{DLCK 、ロックを解除する いささかプログラムが面倒になるとはいうものの、ファ F-UNLCK があります。ここで注意してほしい点があり イルの一部分にだけロックがかけられるのはとても便利で ます。 F-RDLCK を実行するにはファイルに対する言もム す。これがあれは flock システムコールはいらないかも みができなけれはならず、 F-WRLCK を実行するにはフ しれません。しかし、レコードロックはその処理も複雑に ァイルに対する書込みができなければならないということ なってしまいます。ファイル全体をロックしてもかまわ です。 ないのであれば、 flock を利用したほうか効率はよくなり 構造体か準備できたら、いよいよ fcntl を使ってロック ます。 をかけますか : ロック時の操作としては F-SETLKW を 利用します。この操作では、ロックがかけられないときは かけられるようになるまでプロックします。プロックした 今回は、複数のプロセスか 1 司時に処理をしてしまわない ように排他制御をおこなうためのロックを紹介しました。 くない場合には F-SETLK を利用します。 ロックのために必要な機構から紹介し、実際に UNIX で ロックを角鄲余する場合、もちろん F-UNLCK を指定し ロックを用いる方法も、シェル・スクリプトの場合や C ながら F-SETLK や F-SETLKW を実行してもよいの 言語の場合など、いくつかをとりあげました。また、 Sys- ですが、ロック対象のファイル・ディスクリプタを close temV 系に端を発するレコードロックに関しても説明しま するだけでもロックを解除することができます。 した。 せつかくレコードロックの機能があるのですから、それ ロックなんて複雑なことはいらないと思っている人もい も試してみることにしましよう。 : 頁のバイトを危頁域 るかもしれません。しかし、ちょっとしたことでもロック A 、次のバイトを危険領域 B とします。これに合わせて は必要になります。 1 つしかない資源を多数のプロセスが プログラムを修正すると、 A は Lstart が 0 で LIen が 1 アクセスする場合には必須の技術です。今回紹介した力法 に、 B は Lstart が 1 で l-len が 1 になります。これら のうち 1 つくらいは應えておくとよいのではないでしよう の修正をおこなった 2 つのプログラムを用意し、それぞれ か。きっと役に立つ日がくるはすです。 を 2 っすっ実行・してみると、たとえは次のような出力か得 ( たしみ・ひさかす ) られます。 [ 1 ] 2444 [ 2 ] 2445 [ 3 ] 2446 ( 2445 ) 危険領域 A に入る ( 2446 ) 危険領域 B に入る ☆ 126 UNIX MAGAZINE 1998.11
図 3 pw-lock 関数 int pw—lock ( ) lockfd = open (-PATH—MASTERPASSWD , O_RDONLY , 0 ) ; if (lockfd く 0 Ⅱ fcntl(lockfd, F_SETFD, 1 ) err ( 1 , " 7 _PATH_MASTERPASSWD) ; if (flock(lockfd, LOCK—EXILOCK—NB)) errx(l , "the password db file is busy") ; ー 1 ) return (lockfd) ; ま $/etc/master. passwd ファイルをオープンしてい ます。このファイルを直孑矗集する必要はないので、言も囚 み専用としてオープンしています。言もムみ専用としてオー プンしたファイルに対して排他 ( 書込み ) ロックをかける こともできます。次に、ファイルが正しくオープンでき ていることを石忍するとともに fcntl システムコーノレを 使って close-on-exec フラグを設疋しています。次が間 題のロックです。ここでは排他ロックをプロックしないよ うにかけています。ロックがかけられなかった場合には、 メッセージを出して終了してしまいます。また、ロックの 解除はおこなっていません。というか、 vipw を終了する ことでロックを解除しています。 厄 ckf コマンド もう 1 つ例をみてみましよう。 /usr/bin/lockf コマン ドです。何をするコマンドかはあとで説明するとして、な かを見てみましよう。このコマンドもファイルのロック をおこなっていますが、ロックは flock システムコール を用いるのではなく open システムコールでおこなってい ます。 static int acquire—lock(const char *name) int fd; if ( ()d = open(name, O_RDONLY! O_CREATI O_EXLOCK IO_NONBLOCK, 0666 ) ) if (errno EAGAIN Ⅱ errno = EINTR) return ー 1 ; err (EX_CANTCREAT , "cannot open %s " 124 return fd ; name) ; フラグとして O-EXLOCK と O-NONBLOCK カ甘旨 定されています。すなわち、ファイルをオープンするとき に排他ロックをかけるが、ロックがかけられなかった場合 にはプロックせすにエラーとして終了するということを意 味しています。次の行では、 errno の値を調べています。 EAGAIN は、通常はプロックしてしまう処理であること を示しています。これは排他ロックがかけられないことを 意味します。 EINTR は、システムコールの実行中に割 込みがかかったことを示します。これ以タ ) 工ラーの場合 には、エラーメッセージを出力してから関数を終了してい ます。 それでは、ロックの解除はどうなっているのでしよう static void cleanup(void) if (keep) flock(lockfd, LOCK—UN) ; else unlink (lockname) ; こちらはごく簡単です。 keep フラグは、ロックファイ ルを残しておくかどうかを示すフラグです。ファイルを残 す場合には、 flock システムコールを使ってロックを解除 しています。ファイルを残す必要がない場合には、 unlink システムコールを利用してファイルを削除します。ファイ ルを削除してしまえは、そのファイルに対してロックをか けることはできなくなるのですから、けっきよくはロック を解除したのと同し効果か得られます。 さて、 lockf がどんなコマンドかは説明せすにファイル のロックの仕方だけを紹介しましたが、このコマンドはシ ェル・スクリプトで用いると便利です。ファイルをロック しながらコマンドを実行してくれるのです。コマンドの使 UNIX MAGAZINE 1998 ユ 1
用形式は、 UNIX MAGAZINE 1998.11 ください。ファイル全体をロックする方法しかないとす たとえは、大きなデータベース・ファイルを想像して けて十分なのでしようか。 全体をロックするというものでした。しかし、この方法だ これまて紹介してきたロックの去は、すべてファイル レコードロック しようか。 グラムとくらべれはぐんと簡単になっているのが分かるで ちょっと面倒なことになっていますが、これまでのプロ ロックした状態でおこなうコマンドが 3 つあるために echo " 危険領域から出る ( $ $ ) " sleep 3 ; echo " 危険領域に入る lockf /tmp/lock sh —c # ! /bin/sh ンドを用いると次のように書き直すことができます。 最初のほうて紹介したシェル・スクリプトは、このコマ マニュアルページで紹介されています。 この 75 という値は一日判勺な失敗を表すもので、 sysexits ともに 1 。 ckf コマンドの終了ステータスを 75 にします。 す。タイムアウトしたときには、メッセージを出力すると イにロックがかけられなけれはコマンドは失敗となりま ちます。タイムアウトに 0 を指定した場合、コマンドの実 指定された場合、 lockf コマンドは指定された時間まで待 ロックがかけられるまでプロックします。タイムアウトが 単位で指定します。何も指定しないと、 lockf コマンドは -t オプションはロックを得るまでのタイムアウトを秒 都合がよい場合に指定します。 で用いる場合などで、よけいな出力はおこなわないほうが いようにするオプションです。シェル・スクリプトのなか ンドではなく、 lockf コマンド自身の出力 ) をおこなわな —s は lockf コマンドからの出力 ( 1 。 ckf か夫行するコマ す。 さきほどの例で述べた keep 変数を設定するためのもので ー -- k はロックファイルを削除しないためのオプションで、 ロックをかけながらコマンドを実行してくれます。 となっており、引数として指定したファイルに対して排他 lockf [-ks] C-t 時間 ] ファイルコマンド [ 引数 ] プログラミング・テクニック ると、ある部分を更新しているプロセスがいるあいだは、 まったく関係ない部分を更新しようとしていてもその他の プロセスはプロックされてしまいます。ファイルの一 ; 分 だけをロックすることができれば、同時に複数の箇所で更 新できるので効率的です。 これまて紹介してきた flock システムコールは、 BSD 系の UNIX で提供されてきた機能です。 SystemV 系 の UNIX では、ファイル全体ではなくファイルの一部 に対するロック ( レコードロックといいます ) も可能な ロッグ冓カ甘是供されてきました。現在では、このイ丗冓を FreeBSD でも利用できるようになっています。 レコードロックをおこなう場合、ファイルのなかのどの 部分にロックをかけるのかを指定する必喫があります。そ のため、必烈勺に lockf システムコールよりも複雑な処理 が必要になります。これも実際の例を見ながら話を進める ことにしましよう。 #include く stdio . h> #include く unistd . h> #include く fcntl . h> #define LOCK int main() int int " /tmp/lock" pid; fd; struct flock pid = getpid() ; fl ; if f 1 ( ()d = open(LOCK, lock file %s\n", LOCK) ; fprintf (stderr, "Can't open O_WRONLY IO—CREAT , 0666 ) ) く 0 ) { exit(l) ; f1. l_start = fl . 1 ー le Ⅱ = 0 ; SEEK_SET ; . l_whence fl ・ l_type = F_WRLCK ; exit(O); printf ( " 危険領域から出る (%d) \n" , pid) ; s1eep(3) ; p て i Ⅱ tf ( " 危険領域に入る (%d)\n", pid) ; fcntl(fd, F-SETLKW, &fl) ; 125 でと同様です。ただし、 flock 構造体を準備して、ロック ロックをかけるファイルをオープンしておくのはこれま
プログラミング・テクニック p て intf ( " 危険領域から出る (%d)\n" flock(fd, LOCK_UN) ; exit(O) ; pid) ; int main() int int fd; pid; これか基本的な使い方です。つまり、最初にファイルを オーフンしておき、ロックをかけて、ファイルを書き換え ます ( ここではたんに sleep ですが・・・・・・ ) 。処理カ鮗れば ロックを外します。 」「記のようにロックをかけて処理をおこない、すぐに終 了してしまうプログラムの場合、もうすこし簡単にプログ ラムすることができます。それには、 open システムコー ルと close システムコール ( さらには exit 関数 ) の機能を 利用します。 #include く fcntl . h> #include く unistd . h> #include く stdio . > くすることが可能です。 これを利用すると、さきはどのプログラムはもうすこし短 うな場合には、たんに exit 関数を呼び出せはよいのです。 て、ファイルのロックを解除してすぐに終了してしまうよ すべてのファイルは自重加勺にクローズされます。したがっ にロックは解除されます 2 。また、 exit 関数を呼び出すと そのファイル・ディスクリプタをクローズすると、自勺 ルで明示的におこなわなくても、 close システムコールで 次に、ロックを角余する去です。 flock システムコー さい。 うな場合には、通常の O-NONBLOCK も指定してくだ られるまでプロックします。プロックしては都合が悪いよ ファイルにロックがかけられないときには、ロックがかけ ファイルに対して排他ロックをかけます。この場合にも、 対して共有ロックをかけ、 O-EXLOCK はオープンする て指定します。 O-SHLOCK はオープンするファイルに 数に O-SHLOCK か O-EXLOCK を論理和でつなげ できます。そのためには、 open システムコールの第 2 引 でファイルをオーフンすると同時にロックをかけることが ますロックをかける部分ですが、 open システムコール #define LOCK "/tmp/lock" 2 dup システムコールの景見も考えると、正確には、、、ロックをかけたファ イルを指しているすべてのファイル・ディスクリプタをクローズする " で す。 UNIX MAGAZINE 1998.11 pid = getpid() ; if ( ()d = open(LOCK, O_RDONLY ー lock file %s\n", LOCK) ; fprintf (stderr, " C open O_CREAT IO_EXLOCK , 0666 ) ) く 0 ) { vipw での実際 exit(0) ; printf ( " 危険領域から出る (%d)\n", pid) ; s1eep(3) ; pri Ⅱ tf ( " 危険領域に入る (%d)\n", pid) ; exit(l); れは pw-util.c ファイルの pw 」 ock 関数でおこなってい それでは、ロックに関係する部分をみてみましよう。 です。 ても、もともとの master. passwd か残っているので安心 最後の段階で、たとえば一印ファイルかイ咫裴されたとし イ求する。 5. 編集結果を角斤して新たなパスワード・ファイルとして 4. コピーした一時ファイルを編集する。 ーする。 3. /etc/master. passwd ファイルを一時ファイルにコピ ックをかける。 2. /etc/master. passwd ファイルをオープンして孑非他ロ 1. 各種の初期化をおこなう。 めます。 れることになるので、 こでは褪各を紹介しておくにとど vipw コマンドの重川を細かく説明するのは論旨から外 の状態に戻せるようにするためです。 これは、ファイルを誤ったかたちで変更してしまっても元 コピーした一時ファイルを編集することになっています。 のファイルを直接編集するのではなく、このファイルを このファイルを修正することです。しかし実際には、 ファイルに格納されています。 vipw コマンドの目的は、 FreeBSD では、ノ、スワードは /etc/master. passwd ます ( 図 3 ) 。 123
プログラミング・テクニック ん。したがって、リンクを用いる方法のほうがお得といえ たことを示します。したがって、ほかのプロセスがすでに ます。 ロックをかけていたことを表しています。ほかの理由で失 敗した場合には、何隻繰り返してもやはり失敗となってし リンクを作成するガ去では、シンポリック・リンクを利 まうでしようから、再度ロックを試みるのは無意味です。 用する方法とハードリンクを利用するガ去とがあります。 あきらめるほうがよいでしよう。 よけいな領域を使わなくてすむのはハードリンクを用いる この方針にのっとってフログラムを作成すると、次のよ 去ですが、この場合にはすでに存在すると分かっている うになります。 ファイルからリンクを張る必要があります。また、 クを張るファイルとロックファイルは同一のファイルシ #include く stdio . h> #include く unistd . h> ステムに置カれていなければならないという制限もありま #include く sys/errno. 五 > す。ロックファイルを作成するためにその元となるファイ #define LOCK " /tmp/lock" ルまで作成するというのは、なんだかもったいないような 気がします。 一方のシンポリック・リンクの場合は、リンクか指す ファイル尉寺するための領域は必要になるものの、ハー ドリンクのような制限はありません。可能であれは、 で紹介したようにシンポリック・リンクを用いるはうがよ いと思います。 int main() buf [ 6 ] ; pid; sprintf (buf , "%d", pid = getpid()) ; while(symlink(buf, LOCK) ) if (errno ! = EEXIST) exit(l) ; printf ( " 危険領域に入る (%d)\n" , pid) ; s1eep(3) ; pri Ⅱ tf ( " 危険領域から出る (%d)\n", pid) ; un1ink(LOCK) ; exit(0); char int ク ッ ロ の で 五ロ 次は、 C 言語を利用している場合のロックの実現方法で す。シェル・スクリプトて利用したガ去をそのまま使うこ とができます。つまり、 mkdir 、 link 、 symlink システ このフログラムでは、たとえは / tmp ディレクトリが存 ムコールを利用してロックファイルを作る方法です。ロッ 在しなかった ( それはそれで、危険な状態ですか ) 場合に クファイルを作成するシステムコールを実行して、成功す も、正しく ( ? ) ェラー終了することができます。 れはロックがかけられたことになります。 C 言語を用いる場合には、 open システムコールや ただし、失敗したときには注意が必要です。システムコ creat システムコールもロックファイルの作成に用いるこ ールが失敗した場合には、なぜ失敗したのかを errno 変数 とができます。 の値から卩ヾます。コマンドの場合にはメッセージが出力 open システムコールは、第 2 引数としてファイルを されましたが、システムコールの場合には errno 変数にな どのようにオープンするのかを示すフラグを指定します。 ぜ失敗したのかを示す値かオ絲タされます。たとえは sym- ここに O-CREAT と O-EXCL の両方を指定するので link システムコールを実行した場合には、引数として指定 す。 O ー CREAT カ甘旨定されていると、ファイルが存雀し した値が不正な場合や、ファイルシステムがいつばいに ない場合には新たにファイルを作成するようになります。 なってしまっている場合など、いくつかの理由で失敗する O-EXCL カ甘旨定されていると、すでにファイルが存す ことがあります。 る場合にはエラーとなります。つまり、ファイルがまだ存 いま注目しているのは、すでにロックファイルか存在す 在しない場合にはファイルを作成し、ファイルか存在する るときに失敗する場合です。システムコールが失敗した場 場合にはエラーとなります。この挙動はシンポリック・リ 合には、 errno 変数の値が EEXIST となっていることを ンクと同様で、ロックファイルの作成に用いることができ 石忍する必要があります。この値が EEXIST なら、第 2 ます。 引数として指定したパス名のファイルがすでに存在してい 121 UNIX MAGAZINE 1998.11
プログラミング・テクニック うになっています。 これとは別の去もあります。ハードウェアの助けを借 りて、値の検と代入を邪魔されることなく 1 つ檗作と しておこなう命令を使う去です。このような命令があれ ば、値を調べているあいだにはかのプロセスが危険領域に 入るということはなくなります。この不可分な操作のこと をアトミックな操作と呼びます。これを孑鬮以的なコードで 当してみましよう。 int lock = 0 ; のロックです。ここでは、アトミックな操作であることが ロックにとってきわめて重要だということを認識してほし かったまでです。 シェル・スクリプトでのロック シェル・スクリフトもフログラムですから、シェル・ス クリプトをしているときに危険領域か現れてしまい、 排他制御の手段か必要になることがあります。このような 場合には、ロックファイルを作成することで排他制彳卸がで きます。 # ! /bin/sh LOCK=/tmp/10ck 一三ロ void begin—critical() (TAS(10ck) ) while —critical() void end while [ —f $LOCK ] touch $LOCK echo " 危険領域に入る sleep 3 echo " 危険領域から出る ( $ $ ) " rm —f $LOCK done do lock 0 ; TAS 関数がハードウェアの力を借りて作った関数で、 lock 変数の値を返しながら変数に値 1 を代入するという ものです。もちろん、 TAS はアトミックな操作として実 exit 0 現されています (TAS は Test And Set の略です ) 。 ところが、この方法ではうまくいきません。この去は 最初にプロセスが危険領域に入ろうとするときには、 さきはどのロック変数の代わりにロックファイル ( / tmp / lock は 0 となっているため TAS は 0 を返します。 while lock) か存在しているかどうかを使って状態を表していま の条件が真とならないので、すぐに危険領域に入ること す。しかし、ロックファイルを卩ヾたり作成したりするた ができます。次のフロセスが危険領域に入ろうとすると、 めに 2 つの命令を使っているので ()f $LOCK と touch TAS は 1 を返すために while ループを回り続けることに の部分です ) 、複数のプロセスが危険領域に入ってしまう なります。最初に危険領域に入ったプロセスが危頁域を ことがあります。 抜けると lock に 0 が代入され、初めて次のプロセスが危 ド頁域に入れるようになります。 % sh 10Ck1 & sh 10Ck1 & sh 10Ck1 [ 1 ] 16206 2 つの鮹去を紹介しましたが、どちらもビジーウェイ [ 2 ] 16207 トやスピンロックと呼はれる技法を使っています。こう ( 16206 ) 危険領域に入る ( 16207 ) 危険領域に入る 書くといかにも難しい技術のように思えるかもしれません ( 16206 ) 危険領域から出る が、たんにテストを繰り返し実行しているという意味です ( 16207 ) 危険領域から出る 危険領域に入る ( 16208 ) (while 文の部分です ) 。しかし、これは CPU 資源の太 危険領域から出る ( 16208 ) 遣いともなりますから、オペレーティング・システムでは Done このような技法はあまり用いられません。オペレーティン グ・システムに関する鶚籍などではたいてい、このあとで よⅵ東されたセマフォやモニターに関する話題へと進ん たった 3 つのプロセスを走らせただけで、危険領域に でいくことになります。しかし、こではそれらの話題に 同時に 2 つのプロセスが入ってしまいました。これでは使 は触れません。なんといっても、今回のテーマはファイル いものになりません。 [ 2 ] sh 10Ck1 sh 10Ck1 119 UNIX MAGAZINE 1998.11
ロックではアトミックな操作であることか重要でした。 今回も、なんとかしてファイルの存在のチェックとファ イルの作成をまとめておこなわなければなりません。シェ ル・スクリプトから利用するのであれば、 ・ディレクトリの作成 ・リンクの作成 が有効でしよう。どちらの場合も、まだ作られていなけ れはファイルが作成されますし、すでに作られていれば工 ラーが発生します。 % mkdir lock % mkdir lock mkdir: lock: Fi1e % rmdir 10Ck % 1 -s $ $ lock % 1 Ⅱ -s $ $ lock exists 1 Ⅱ : lock: Fi1e exists % rm lock こではリンクとしてシンポリック・リンクを使い、さ リンク元としては存在していないファイルを指定し ています。このとき、たんに存在しないファイルを指定し ているのではなく、ロックファイルを作成したプロセス ID を使っています。シェル・スクリプトを実行している シェル ( もしくは、対言乱勺に実行しているシェル ) のプロ セス ID は、 $ $ で得られることを思い出してください。 の値を使うかどうかは別にして、ロックをかけているプロ セスが分かるのはよいと思いませんか ? では、このシンポリック・リンクを用いる方法で実際に while が until となっていることです。また、 ln コマン ドからはエラーメッセージが出力されるので、エラーメッ セージを捨てるためのリダイレクションもおこなってい ます。 このプログラムを実行しても、同時に複数のプロセスが 危険領域に入ることはありません ( 誌面の都合上、て折 % sh 10Ck2 & sh 10Ck2 & sh 10Ck2 & り返しています。以ード同様 ) 。 ( 25835 ) ( 25835 ) ( 25831 ) ( 25831 ) ( 25833 ) ( 25833 ) ( 25834 ) ( 25834 ) ( 25832 ) ( 25832 ) ( 25830 ) ( 2583 の sh 10Ck2 & sh 10Ck2 & sh 10Ck2 危険領域から出る 危険領域に入る 危険領域から出る 危険領域に入る 危険領域から出る 危険領域に入る 危険領域から出る 危険領域に入る 危険領域から出る 危険領域に入る 危険領域から出る 危険領域に入る [ 5 ] 25834 [ 4 ] 25833 [ 3 ] 25832 [ 2 ] 25831 [ 1 ] 25830 [ 5 ] [ 4 ] [ 3 ] [ 2 ] [ 1 ] 十 十 Done 十 Done 十 Done 十 D one sh 10Ck2 sh 10Ck2 sh 10Ck2 sh 10Ck2 sh 10Ck2 排他制御をおこなってみましよう。 # ! /bin/sh LOCK=/tmp/10ck until ln —s $ $ $LOCK 2>/dev/nu11; exit 0 rm $LOCK echo " 危険領域から出る sleep 3 echo " 危険領域に入る do done さきほどのフログラムと異なる点は、最初の条件部がた プを回るための条件がさきほどとは反転しているために ンクを作成するコマンドになっていること、さらに、ルー んにファイルの存在を調べるのではなくシンポリック・リ 120 プロセスの数を増やしてみましたが、まったく間題あり ません。 ちなみに、ロックのためのシンポリック・リンクか存 在している場合、次のようなコマンドを実行すれは、ロッ クをかけているプロセスのプロセス ID を得ることかでき ます。 % ls ー 1 /tmp/lock ー sed 's/ . * / / , 25920 ディレクトリを作成する去と、リンクを作成する去 を紹介しました。それでは、どちらを用いたらよいでしょ うか。ディレクトリを用いる去では、親ディレクトリの ディレクトリ・エントリを変更するだけでなく、新たに作 成するディレクトリのための領域も作らなけれはなりませ UNIX MAGAZINE 1998.11
プログラミング・テクニック 図 1 割込みの禁止によるお也制胸 割込み割込み禁止 割込み許可 プロセス 1 プロセスの休眠 プロセスの切替え プロセス 2 割込み 時間 プロセスの切替え うなタイミングで作業をおこなったのかによって、最終 結果が正しくなったりおかしくなったりしました。このよ うな事態を競合状態と呼びます。それでは、パスワード・ ファイルの例では、本質的に何が問題の原因であったのか を考えてみましよう。 ます、、、管理者が 2 人いた " ことカげられます。管理 者が 3 人以 - E いても間題が複雑になるだけですから、、、管 理者が 2 人以 - E いた " ことと言い換えてもいいでしよう。 しかし、たんに管理者が複数いるだけでは間題は起きませ ん。パスワード・ファイルを編集する担当者を決めるな ど、いくっかの方法で可壁することができます。本当の問 題は、たんに管理者が複数いたことではなく、その複数の 管理者が同時に共有資源であるパスワード・ファイルをア クセスしたことです。これさえ分かればあとは簡単です。 複数の管理者か共有資源であるパスワード・ファイルに同 時にアクセスしないように排他制御をおこなえばよいので す。つまり、ある管理者がパスワード・ファイルにアクセ スしているときには、はかの管理者がアクセスできないよ うに制御するのです。 この排他制御の対象となる共有資源にアクセスする部分 のことを、オペレーティング・システムの世界では危険 領域と呼んでいます。また、さきほどの例における管理者 はプロセスに対応します。つまり、競合状態を回避するに は、、、複数のプロセスが同時に危険領域内に存在しない " ことか重要になります。そのためには、排他制御を実現す る方法がきわめて重要です。 排他制御の実現 それでは、排他制御を寒見するいくつかの去を調べて みましよう。 UNIX MAGAZINE 1998.11 オペレーティング・システムについて考えます。あるプ ロセスから別のプロセスへと制御が移るのは、割込み ( ク ロック割込みなど ) によってオペレーティング・システム かフ。ロセスを切り替えるためです。そこで、あるプロセス が危ド剱頁域に入るときに割込みを禁止してしまえば、事実 上ほかのプロセスが実行されることはないので排他的に処 理をおこなえます。このプロセスが危ド頁域を離れる場合 には再度割込みを許可することで、オペレーティング・シ ステムカワ。ロセスの切替えをできるようにしなけれはなり ません ( 図 1 ) 。 しかし、この方法にも間題はあります。割込みを禁止し たプロセスか再隻許可するのを忘れると、システムが正し く重川しなくなってしまう可能匪があります。割込みを禁 止するなどという危険な命令を普通のフログラムに許して しまうのは、オペレーティング・システムとしては失格で しよう。 それでは、ロック変数を用いるのはどうでしようか。 れは、プロセスが共有領域にアクセスできると仮定して、 この共有領域に変数を 1 っ準備し、危険領域に入るときに はこの変数に 1 を設定し、危険領域から出るときには変数 に 0 を設定するのです。こうしておけば、変数を見るだけ で危険領域に別のプロセスが入っているかどうかがすぐに 分かるので、複数のプロセスか 1 司じ危ド貞域に入ってしま うことを防げます。 この方法は、一見うまくいきそうに思えます。しかし、 タイミングによっては複数のプロセスが危険領域に入って しまうことがあります。 ます、最初のプロセスがロック変数の値を読み込みま す。この段階では変数の値は 0 です ( 図 2-1 ) 。 変数に 1 を設定する前にフロセスの切替えカ起こり、別の フロセスカ躾行されたとします。この別のプロセスは同様 117
ロプロクラミンク・テクニック 多治見寿和 ファイルのロック 前回は ls コマンドを紹介しました。 ls はもっとも初期 の UNIX から存在しているコマンドで、いまでは 26 も のオプションをもった複雑なものとなっています。その ソースコードを眺めると、ファイルフラグという概念や、 ファイルシステム内のヨ冓造を巡回するための fts 関委羊 などが目を引きます。 今回は、ファイルのロックをとりあげます。ある資源を 複数のものが使う可能性があるとき、そのイ吏用に関して調 整をおこなわなければなりません。そのための財冓がロッ クです。 いうまでもなく、 UNIX はマルチューサー、マルチプ ロセスのオペレーティング・システムです。このような オペレーティング・システムでは、つねに複数のユーサー か 1 司ーの作業をおこなうかもしれないという可能生を考え なければなりません。たとえば、 UNIX ではパスワード・ ファイルはたいへん重要です。このファイルを編集する場 合には、複数のユーザー ( 管理者 ) か 1 司時に変更しておか しくなってしまわないように、専用のプログラムを使うこ とになっています。 ます、 2 人の管理者か 1 司時に修正しようとするとどんな 間題か起こるかを考えてみましよう。最初の状態では、ユ ーサー A 、 B の 2 人がパスワード・ファイルに書かれて いたとします。ここで 1 人目の管理者が、新たなユーサー C を加えるためにパスワード・ファイルを編集し始めまし た。この段階で、別の管理者がもう 1 人の新たなユーサー D を追加しようとパスワード・ファイルの編集を始めた とします。このとき、ファイルシステム上のパスワード・ バスワード・ファイルの編集 116 ファイルにはまだューサー C が追加されていないため、 2 人目の管理者は A 、 B の 2 人のユーザーだけか書かれた 状態から編集を始めます。 そして、最初の管理者カ扁集を終了して、 A 、 B 、 C の 3 人のユーサーか書かれたパスワード・ファイルを保存しま す。この点では、ファイルシステム上にはたしかに A 、 B 、 C か書かれたファイルが存在しています。ところが、 このあとで 2 人目の管理者がユーサー D を追加したファ イルを保存すると、パスワード・ファイルは置き換わって しまい、 A 、 B 、 D の 3 人か書かれたファイルとなりま す。つまり、ユーサー C に関する作業は無効となってし まうのです。 1 人目の管理者がパスワード・ファイルを修正して保存 し、そのあとで 2 人目の管理者がその、、修正された " パス ワード・ファイルに対してイ 1 喋をして保存すれは、このよ うな問題は発生しないのは明らかです。 そこで UNIX では、パスワード・ファイルを編集する ための専用のコマンド vipw が用意されています。 vipw を使えは、はかの管理者がパスワード・ファイルを編集し ているあいだは、新たにパスワード・ファイルの編集がで きないようになっています。さきはどの例では、 2 人目の 管理者はパスワード・ファイルの編集を始められなくなる のです。こうすることで、 2 回の編集竹喋を ( 同時ではな く ) 順番におこなうことになり、かならす変更か有効とな ルという共有データに対して読み書きをおこない、 パスワードの例は、 2 人の管理者かパスワード・ファイ 排他制御と危険領域 ります。 どのよ UNIX MAGAZINE 1998.11