中断读调用信号
Interrupt read call on signal
如果按下 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 的 return 值,所以我不能使用 fork,只能使用不同的信号处理程序退出进程。
还有其他方法可以中断读取调用吗?
这是当前发生的情况:当您从键盘发送中断信号时,信号处理程序开始运行,将 \nctrl-c has been pressed\n
消息写入您的控制台并设置 should_stop
变量。然后,控制返回到 read(0, &buf, 1)
语句。由于标准输入被缓冲, read
在遇到换行符之前不会结束。如果您之后按 Enter
— read
读取一位,然后 returns。之后,再次检查条件 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)
returns 出现 EINTR
错误并且不会重新启动。
在代码的可移植性方面,signal(2)
通常不如 sigaction(2)
,您可以阅读 here
如果您将 should_stop
变量声明为 volatile
,它应该可以工作。这将指示编译器在每次访问时从内存中重新读取它:
...
volatile int should_stop = 0;
...
仅取决于您的系统,read
调用可能会在发出信号后重新启动,您必须在 Ctrl-C 后按 return 才能结束程序。默认情况下,我的 FreeBSD 11 bos 的行为是这样的。
如果您不想重新启动 read
调用,您应该使用 siginterrupt
:
明确要求该行为
...
signal(SIGINT, &sighandler);
siginterrupt(SIGINT, 1);
...
这样,程序将在 Ctrl-C 后立即停止
如果按下 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 的 return 值,所以我不能使用 fork,只能使用不同的信号处理程序退出进程。
还有其他方法可以中断读取调用吗?
这是当前发生的情况:当您从键盘发送中断信号时,信号处理程序开始运行,将 \nctrl-c has been pressed\n
消息写入您的控制台并设置 should_stop
变量。然后,控制返回到 read(0, &buf, 1)
语句。由于标准输入被缓冲, read
在遇到换行符之前不会结束。如果您之后按 Enter
— read
读取一位,然后 returns。之后,再次检查条件 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)
returns 出现 EINTR
错误并且不会重新启动。
signal(2)
通常不如 sigaction(2)
,您可以阅读 here
如果您将 should_stop
变量声明为 volatile
,它应该可以工作。这将指示编译器在每次访问时从内存中重新读取它:
...
volatile int should_stop = 0;
...
仅取决于您的系统,read
调用可能会在发出信号后重新启动,您必须在 Ctrl-C 后按 return 才能结束程序。默认情况下,我的 FreeBSD 11 bos 的行为是这样的。
如果您不想重新启动 read
调用,您应该使用 siginterrupt
:
...
signal(SIGINT, &sighandler);
siginterrupt(SIGINT, 1);
...
这样,程序将在 Ctrl-C 后立即停止