一尘不染

SIGIO到达文件描述符时,我没有设置它,并且当没有可能的IO时

linux

我正在尝试在文件描述符上进行I / O时接收信号。该程序在不执行I / O时需要做其他事情,因此不能使用select(2)。

当我在下面运行示例代码时,即使在stdin上没有数据,它也会从处理程序内部以最快的速度打印消息。甚至更奇怪的是,siginfo_t结构中报告的文件描述符因运行而异。我只将其设置为stdin(fd
0); 处理程序为什么还要报告其他任何值?有时我看到0,有时我看到1,大多数时候我看到“?”,这表示0、1或2以外的值。

这是在OpenSUSE 12.3,Linux内核3.7.10-1.16上,但是我看到在带有其备用内核的CentOS 6.4上似乎是相同的问题。

我在处理程序中使用write,因为signal(7)表示它是可重入的,因此在信号处理程序中使用是合法的。这也是为什么我不打印sinfo->
si_fd的值的原因。snprintf不是可重入的。有一阵子我怀疑使用SIGIO的stdio库,这就是为什么示例程序中的任何地方(除了库函数err(3)之外)都没有stdio调用的原因。

感谢您花时间阅读我的代码。

#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>

int needRead = 0;
const unsigned int bufsize = 256;

void handler(int sig, siginfo_t *sinfo, void *value)
{
    char *cp;

    cp = "in handler. fd: ";
    write(2, cp, strlen(cp));
    switch(sinfo->si_fd) {
        case 0: cp = "0\n"; break;
        case 1: cp = "1\n"; break;
        case 2: cp = "2\n"; break;
        default: cp = "?\n"; break;
    }
    write(2, cp, strlen(cp));

    needRead = 1;
}

int main(int argc, char *argv[])
{
    struct sigaction act;
    unsigned int counter = 0;
    int flags;
    char *outp = ".";

    /* set up the signal handler for SIGIO */
    act.sa_sigaction = handler;
    act.sa_flags = 0;
    act.sa_flags = SA_RESTART;
    sigemptyset(&act.sa_mask);
    if (sigaction(SIGIO, &act, NULL) == -1)
        err(1, "attempt to set up handler for SIGIO failed");

    /* arrange to get the signal */
    if (fcntl(0, F_SETOWN, getpid()) == -1)
        err(1, "fnctl to set F_SETOWN failed");
    flags = fcntl(0, F_GETFL);
    if (flags >= 0 && fcntl(0, F_SETFL, flags | O_ASYNC ) == -1)
        err(1, "fnctl F_SETFL to set O_ASYNC failed");

    while (1) {
        char in_buf[bufsize];
        int nc;

        counter++;

        write(STDERR_FILENO, outp, strlen(outp));

        if (needRead) {
            needRead = 0;
            if ((nc = read(STDIN_FILENO, in_buf, bufsize)) == -1) {
                err(1, "read from stdin failed");
            } else {
                outp = "Read '";
                write(STDERR_FILENO, outp, strlen(outp));
                write(STDERR_FILENO, in_buf, nc);
                outp = "'\n";
                write(STDERR_FILENO, outp, strlen(outp));
            }
        }
    }
    return 0;
}

阅读 271

收藏
2020-06-03

共1个答案

一尘不染

啊,有趣。

简短的答案是,因为stdin是 可写的 ,所以SIGIO反复到达stdin ,并且单独地,您的SIGIO传递设置不正确。

为什么si_fd显然不可靠?

首先,您需要先指定SA_SIGINFO sa_flags才能安全使用sa_sigaction处理程序。

其次,#define _GNU_SOURCE在Linux
为您填充si_fd(和si_band)之前,您需要将F_SETSIG显式地添加到SIGIO
。有点傻,恕我直言,但事实如此。si_fd如您所发现的,没有这个,传入的值就没有意义。

为什么要反复交付SIGIO?

我猜您程序的stdin是从您调用的shell继承的,我猜它是一个终端设备并且是可写的。就像fd 0会持续 select(2)
可写一样,它也会连续为您生成SIGIO。

无论如何,si_band保持答案。启用F_SETSIG,,#include <poll.h>并检查si_bandPOLLIN,POLLOUT等,以确定哪些I / O事件使信号跳闸。

真的,stdin是可写的吗?

是的 尝试以下比较:

$ [ -w /dev/stdin ] && echo Yes, stdin is writeable
Yes, stdin is writeable

# Endless SIGIOs
$ ./my-sigio-prog
^C

# No SIGIO
$ ./my-sigio-prog < /dev/null

# Two SIGIOs, after a delay.  One for line-buffered "foo\n" and one for EOF
$ { sleep 3; echo foo; sleep 3; } | ./my-sigio-prog
2020-06-03