1

ご覧のとおり、これは APUE でのサンプルです。

#include "apue.h"

static void sig_int(int sig);

int main(int argc, char **argv)
{
    char buf[MAXLINE];
    pid_t pid;
    int status;

    if (signal(SIGINT, sig_int) == SIG_ERR) //sig_int is a simple handler function
        err_sys("signal error");

    printf("%% ");
    while (fgets(buf, MAXLINE, stdin) != NULL) {
    //This is a loop to implement a simple shell
    }
    return 0;
}

これはシグナルハンドラです

void sig_int(int sig)   
/*When I enter Ctrl+C, It'll say a got SIGSTOP, but it would terminate.*/
{
    if (sig == SIGINT)
        printf("got SIGSTOP\n");
}

Ctrl+C を入力すると、SIGSTOP が発生したと表示されますが、すぐに終了します。

4

1 に答える 1

6

短いバージョンは、シグナルが現在のシステムコールを中断することです。あなたはやっていますがfgets()、おそらくread()システムコールでブロックされています。呼び出しが中断され、read()-1 が返され、 に設定errnoされEINTRます。

これによりfgetsNULL が返され、ループが終了し、プログラムが終了します。

いくつかの背景

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() を使用してください。#definesigaction() を使用すると、(おそらく)非表示に基づいてセマンティクスを変更する代わりに、何が起こるかを制御できますsignal()

于 2013-11-09T13:49:43.660 に答える