8

在下面的程序中,如果我取消注释_XOPEN_SOURCE行,我的程序会在我点击 时C-c终止,如果我不注释该行,相同的程序不会终止。任何人都知道以什么方式_XOPEN_SOURCE影响信号处理?我在 linux 上使用 gcc (4.6.3) 和 glibc (2.15)。

/* #define _XOPEN_SOURCE 700 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

typedef void (*sighandler_t)(int);

void handle_signal(int signo)
{
    printf("\n[MY_SHELL] ");
    fflush(stdout);
}

int main()
{
    int c;
    signal(SIGINT, SIG_IGN);
    signal(SIGINT, handle_signal);
    printf("[MY_SHELL] ");
    while ((c = getchar()) != EOF) {
        if (c == '\n')
            printf("[MY_SHELL] ");
    }
    printf("\n");
    return 0;
}
4

2 回答 2

4

问题是该signal()函数在安装信号处理函数时可能有两种不同形式的行为:

  • System V 语义,其中信号处理程序是“一次性的”——即在调用信号处理函数后,信号的处置被重置为SIG_DFL——并且被信号中断的系统调用不会重新启动;或者
  • BSD 语义,其中信号处理程序在信号触发时不会重置,信号在信号处理程序执行时被阻塞,并且大多数中断的系统调用会自动重新启动。

在带有 glibc 的 Linux 上,如果已定义,您将获得 BSD 语义,如果_BSD_SOURCE未定义,您将获得 System V 语义。_BSD_SOURCE宏是默认定义的,但如果您定义(或其他一些宏,如 和 ),则会抑制此_XOPEN_SOURCE默认_POSIX_SOURCE定义_SVID_SOURCE

在 System V 语义下,如果read()底层的系统调用getchar()被中断,那么SIGINTgetchar()返回set to (这将导致您的程序正常退出)。此外,在第一次之后,该信号的处置被重置为默认值,并且默认操作是终止进程(因此即使您的程序在第一次中幸存下来,第二次也会导致它异常退出)。EOFerrnoEINTRSIGINTSIGINTSIGINT

解决方案是根本不signal()用于安装信号处理功能;相反,您应该使用sigaction()可移植的 —— 它在任何地方都提供相同的语义。sa_flags设置为,它将提供 BSD 语义,这正是您想要的SA_RESTARTsigaction()

于 2012-05-23T07:38:15.420 回答
1

这些细微的行为差异是为什么sigprocmask()通常首选signal()具有_XOPEN_SOURCE 700定义调用的版本(如 strace 所示)

rt_sigaction(SIGINT, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {0x80484dc, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, 8) = 0

而被注释掉的电话:

rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART}, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x80484dc, [INT], SA_RESTART}, {SIG_IGN, [INT], SA_RESTART}, 8) = 0

_XOPEN_SOURCE缺少的重要的额外标志是SA_RESTART允许read系统调用继续进行,就好像没有信号发生一样。没有它,系统调用将指示失败,getchar()返回-1(但指示失败,而不是真正的 EOF),并且您的程序终止。

于 2012-05-23T07:47:44.087 回答