4

我有一个使用 sigaction 设置的信号处理程序,如下所示:

struct sigaction act, oldact;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGALRM);
sigaddset(&act.sa_mask, SIGINT);
sigaddset(&act.sa_mask, SIGTERM);
sigaddset(&act.sa_mask, SIGTSTP);
sigaction(SIGALRM, &act, &oldact);
sigaction(SIGINT, &act, &oldact);
sigaction(SIGTERM, &act, &oldact);
sigaction(SIGTSTP, &act, &oldact);
act.sa_flags = 0;

在此之后,我运行一个 for 循环来收集输入并将其打印出来(基本上就像猫一样)。

char *linebuf = NULL;
size_t n = 0;
int len;
while(1){
    len = getline(&linebuf, &n, stdin);
    if(len>0){
        printf("%s", linebuf);
    }
}

但是,一旦我从处理信号返回,getline()就不再阻塞输入,而是-1在设置中断系统调用errno时始终返回。EINTR我显然打算中断getline(),但是如何重置它以便我可以继续阅读输入?

有一些类似的问题并没有真正解决我的问题,但它可能会帮助您更好地理解问题。1 2

另一个有趣的花絮是,当我用于信号处理时,我没有遇到这个问题signal(),但我改成是sigaction()因为它符合 POSIX 标准。

4

2 回答 2

6

一个很晚的答案,但我会分享一些我最近在处理这个问题时学到的东西。当您在 sigaction 结构中将 sa_flags 设置为 0 时,您清除了在收到信号时读取的默认行为。当您使用旧的 signal() 方法时,会保留此默认行为。要在您的情况下恢复这一点,您必须设置 sa_flags = SA_RESTART。但是,在这种情况下,将调用信号处理程序,如果 getline 不在读取过程中,它将继续等待输入。如果信号发生在读取过程中,您仍然会得到 -1,并且您必须在恢复之前 clear() 流。但是,如果读取被阻止等待数据并且信号发生,使用 SA_RESTART,您将在信号处理程序成功执行后立即返回读取。

我个人希望能够中断 getline(),这样我就可以很好地终止在管道上等待的程序并且遇到了麻烦,因为我使用了旧的 signal() 方法来设置我的处理程序。所以我会得到信号,但 getline 永远不会被中断,因为 read 被阻止等待数据,它只会在我的信号处理程序执行后自动重新启动读取。当我移动到 sigaction 结构并设置 sa_flags=0 时,它中断了 getline 导致它基本上返回 EINTR,我可以向我的进程发送一个信号来告诉它关闭。

我喜欢这个论坛。我无法计算它帮助解决棘手问题的次数。以为我会回馈。希望这对下一个程序员有帮助!

于 2014-03-07T13:46:11.830 回答
4

One of the questions you linked to actually has your answer. But first, a couple of references. Note that getline is internally using other functions (probably read).

From the man page of read:

If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR].

...

... there is an additional function, select(), whose purpose is to pause until specified activity (data to read, space to write, and so on) is detected on specified file descriptors. It is common in applications written for those systems for select() to be used before read() in situations (such as keyboard input) where interruption of I/O due to a signal is desired.

From the man page of select:

The pselect() function shall examine the file descriptor sets ... to see whether some of their descriptors are ready for reading, are ready for writing, or have an exceptional condition pending, respectively.

So you can use select to know when it's safe to start reading. However, that still doesn't prevent read from getting interrupted later. (I just said this for your information).

To actually clear the error flag of your stream, as this answer to one of your linked questions states (although in C++), you need to use the clearerr function to clear any error flags on the stream. Note that the man page (as well as the C standard) doesn't say specifically that it also clears "interrupted" flag, but that is just because that is beyond the scope of C and is in the realm of POSIX.


As a side note, I'd like to say, please don't use memset to clear a struct. C has a very nice way of doing it:

struct sigaction act = {0};

Or if you want to be pedantic, since the first element of struct sigaction could be another struct or an array, you can assign to one of its mandated fields:

struct sigaction act = { .sa_handler = NULL };
于 2013-10-02T15:59:20.867 回答