"wait and waitpid are always interrupted when a signal is caught" 是什么意思?
What does "wait and waitpid are always interrupted when a signal is caught" mean?
来自APUE:
To prevent applications from having to handle interrupted system
calls, 4.2BSD introduced the automatic restarting of certain
interrupted system calls. The system calls that were automatically
restarted are ioctl
, read
, readv
, write
, writev
, wait
, and waitpid
. As
we’ve mentioned, the first five of these functions are interrupted by
a signal only if they are operating on a slow device; wait
and waitpid
are always interrupted when a signal is caught. Since this caused a
problem for some applications that didn’t want the operation restarted
if it was interrupted, 4.3BSD allowed the process to disable this
feature on a per-signal basis.
是不是说在自动重启被引入之前,如果一个进程捕获到信号,wait
和waitpid
会立即停止等待并执行后续代码?
例如:
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void handler(int sig){}
void handler2(int sig){}
int main(){
pid_t pid;
int status;
signal(SIGUSR1, handler);
signal(SIGUSR2, handler2);
if((pid == fork()) < 0){
printf("fork error\n");
}else{
if(pid){
//child
//do something, needs several hours.
}else{
//parent
waitpid(pid, &status, 0);
printf("Hello world\n");
}
}
return 0;
}
如果不提供自动重启,当我运行这个程序在后台gcc test.c && ./a.out &
,然后我发送一个信号kill -SIGUSR1 pid
或者kill -SIGUSR2 pid
、waitpid
将 return 和 waitpid(pid, &status, 0);
之后的代码将执行。
并且如果提供自动重启,waitpid
将再次执行并且父级将继续等待。
我的理解正确吗?
wait
和 waitpid
与任何其他阻塞函数一样,可以通过将 errno
设置为 EINTR
来中断 - 这 正是因为 信号处理程序可以做的事情非常少 - 主要是设置一些标志。现在,如果一个阻塞函数 而不是 return 和 EINTR
,你怎么能以任何方式对信号做出反应?!
但这也意味着您需要对每个函数进行复杂的循环 - 您可能有 一些 信号,您知道您不希望系统调用被中断, 所以你可以设置这个信号有自动重启。
signal()
(System-V 语义)的原始行为是,如果进程当前处于睡眠状态,则中断任何系统调用,执行信号处理程序和系统调用 returns -EINTR
.然后,BSD4.3发明了restart机制,任何系统调用中断后都会自动重启。如果涉及信号处理程序,这避免了为每个系统调用编写循环。
Linux 没有改变 signal()
syscall 的语义。但是,现在 signal()
glibc 包装函数 默认调用带有 SA_RESTART
标志的系统调用 sigaction()
。因此,如果您 不需要 需要重新启动行为,则必须调用 sigaction()
并省略该标志。
因此,您的代码确实在 BSD 和 linux
上都使用了 restart 机制
来自APUE:
To prevent applications from having to handle interrupted system calls, 4.2BSD introduced the automatic restarting of certain interrupted system calls. The system calls that were automatically restarted are
ioctl
,read
,readv
,write
,writev
,wait
, andwaitpid
. As we’ve mentioned, the first five of these functions are interrupted by a signal only if they are operating on a slow device;wait
andwaitpid
are always interrupted when a signal is caught. Since this caused a problem for some applications that didn’t want the operation restarted if it was interrupted, 4.3BSD allowed the process to disable this feature on a per-signal basis.
是不是说在自动重启被引入之前,如果一个进程捕获到信号,wait
和waitpid
会立即停止等待并执行后续代码?
例如:
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void handler(int sig){}
void handler2(int sig){}
int main(){
pid_t pid;
int status;
signal(SIGUSR1, handler);
signal(SIGUSR2, handler2);
if((pid == fork()) < 0){
printf("fork error\n");
}else{
if(pid){
//child
//do something, needs several hours.
}else{
//parent
waitpid(pid, &status, 0);
printf("Hello world\n");
}
}
return 0;
}
如果不提供自动重启,当我运行这个程序在后台gcc test.c && ./a.out &
,然后我发送一个信号kill -SIGUSR1 pid
或者kill -SIGUSR2 pid
、waitpid
将 return 和 waitpid(pid, &status, 0);
之后的代码将执行。
并且如果提供自动重启,waitpid
将再次执行并且父级将继续等待。
我的理解正确吗?
wait
和 waitpid
与任何其他阻塞函数一样,可以通过将 errno
设置为 EINTR
来中断 - 这 正是因为 信号处理程序可以做的事情非常少 - 主要是设置一些标志。现在,如果一个阻塞函数 而不是 return 和 EINTR
,你怎么能以任何方式对信号做出反应?!
但这也意味着您需要对每个函数进行复杂的循环 - 您可能有 一些 信号,您知道您不希望系统调用被中断, 所以你可以设置这个信号有自动重启。
signal()
(System-V 语义)的原始行为是,如果进程当前处于睡眠状态,则中断任何系统调用,执行信号处理程序和系统调用 returns -EINTR
.然后,BSD4.3发明了restart机制,任何系统调用中断后都会自动重启。如果涉及信号处理程序,这避免了为每个系统调用编写循环。
Linux 没有改变 signal()
syscall 的语义。但是,现在 signal()
glibc 包装函数 默认调用带有 SA_RESTART
标志的系统调用 sigaction()
。因此,如果您 不需要 需要重新启动行为,则必须调用 sigaction()
并省略该标志。
因此,您的代码确实在 BSD 和 linux
上都使用了 restart 机制