4

メインスレッドのerrno処理に影響を与えないように、errno変数を格納する次のシグナルハンドラーコードに出くわしました。

void myhandler(int signo)
{
    int esaved;
    esaved = errno;
    write(STDOUT_FILENO, "Got a signal\n", 13);
    errno = esaved;
}

しかし、これは本当に目的を果たしますか? write() の直後で errno を復元する前に、別のスレッドが共有 errno 変数をチェックするとどうなりますか? そのスレッドは、競合状態のために間違った errno 値を取得しますか?

または、シグナルハンドラーがスレッド/プロセスに関してアトミックに実行されるため、シグナルハンドラーが実行されると、カーネルはシグナルハンドラーが終了するまでスレッドをスケジュールしませんか?

言い換えれば、-いったん開始されると、シグナルハンドラは以下によって中断されることなく実行されます:

 - 1) Scheduler (process/threads),  or  
 - 2) Other signals,  or
 - 3) Hardware interrupt handlers  ?
4

3 に答える 3

4

Linux では、errnoスレッドごとに異なる変更可能な左辺値を返す関数呼び出しに展開されるマクロです。

マニュアルページをチェックしてください:

errno は、ISO C 標準によって、int 型の変更可能な左辺値であると定義されており、明示的に宣言してはなりません。errno はマクロの場合があります。errno はスレッドローカルです。あるスレッドで設定しても、他のスレッドの値には影響しません。

ただし、実際には、シグナル ハンドラーの実行中にシグナルが再度発生する可能性があります (環境によっては、signal()の代わりにを使用した場合。これらの矛盾により、代わりに が推奨される理由です)、または別のシグナルがハンドラーの実行を中断する可能性があります。これが、通常、シグナル処理を設定するときに、シグナル ハンドラーの実行中に、シグナル マスクにシグナルを追加することによって、シグナルをブロックするようにする理由です。 . これにより、シグナル ハンドラーがそれ自体を中断するのを防ぎます (場合によっては、無限ループを防ぎます)。sigaction()sigaction()

参考文献:

  • sa_maskに渡すことができるコンポーネントsigaction():

    sa_mask は、シグナル ハンドラの実行中にブロックする (つまり、シグナル ハンドラが呼び出されるスレッドのシグナル マスクに追加する) 必要があるシグナルのマスクを指定します。さらに、SA_NODEFER フラグが使用されていない限り、ハンドラーをトリガーしたシグナルはブロックされます。

  • signal():

    signal() の移植可能な唯一の使用法は、シグナルの性質を SIG_DFL または SIG_IGN に設定することです。signal() を使用してシグナルハンドラを確立するときのセマンティクスは、システムによって異なります (POSIX.1 では、このバリエーションが明示的に許可されています)。この目的には使用しないでください。

    POSIX.1 は sigaction(2) を指定することで移植性の混乱を解決しました。これにより、シグナルハンドラーが呼び出されたときにセマンティクスの明示的な制御が提供されます。signal() の代わりにそのインターフェイスを使用します。

    [...] さらに、同じシグナルを迅速に配信すると、ハンドラーが再帰的に呼び出される可能性があります。

于 2013-03-27T04:43:10.277 に答える
4

実際、シグナルハンドラは別のシグナルによって中断される可能性があります (最初にハンドラを呼び出したシグナルと同じシグナルではない場合)。

ハンドラーは、別の種類のシグナルの配信によって中断される可能性があります。これを回避するには、sigaction に渡されるアクション構造体の sa_mask メンバーを使用して、シグナル ハンドラの実行中にブロックするシグナルを明示的に指定します。これらのシグナルは、ハンドラーが呼び出されたシグナル、およびプロセスによって通常ブロックされるその他のシグナルに追加されます。ハンドラーのブロッキングを参照してください。

ハンドラーが戻ると、ブロックされたシグナルのセットは、ハンドラーが実行される前の値に復元されます。そのため、ハンドラー内で sigprocmask を使用すると、ハンドラー自体の実行中に到着できるシグナルにのみ影響し、ハンドラーが戻った後に到着できるシグナルは影響を受けません。

http://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html#Signals-in-Handler

于 2013-03-27T04:44:16.790 に答える
4

変数errnoはスレッド固有です。より正確には、スレッド環境では、スレッドローカルまたはスレッドごとの値です。したがってerrno、このスレッドで行われたことは、他のスレッドには影響しませんerrno

コードの保存と復元の目的は、のシステム コールerrnoによって設定されたエラーを隠すことです。しかし、 が失敗した場合、新しい値に設定される可能性があります — ゼロにはなりませんが、それはあなたが言うことができるすべてです — しかし、あなたが求めているコードは、 への呼び出しの前からの呼び出しの後に値を元に戻すので、書き込みが発生したという事実は、このスレッドに影響を与えないという意味で「見えない」ということです。write()myhandler()write()errnowrite()write()errno

シグナル ハンドラ関数自体が、応答しているシグナルのシグナル マスクによってブロックされていないシグナルによって中断される場合があります。また、再スケジュールされる可能性があります。ハードウェア割り込みも発生する可能性がありますが、コードはこれらの影響に気付くのに苦労します。


/usr/include/bits/errno.hLinux では、マクロの定義を見つけることができます (ここに示されているよりも多くのコードでerrnoラップされています)。#ifdef

extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif
于 2013-03-27T04:38:55.643 に答える