短いバージョンは、シグナルが現在のシステムコールを中断することです。あなたはやっていますがfgets()
、おそらくread()
システムコールでブロックされています。呼び出しが中断され、read()
-1 が返され、 に設定errno
されEINTR
ます。
これによりfgets
NULL が返され、ループが終了し、プログラムが終了します。
いくつかの背景
Linux の glibc は、 に対して 2 つの異なる概念を実装していsignal()
ます。システムコールがシグナル間で自動的に再起動されるものとそうでないもの。
システムコールでシグナルが発生し、処理がブロックされた場合、システムコールは中断(「キャンセル」)されます。ユーザー空間アプリケーションで実行が再開され、シグナル ハンドラが発生します。中断されたシステム コールはエラーを返し、errno を に設定しますEINTR
。
次に何が起こるかは、システムコールが再起動されるかどうかによって異なります。
システム コールが再起動可能な場合、ランタイム (glibc) は単にシステム コールを再試行します。read()
システム コールの場合、これは次のように実装されるのと似ていread()
ます。
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
while ((sz = syscall_read(fd, buf, len)) == -1
&& errno == EINTR);
return sz;
}
システム コールが自動的に再開されない場合は、次のread()
ように動作します。
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
sz = syscall_read(fd, buf, len));
return sz;
}
後者の場合、シグナルによって中断されたために read() が失敗したかどうかを確認するのはアプリケーション次第です。そして、信号が処理されたために一時的に失敗したかどうかを判断するのはあなた次第です。呼び出しread()
を再試行するのはあなた次第ですread()
シグナルとシグアクション
sigaction()
の代わりにを使用することでsignal()
、システム コールを再開するかどうかを制御できます。指定する関連フラグsigaction()
は
SA_RESTART 特定のシステム コールをシグナル間で再開可能にすることにより、BSD シグナル セマンティクスと互換性のある動作を提供します。このフラグは、シグナルハンドラを確立する場合にのみ意味があります。システムコールの再起動については、signal(7) を参照してください。
BSD vs SVR4 セマンティクス
を使用する場合はsignal()
、必要なセマンティクスによって異なります。の説明にあるSA_RESTART
ように、BSD シグナル セマンティクスの場合は、システム コールが再開されます。これは glibc のデフォルトの動作です。
もう 1 つの違いは、BSD セマンティクスでは、シグナルsignal()
が処理された後、インストールされたシグナル ハンドラーがインストールされたままになることです。SVR4 セマンティクスはシグナル ハンドラーをアンインストールします。さらに多くのシグナルをキャッチするには、シグナル ハンドラーでハンドラーを再インストールする必要があります。
apue.h
ただし、「apue.h」は、インクルードする_XOPEN_SOURCE 600
前にマクロを定義し<signal.h>
ます。これにより、signal() は、システム コールが再起動されないSVR4 セマンティクスを持つようになります。これにより、 fgets() 呼び出しが「失敗」します。
signal() を使わず、sigaction() を使う
これらすべての動作の違いにより、シグナルの代わりに sigaction() を使用してください。#define
sigaction() を使用すると、(おそらく)非表示に基づいてセマンティクスを変更する代わりに、何が起こるかを制御できますsignal()