6

まず、ミューテックスは通常、非同期セーフとは見なされないことを認識しています。sigprocmaskこの質問は、非同期シグナルとシグナルハンドラーを備えたマルチスレッドプログラムでミューテックスを安全にするための使用に関するものです。

概念的に次のようなコードがあります。

struct { int a, b; } gvars;

void sigfoo_handler(int signo, siginfo_t *info, void *context) {
    if(gvars.a == 42 || gvars.b == 13) {
        /* run a chained signal handler */
    }
}

/* called from normal code */
void update_gvars(int a, int b) {
    gvars.a = a;
    gvars.b = b;
}

gvarsは大きすぎて単一に収まらないグローバル変数ですsig_atomic_t。通常のコードで更新され、シグナルハンドラから読み取られます。制御されるコードは連鎖信号ハンドラーであるため、信号ハンドラーコンテキストで実行する必要があります(infoまたはを使用する場合がありますcontext)。したがって、すべてのアクセスは、gvarsある種の同期メカニズムを介して制御する必要があります。複雑なことに、プログラムはマルチスレッドであり、どのスレッドも。を受け取る可能性がありますSIGFOO

質問:(sigprocmaskまたはpthread_sigmask)とを組み合わせることで、次のようなコードを使用して同期を保証するpthread_mutex_tことはできますか?

struct { int a, b; } gvars;
pthread_mutex_t gvars_mutex;

void sigfoo_handler(int signo, siginfo_t *info, void *context) {
    /* Assume SIGFOO's handler does not have NODEFER set, i.e. it is automatically blocked upon entry */
    pthread_mutex_lock(&gvars_mutex);
    int cond = gvars.a == 42 || gvars.b == 13;
    pthread_mutex_unlock(&gvars_mutex);

    if(cond) {
        /* run a chained signal handler */
    }
}

/* called from normal code */
void update_gvars(int a, int b) {
    sigset_t set, oset;
    sigemptyset(&set);
    sigaddset(&set, SIGFOO);
    pthread_sigmask(SIG_BLOCK, &set, &oset);
    pthread_mutex_lock(&gvars_mutex);
    gvars.a = a;
    gvars.b = b;
    pthread_mutex_unlock(&gvars_mutex);
    pthread_sigmask(SIG_SETMASK, &oset, NULL);
}

ロジックは次のようになります。内sigfoo_handlerでは、SIGFOOがブロックされるため、を中断することはできませんpthread_mutex_lock。内update_gvarsでは、保護されたクリティカル領域SIGFOO中に現在のスレッドで発生させることはできないため、どちらも中断することはできません。他のシグナルがないと仮定すると(そして問題となる可能性のある他のシグナルを常にブロックできる)、ロック/ロック解除は常に現在のスレッドで通常の中断できない方法で進行する必要があり、ロック/ロック解除を使用すると他のスレッドが干渉しないでください。私は正しいですか、それともこのアプローチを避けるべきですか?pthread_sigmaskpthread_mutex_lock

4

2 に答える 2

1

あなたは明らかに、sig_atomic_tについて言及することで、未定義の行動領域に入っていることを知っています。そうは言っても、この正確な例が最新のUNIXライクなシステムで機能しないことを確認できる唯一の方法は、信号がSA_NODEFERで設定されているかどうかです。

ミューテックスは、異なるスレッド(別のスレッドで実行されているシグナルハンドラーを含む)間の適切な同期を保証するのに十分であり、sigmaskは、このスレッドのシグナルハンドラーがミューテックスを繰り返すのを防ぎます。

そうは言っても、あなたはシグナルハンドラーの内部にロックがかかった深海にいます。1つのシグナルハンドラーで十分安全かもしれませんが、2つのシグナルハンドラーが異なるロックで同じトリックを実行している場合、ロック順序付けのデッドロックが発生します。これは、スレッドsigmasksの代わりにプロセスsigmasksを適用することでいくらか軽減できます。たとえば、シグナルハンドラの単純なデバッグfprintfは、ロックの順序に間違いなく違反します。

シグナルハンドラーのこのようなものは、複雑になりすぎて壊れやすくなっている兆候であるため、アプリケーションを元に戻して再設計します。1つのsig_atomic_tに触れるシグナルハンドラーは、他のものを正しく取得することの爆発的な複雑さのために、C標準で定義されている唯一のものです。

于 2013-01-25T12:44:14.463 に答える