セマフォの排他制御で悩んでいます。セマフォについて書かれたドキュ
メントは幾つか読みましたが、semop()にてIPC_NOWAITを使ったサンプ
ルは見当たらなくて難儀しています。リアルタイム処理のまざった排他
制御の良い例などありましたら、ご紹介・ご指摘等頂ければ幸いです。
Linux kernel 2.2.13(Turbo Linux 4.5FTP版) 上でA,B2つのプロセス
があり、プロセス間でメモリを共有しています。これらは機能は違えど
ある意味サーバとして動作しており、1台のPC上で同時に動作していま
す。各々の機能は次のとおりです。
*自己フォローで、A,Bの簡単なサンプルコードを付けておきます。
(簡略化のため、サンプルコードでは、メモリの共有等の処理は行って
いません)
A.セマフォのロックを試みて、ロックが成功すれば共有メモリにデータ
を出力するが、ロックに成功しなければ速やかにループを継続する。
B.セマフォのロックを試みる。ロックが可能になるまで処理をブロック
し、共有メモリからデータを読み出したら、アンロックする。
●問題:セマフォを用い、AにてIPC_NOWAITを指定したが、期待した動
作をしない。
Aでsemop()にIPC_NOWAITを指定すると確かにブロックしないが、B
のsemop()によるロック?アンロック動作が一度でも行われると、
Aのsemop()はperror()にて"Resource temporarily unavailable"
となる。(以後Aのsemop()は、Bからのロックが為されなくとも失
敗し続ける)一方Bでは、ロック?アンロックは成功し続ける。
一度この状態になると、AあるいはBにおいてsemctlを使用し、セマ
フォを変更しようとしても、Bのsemop()は依然として"Resource
temporarily unavailable"のままである。
アセンブラを使って排他制御をすれば、セマフォなんかどうでもよくな
るのですが、B側ではビットが立つ(あるいは下ろす)の監視ループが
必要なので、僅かといえ無駄なCPUタイムを食ってしまいます。(理論的
には最大33ms)監視ループ中でusleep()あるいはnanosleep()など等価
なシステムコールを呼ぶか、いっそのこと監視ループなど用意せずとも
HLTしてしまえば効率がよいのか?とは思います。ただしアセンブラでの
テストにはまだ手をつけていません。
以上
a,b各々は/tmp/abcdeという適当なファイルを作ってから実行して下さい。
次にX上でターミナルを2つ開いて、各々のシェルからa(が先)とbを起動し
ます。
なお、文末に私の環境での動作例を付けました。
---------- >8 ---------- a.c ---------- >8 ----------
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
main()
{
key_t mykey;
int semid;
struct sembuf trylock = {0, -1, SEM_UNDO | IPC_NOWAIT};
struct sembuf unlock = {0, 1, SEM_UNDO};
union semun myus;
mykey = ftok("/tmp/abcde", 'k');
semid = semget(mykey, 1, IPC_CREAT | 0666);
myus.val = 1;
semctl(semid, 0, SETVAL, myus); /* 初期値 1 即ちアンロック状態 */
while(1){
sleep(1); /* ウエイト。削ってはいけません */
if(semop(semid, &trylock, 1) > -1){
perror(" try-lock");
sleep(3); /* 本当はここで共有メモリにデータを出力 */
/* ここではsemvalはsemop()の影響を受けていないように見える。
semctl(semid, 0, GETVAL, myus);
printf("semval: %d\n", myus.val); */
semop(semid, &unlock, 1);
perror(" unlock");
} else {
perror("can't lock");
}
}
}
---------- >8 ---------- b.c ---------- >8 ----------
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
main()
{
key_t mykey;
int semid, i;
struct sembuf mysem1 = {0, -1, SEM_UNDO};
struct sembuf mysem2 = {0, 1, SEM_UNDO};
union semun mysu;
mykey = ftok("/tmp/abcde", 'k');
semid = semget(mykey, 1, IPC_CREAT | 0666);
/* ここを一度でも通過したら、a は Resource...になる */
for(i = 0; i < 2; i++){
sleep(1);
semop(semid, &mysem1, 1);
perror(" lock");
sleep(2); /* 本当は共有メモリからデータを入力 */
semop(semid, &mysem2, 1);
perror("unlock");
}
/* mysu.val = 1;
semctl(semid, 0, SETVAL, mysu);
perror("finished");*/ /* 効果なし */
}
---------- >8 ---------- b.c ---------- >8 ----------
PC環境:
K6-2 333MHz, TurboLinux 4.5 (Kernel 2.2.13)
カーネルはモジュール組み込みのため再構築済み。
libc等ライブラリの再コンパイル、インストールはせず。
なお、ipcs、ipcrmは正常に動作している模様。
aの動作例
try-lock: Success
解説:semop()によるロックが成功しているが、何故か
semvalは1のまま変化していない
unlock: Success
try-lock: Success
unlock: Success
解説:この例ではここのあたりでbを実行
can't lock: Resource temporarily unavailable
解説:semop()の戻り値は失敗(-1)だがerrnoは11(10進)
try-lock: Resource temporarily unavailable
解説:semop()の戻り値は成功(0)だがerrnoは11(10進)
semvalは1のまま変化していない
unlock: Resource temporarily unavailable
(semop()の戻り値は成功(0))
can't lock: Resource temporarily unavailable
try-lock: Resource temporarily unavailable
unlock: Resource temporarily unavailable
...以後 Resource...の繰り返し
bの動作例
lock: Success
unlock: Success
lock: Success
unlock: Success
ちなみにbにてロックにIPC_NOWAITを指定すると、立場が逆転します。
semaphore を使ったことがないので、これについては解りませんが、
> try-lock: Resource temporarily unavailable
> 解説:semop()の戻り値は成功(0)だがerrnoは11(10進)
> semvalは1のまま変化していない
> unlock: Resource temporarily unavailable
> (semop()の戻り値は成功(0))
> try-lock: Resource temporarily unavailable
> unlock: Resource temporarily unavailable
この辺は、semop が成功してるので errno に意味はありません。
glibc の info から引用します。
| The initial value of `errno' at program startup is zero. Many
| library functions are guaranteed to set it to certain nonzero
| values when they encounter certain kinds of errors. These error
| conditions are listed for each function. These functions do not
| change `errno' when they succeed; thus, the value of `errno' after
| a successful call is not necessarily zero, and you should not use
| `errno' to determine *whether* a call failed. The proper way to
| do that is documented for each function. *If* the call the
| failed, you can examine `errno'.
では。
--
原野 裕樹
mailto:ma...@sa.uno.ne.jp
http://www.nn.iij4u.or.jp/~masm/
In article <38E896F1...@d4.dion.ne.jp> ,
歩野零一<nospam....@d4.dion.ne.jp> writes
> Aでsemop()にIPC_NOWAITを指定すると確かにブロックしないが、B
> のsemop()によるロック?アンロック動作が一度でも行われると、
> Aのsemop()はperror()にて"Resource temporarily unavailable"
> となる。
NOWAIT はブロックしないで、error を返すという指示なので、それで
正しい動作だと思います。自分で errno を直してやれば良いのかな。
>アセンブラを使って排他制御をすれば、セマフォなんかどうでもよくな
>るのですが、B側ではビットが立つ(あるいは下ろす)の監視ループが
>必要なので、僅かといえ無駄なCPUタイムを食ってしまいます。
semop したら、そんなものではすまないと思います。でも、33ms
は長いな。今だったら、この手のプログラムは thread を使うんじ
ゃないかな。何をしたいのかにもよりますが....
でも、thread がない場合もあるしな。
---
Shinji KONO @ Information Engineering, University of the Ryukyus
河野真治 @ 琉球大学工学部情報工学科
"歩野零一" <nospam....@d4.dion.ne.jp> wrote in message
news:38E896F1...@d4.dion.ne.jp
> Linux kernel 2.2.13(Turbo Linux 4.5FTP版) 上でA,B2つのプロセス
> があり、プロセス間でメモリを共有しています。
ってことですから、Thread はありますね。pthread なら Linux にも FreeBSD
にも Solaris にもありますから、かなりの potability があるといえるん
じゃないかな?
--
内田 俊明 (UCHIDA,Toshiaki)
開発中のソフトを云々するのは気が引けますが、説明します。
#まぁ今は商用ではありませんけど。(^^;
ずばりAは何をやっているかというと、リアルタイムでビデオ
のキャプチャ~JPEG圧縮をしています。
このJPEG画像を共有メモリとセマフォを用いてB(独自のHTTP
サーバ)に渡しているわけです。Bはクライアントからの要求が
あると共有メモリを排他制御し、ここからJPEGデータを読み出
したら、Webクライアントに送信します。その後Bは共有メモリ
を解放します。
一方Aは共有メモリがロックできれば、そこへJPEGデータを書
き込みますが、ロックできなければそれはBがデータを読み出
している最中だと分るので、処理をブロックすることなくビデ
オ取り込みを続けます。
よって、セマフォによってきちんとメモリが排他制御されてい
るかどうかはwebブラウザを通して画像を見れば一目瞭然です。
しかし、タイミングによってはJPEGファイルの先頭ブロックに
誤ったデータが現れますので、セマフォによる排他制御が不完
全である可能性を考えました。(断定は出来ませんが)
Bのほうはファイルの連続送信程度の負荷(数10KBのファイル
を秒あたり3~4つ程度)では落ちないことが分っていますの
で、とりあえずBのメモリ管理~送信アルゴリズムについて、
今は追求しないことにしました。
もちろん問題を簡単にする為に、Webクライアントは1つ
(Netscape 4.7)、Bで起動される処理スレッド(pthread)も1つ
にしています。
なおA,Bを一つに纏めて、pthreadを使う制御は既に試みていま
す。(最初の設計はそうだったのです)
結果は色(RGBいずれかのフレーム丸ごとと思われる)が抜け落
ちるなど悲惨な状況でした。どうもビデオキャプチャ時に割り
込みが起きているかのような状況でしたので、ビデオ取込~圧
縮にpthreadを使うのは止めました。
やり方にもよるのでしょうが、問題の根が深そうだったので、
事を簡潔にするべく処理を別々のプログラムに分けたのです。
ブラウザからアクセス頻度が1秒あたり1回程度ならば問題なく
動くのですが、高負荷時に問題が露見します。
まずAを実行して数秒待ち、その後、 Bを実行するものとします。
1.BによってロックされるまではAのsemop()[IPC_NOWAIT側]の戻り値は -1
以上(要は成功)を返しているのです。これはBによってロックされる
まで常に成功しつづけます。Aの出力は「try-lock...」「unlock...」
の2つです。
2.Bによってロックされると、Aのsemop()[IPC_NOWAIT側]は失敗します。
ここまでは、一見「正しい動作をしている」?ように見えます。
このあたりから、Aの出力は「can't lock」「try-lock」「unlock」の
3つになります。
3.ところがBによるロックが終わった後でも、Aのsemop()[IPC_NOWAIT側]
は成功と失敗を**勝手に繰り返します**
(semop()[IPC_NOWAIT側]を呼ぶ度、戻り値が不定になる!)
Bの終了後、Aの出力は「try-lock」「unlock」の2つの繰り返しに戻
るのではなく、「can't lock」「try-lock」「unlock」の3つを繰り
返しているので分りますね。
(再現性がないかもしれませんが)実際にサンプルを動かしてみると、変
だと思われるのではないでしょうか。
さて、
こうしている間にセマフォの代替関数をアセンブラで書いてしまいました
ので、とりあえずLinuxのセマフォの使用は中止しました。(テストはまだ
不十分です)でもなぜsemop()の戻り値が一定にならないのか謎ですね。
#もしかすると謎じゃないかもしれませんけど。
<38EAB2A1...@d4.dion.ne.jp>の記事において
nospam....@d4.dion.ne.jpさんは書きました。
>> まずAを実行して数秒待ち、その後、 Bを実行するものとします。
>>
>> 1.BによってロックされるまではAのsemop()[IPC_NOWAIT側]の戻り値は -1
>> 以上(要は成功)を返しているのです。これはBによってロックされる
>> まで常に成功しつづけます。Aの出力は「try-lock...」「unlock...」
>> の2つです。
>>
>> 2.Bによってロックされると、Aのsemop()[IPC_NOWAIT側]は失敗します。
>> ここまでは、一見「正しい動作をしている」?ように見えます。
>> このあたりから、Aの出力は「can't lock」「try-lock」「unlock」の
>> 3つになります。
>>
>> 3.ところがBによるロックが終わった後でも、Aのsemop()[IPC_NOWAIT側]
>> は成功と失敗を**勝手に繰り返します**
>> (semop()[IPC_NOWAIT側]を呼ぶ度、戻り値が不定になる!)
私のとこれでは、redhat-6.1 と redhat-6.2 および Kondara
のシステムがあるのですが、これらのシステムでは、歩野さんの
示されたサンプルプログラムがうまくコンパイルできませんでし
た。どうもヘッダファイルがおかしい感じなのです。そこでいろ
いろ探して、
#include <sys/ipc.h>
#include <sys/sem.h>
の部分を
#include <linux/ipc.h>
#include <linux/sem.h>
に書き換えたところコンパイルできました。この状況で動作させ
てみましたが、特におかしな動作はないようです。ようするに、
歩野さんが当初期待した通りの動作をするようです。
Turbo で変更なしでコンパイルできるというところが気になり
ます。ひょっとすると、コンパイル出来るだけの壊れたヘッダファ
イル残っているのではないでしょうか? 一度上記の変更を行って
コンパイルして動作を確認された方が良いかもしれません。
# なぜ、linux でこんな変な状況になっているのかは存じません。
--
Email: oka...@cs.ehime-u.ac.jp
ははぁ、そうでしたか。man semop には<sys/*.h>とありましたので、その
ようにしていました。
さて、<linux/*.h>の定義は、よく<sys/types.h>などとぶつかって煩わしい
ので、あまり使ったことはありません。
ちなみにvideo for linuxを同時に使っているのですが、他の定義とぶつかっ
てばかりなので自分で定義しなおして使っています。
私はその部分は変更せず、a.c, b.c の両方に
union semun {
int val;
};
を追加したところ、コンパイルできました。
で、以下の2つの環境で試してみましたが、これでも特におかしな
動作はありませんでした。
ix86, glibc 2.1.3, kernel 2.3.99-pre3, gcc 2.95.2
sparc, glibc 2.1.1, kernel 2.2.5, egcs 2.91.66
> # なぜ、linux でこんな変な状況になっているのかは存じません。
bits/sem.h に「以前はここに union semun があったけど、ユーザ
が定義すべきものであって、ここにあるのは間違いだ」みたいなこ
とが書いてありますね。なぜそうなのかは解りませんが…
Yuuki Harano <ma...@sa.uno.ne.jp> writes:
>
> 私はその部分は変更せず、a.c, b.c の両方に
> union semun {
> int val;
> };
> を追加したところ、コンパイルできました。
>
> (断章)
>
> > # なぜ、linux でこんな変な状況になっているのかは存じません。
>
> bits/sem.h に「以前はここに union semun があったけど、ユーザ
> が定義すべきものであって、ここにあるのは間違いだ」みたいなこ
> とが書いてありますね。なぜそうなのかは解りませんが…
>
union semun はプログラムの側で定義すべき物です。これは
UNIX 98 の規格です。これまでの Linux と FreeBSD の仕様
は間違っている旨の記述が、Stevens の UNP vol.2 p.288
にあります。
``Unfortunately some systems (FreeBSD and Linux) define this
union as a result of including the <sys/sem.h> header, making
it to hard to write portable code. Even though having the
system header declare this union makes sense, UNIX 98 states
that it must be explicitely declared by the application. ''
更に Linux の semctl のプロトタイプも「間違って」いて
int semctl(int semid, int semnum, int cmd, union semun);
ではなく
int semctl(int semid, int semnum, int cmd, ...);
が正しいです。
--
Takeyasu Wakabayashi
Faculty of Economics, Toyama University
twa...@eco.toyama-u.ac.jp
うーむ。やっぱりよく解らないです。
といっても、semctl が何をするものなのかさえ解ってないので、
話にならないですね。^^;
精進します。
私もさほど深くは調べていませんが…。
さて、今のsemctl()のGETVALは現在のsemval値ではなく、semctl()にて
SETVALを使って設定した値を返すという、あまりありがたくない実装の
ようです。
つまり、今semop()で設定したセマフォの値(semval)が幾つになっている
か不明なのですね。
これは使えない、使えなさ過ぎる、ということで、fj.sourcesにアセンブラを
使ったバイナリセマフォのサンプルをポストしておきました。
興味のある方、あるいは怖いもの知らずの方はご自由にお使いください。
Yuuki Harano wrote:
> といっても、semctl が何をするものなのかさえ解ってないので、
> 話にならないですね。^^;
> 精進します。
いえ、私は理解するのを早々に諦めました。
<m2em8ja...@kongzi.eco.toyama-u.ac.jp>の記事において
twa...@eco.toyama-u.ac.jpさんは書きました。
>> union semun はプログラムの側で定義すべき物です。これは
>> UNIX 98 の規格です。これまでの Linux と FreeBSD の仕様
>> は間違っている旨の記述が、Stevens の UNP vol.2 p.288
>> にあります。
あら、そうだったのですね。勝手に変な状況など言って
しまって軽率でした。
# Turbo はどうなってるんだろう(^^;
> # Turbo はどうなってるんだろう(^^;
TurboLinux4.5(FTP版)ではunion semunはプログラム側で用意しなければ
だめです。
でも、もうどっちでもいいですね。自作のセマフォはとりあえず順調に動
いてますから。
歩野零一 <nospam....@d4.dion.ne.jp> writes:
> Yuuki Harano wrote:
>
> > といっても、semctl が何をするものなのかさえ解ってないので、
> > 話にならないですね。^^;
> > 精進します。
>
> いえ、私は理解するのを早々に諦めました。
>
System V の IPC で「使えそう」なのは共有メモリぐらいのような
気がします。セマフォとメッセージキューはあまりにも複雑すぎて
著しく使いづらいです。POSIX セマフォはかなりインタフェースが
改善されていますが、残念なことに Linux ではスレッド間でしか使
えません。フリーな OS で、まともな POSIX IPC(IEEE Std. 1003.1b)
が実装されているものを知りません(Solaris 8 をフリーと言うなら
ば別ですが)。
いえ、私の記事から化けてますね。
どうもすみませんです _o_
# 何やったんだろう..