简体   繁体   English

信号中断读取调用

[英]Interrupt read call on signal

I have to interrupt a read call if ctrl-c is pressed, using signal.如果按下 ctrl-c,我必须使用信号中断读取调用。 I wrote this (simplified) sample code:我写了这个(简化的)示例代码:

#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);
}

As read is a blocking call (as far as I understood), the should_stop global variable will not be evaluated once read has been called.由于 read 是一个阻塞调用(据我所知),一旦调用 read 就不会评估 should_stop 全局变量。 So I don't know how I could interrupt the read call by pressing ctrl-c.所以我不知道如何通过按 ctrl-c 来中断读取调用。

Another constraint is that I'm only allowed to use those functions:另一个限制是我只能使用这些功能:

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

So I can't use select to set a timeout value.所以我不能使用 select 来设置超时值。 As I also need the return value of read_function, I can't use fork and just exit the process with a different signal handler.由于我还需要 read_function 的返回值,所以我不能使用 fork,而只能使用不同的信号处理程序退出进程。

Is there another way to interrupt the read call?还有其他方法可以中断读取调用吗?

This is what currently happens: when you send an interrupt signal from keyboard, the signal handler comes into action, writes the \nctrl-c has been pressed\n message to your console and sets the should_stop variable.这是当前发生的情况:当您从键盘发送中断信号时,信号处理程序开始工作,将\nctrl-c has been pressed\n消息写入您的控制台并设置should_stop变量。 Then, control is returned back to read(0, &buf, 1) statement.然后,控制权返回给read(0, &buf, 1)语句。 As the stdin is buffered, read won't be over until it meets a newline.由于标准输入被缓冲, read在遇到换行之前不会结束。 If you press Enter afterwards — read reads one bit and returns.如果您之后按Enterread读取一位并返回。 After that, a condition should_stop is checked again, and since it contains 1 value now — loop is over.之后,再次检查条件should_stop ,因为它现在包含1值 - 循环结束。

Now, we want to modify that behavior so that your program is gracefully shut down after SIGINT.现在,我们要修改该行为,以便您的程序在 SIGINT 之后正常关闭。

From man 7 signal :来自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:

From man 2 signal :来自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;

So, here's how we employ sigaction(2) for our case:因此,以下是我们在案例中使用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);
}

This way, upon interruption by signal handler, read(2) returns with EINTR error and is not restarted.这样,当信号处理程序中断时, read(2)会返回EINTR错误并且不会重新启动。

signal(2) is generally inferior to sigaction(2) when it comes to portability of the code, you can read about it here在代码的可移植性方面, signal(2)通常不如sigaction(2) ,你可以在这里阅读

It should work provided you declare the should_stop variable to be volatile .如果您将should_stop变量声明为volatile ,它应该可以工作。 This will instruct the compiler to re-read it from memory on every access:这将指示编译器在每次访问时从 memory 重新读取它:

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

Simply depending on your system, a read call may be restarted after a signal, and you will have to hit return after the Ctrl-C to end the program.取决于您的系统, read调用可能会在发出信号后重新启动,并且您必须在 Ctrl-C 之后按回车键才能结束程序。 By default, my FreeBSD 11 bos behaves like that.默认情况下,我的 FreeBSD 11 bos 的行为是这样的。

If you want the read call not to be restarted, you should explicitely ask for that behaviour with siginterrupt :如果您希望不重新启动read调用,则应使用siginterrupt明确要求该行为:

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

That way, the program will stop immediately after the Ctrl-C这样,程序将在 Ctrl-C 后立即停止

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM