中断读呼叫信号

时间:2020-04-03 07:27:24

标签: c process signals

如果按下ctrl-c,则必须使用信号中断读取调用。 我写了这个(简化的)示例代码:

#include <unistd.h>
#include <sys/wait.h>

int should_stop = 0;

void sighandler(int signal)
{
    write(1, "\nctrl-c has been pressed\n", 25);
    should_stop = 1;
}

void read_function()
{
    char c;

    while (!should_stop)
        read(0, &c, 1);
    //Do some stuff and return someting
}

int main()
{
    signal(SIGINT, &sighandler);
    read_function();
    write(1, "read_function is over\n", 22);
    return (0);
}

由于read是一个阻塞调用(据我所知),一旦调用read,将不对should_stop全局变量进行求值。 因此,我不知道如何通过按ctrl-c来中断读取调用。

另一个限制是只允许我使用这些功能:

- write
- read
- fork
- wait
- signal
- kill
- exit

所以我不能使用select设置超时值。 因为我还需要read_function的返回值,所以我不能使用fork,而只能使用其他信号处理程序退出该过程。

还有另一种方法来中断读取调用吗?

2 个答案:

答案 0 :(得分:2)

这是当前发生的情况:当您从键盘发送中断信号时,信号处理程序开始工作,将\nctrl-c has been pressed\n消息写入控制台并设置should_stop变量。然后,控制权返回到read(0, &buf, 1)语句。由于标准输入已缓冲,read直到遇到换行符时才结束。如果之后再按Enter,则read读取一位并返回。之后,再次检查条件should_stop,由于它现在包含1值-循环结束。

现在,我们要修改该行为,以便在SIGINT之后正常关闭您的程序。

来自man 7 signal

If  a  blocked  call  to one of the following interfaces is interrupted by a
signal handler, then the call is automatically restarted  after  the  signal
handler  returns  if  the SA_RESTART flag was used; otherwise the call fails
with the error EINTR:

来自man 2 signal

certain  blocking  system calls are automatically
restarted if interrupted by a signal handler (see signal(7)).  The  BSD  se‐
mantics are equivalent to calling sigaction(2) with the following flags:

   sa.sa_flags = SA_RESTART;

因此,这是我们为案例使用sigaction(2)的方式:

int main()
{
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = sighandler;
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, NULL);
    read_function();
    write(1, "read_function is over\n", 22);
    return (0);
}

这样,read(2)在被信号处理程序中断时返回,并返回EINTR错误,并且不会重新启动。

signal(2)在代码的可移植性方面通常不如sigaction(2),您可以阅读here

答案 1 :(得分:1)

只要您声明should_stop变量为volatile,它就可以工作。这将指示编译器在每次访问时从内存中重新读取它:

...
volatile int should_stop = 0;
...

仅取决于您的系统,在信号发出后可能会重新启动read调用,并且您必须在Ctrl-C之后按回车键才能结束程序。默认情况下,我的FreeBSD 11 Bos就是这样。

如果您不希望重新启动read呼叫,则应使用siginterrupt明确要求该行为:

...
signal(SIGINT, &sighandler);
siginterrupt(SIGINT, 1);
...

这样,程序将在Ctrl-C之后立即停止