Ubuntuで実行しているg ++でコンパイルされた複数のアプリがあります。名前付きセマフォを使用して、異なるプロセス間を調整しています。
すべてが正常に動作しますexcept次の状況の場合:プロセスの1つがsem_wait()
またはsem_timedwait()
を呼び出してセマフォをデクリメントし、その後クラッシュまたは-9で強制終了した場合sem_post()
を呼び出す機会を得て、その瞬間から、名前付きセマフォは「使用不可」になります。
「使用不可」とは、セマフォのカウントがゼロになり、1に戻す必要があったプロセスが終了または強制終了したことを意味します。
最後にデクリメントしたプロセスがクラッシュしたことを通知するsem_*()
APIが見つかりません。
どこかにAPIがありませんか?
名前付きセマフォを開く方法は次のとおりです。
sem_t *sem = sem_open( "/testing",
O_CREAT | // create the semaphore if it does not already exist
O_CLOEXEC , // close on execute
S_IRWXU | // permissions: user
S_IRWXG | // permissions: group
S_IRWXO , // permissions: other
1 ); // initial value of the semaphore
これが私がそれを減らす方法です:
struct timespec timeout = { 0, 0 };
clock_gettime( CLOCK_REALTIME, &timeout );
timeout.tv_sec += 5;
if ( sem_timedwait( sem, &timeout ) )
{
throw "timeout while waiting for semaphore";
}
セマフォを確実に回復する方法がないことがわかりました。確かに、誰でも名前付きセマフォにpost_sem()
を追加して、カウントをゼロから再び増やすことができます。提供されるAPIは制限が多すぎて、これがいつ発生したかを示すものではありません。
Ipcツールにも注意してください-一般的なツールipcmk
、ipcrm
、およびipcs
は、古いSysVセマフォ専用です。これらは特に、新しいPOSIXセマフォでは機能しません。
しかし、何かをロックするために使用できるものが他にもあるようです。これは、アプリケーションがシグナルハンドラーでキャッチできない方法で停止したときに、オペレーティングシステムが自動的に解放します。 2つの例:特定のポートにバインドされた待機ソケット、または特定のファイルのロック。
ファイルのロックが必要な解決策であると判断しました。したがって、sem_wait()
およびsem_post()
呼び出しの代わりに、次を使用しています:
lockf( fd, F_LOCK, 0 )
そして
lockf( fd, F_ULOCK, 0 )
アプリケーションが何らかの方法で終了すると、ファイルは自動的に閉じられ、ファイルロックも解放されます。 「セマフォ」を待機している他のクライアントアプリは、期待どおりに自由に続行できます。
助けてくれてありがとう、みんな。
@Stéphaneのソリューションのように、セマフォの代わりにロックファイルを使用しますが、flock()呼び出しは使用しません。排他ロックを使用してファイルを開くだけです。
//call to open() will block until it can obtain an exclusive lock on the file.
errno = 0;
int fd = open("/tmp/.lockfile",
O_CREAT | //create the file if it's not present.
O_WRONLY | //only need write access for the internal locking semantics.
O_EXLOCK, //use an exclusive lock when opening the file.
S_IRUSR | S_IWUSR); //permissions on the file, 600 here.
if (fd == -1) {
perror("open() failed");
exit(EXIT_FAILURE);
}
printf("Entered critical section.\n);
//Do "critical" stuff here.
//exit the critical section
errno = 0;
if (close(fd) == -1) {
perror("close() failed");
exit(EXIT_FAILURE);
}
printf("Exited critical section.\n");
これは、セマフォを管理する際の典型的な問題です。一部のプログラムは、セマフォの初期化/削除を管理するために単一のプロセスを使用します。通常、このプロセスはこれだけを実行し、他には何も実行しません。他のアプリケーションは、セマフォが使用可能になるまで待機できます。 SYSVタイプのAPIを使用してこれを実行したが、POSIXを使用していないことを確認しました。 'Duck'と同じように、semop()呼び出しでSEM_UNDOフラグを使用します。
しかしあなたが提供した情報と一緒にセマフォを使用しないことをお勧めします。特に、プロセスが強制終了またはクラッシュする危険がある場合。 OSが自動的にクリーンアップするものを使用してみてください。
lsof
を使用してシェルから見つけることができるはずです。次に、おそらくそれを削除できますか?
更新
ああそう...man -k semaphore
救助へ。
ipcrm
を使用してセマフォを取り除くことができるようです。あなたはこの問題の最初ではないようです。
再確認する必要がありますが、sem_postはシグナルハンドラーから呼び出すことができると思います。プロセスを停止させるいくつかの状況を把握できる場合は、これが役立つ場合があります。
Mutexとは異なり、任意のプロセスまたはスレッド(アクセス権付き)はセマフォにポストできます。それをリセットする簡単なユーティリティを書くことができます。おそらく、システムがいつデッドロックしたか知っているでしょう。それを停止してユーティリティプログラムを実行できます。
また、セマフォンは通常/ dev/shmの下にリストされており、削除できます。
SysVセマフォは、このシナリオに適しています。 SEM_UNDOを指定できます。この場合、システムが停止した場合に、プロセスによって行われたセマフォへの変更がシステムによってバックアウトされます。また、セマフォを変更するための最後のプロセスIDを通知する機能もあります。
プロセスが強制終了された場合、プロセスがなくなったことを確認する直接的な方法はありません。
あなたが持っているすべてのセマフォ全体である種の定期的な整合性チェックを操作することができます- semctl (cmd = GETPID)を使用して、記述した状態で各セマフォにアクセスした最後のプロセスのPIDを見つけ、チェックしますそのプロセスがまだ残っているかどうか。そうでない場合は、クリーンアップを実行します。
名前付きセマフォを使用する場合、lsof
またはfuser
で使用されるようなアルゴリズムを使用できます。
これらを考慮してください:
1.各名前付きPOSIXセマフォは、通常は次のパスの下にあるtmpfsファイルシステムにファイルを作成します。
/dev/shm/
2.各プロセスには、Linuxのパスの下にmap_filesがあります。
/proc/[PID]/map_files/
これらのマップファイルは、プロセスメモリのどの部分が何にマップされているかを示します。
したがって、これらの手順を使用して、名前付きセマフォがまだ別のプロセスによって開かれているかどうかを確認できます。
1オプション)名前付きセマフォの正確なパスを検索します(/dev/shm
の下にない場合)
メモリ内でのポインタのアドレス位置を検索し(通常、整数へのポインタのアドレスのキャストを使用)、それを16進数(つまり、結果:0xffff1234
)に変換してから、次のパスを使用します。
/proc/self/map_files/ffff1234-*
この条件を満たすファイルは1つだけです。
そのファイルのシンボリックリンクターゲットを取得します。これは、名前付きセマフォの絶対パスです。
2-すべてのプロセスを反復して、そのシンボリックリンクタゲットが名前付きセマフォの完全パスと一致するマップファイルを見つけます。存在する場合はセマフォが実際に使用されていますが、存在しない場合は、名前付きセマフォを安全にリンク解除して、再び使用できるように再度開くことができます。
---([〜#〜]更新[〜#〜]
手順2で、フォルダーmap_file
内のすべてのファイルを繰り返すのではなく、すべてのプロセスを繰り返す場合は、ファイル/proc/[PID]/maps
を使用して、名前付きセマフォファイルの完全パスを検索することをお勧めします(つまり: /dev/shm/sem_xyz
)その中に。このアプローチでは、他のいくつかのプログラムが名前付きセマフォのリンクを解除したが、セマフォが他のプロセスでまだ使用している場合でも、それを見つけることができますが、ファイルパスの末尾に「(削除済み)」のフラグが追加されます。
sem_unlink()
の直後にsem_open()
を実行するだけです。 Linuxは、内部クローズを含むすべてのプロセスがリソースをクローズした後に削除します。