如果按下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,而只能使用其他信号处理程序退出该过程。
还有另一种方法来中断读取调用吗?
答案 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之后立即停止