在早期UNIX系统中,进程在执行一个低速系统调用而阻塞,
如果在此期间捕捉到一个信号,则该系统调用就被中断不再继续执行。该系统调用返回出错,其errno被设置成为EINTR。这样处理的理由是:因为一个信号发生了,进程捕捉到了它,这意味着已经发生了某种事情,所以是唤醒阻塞的系统调用的好机会。
但是,有些应用场景下,我们想让系统调用不要因为软中断(信号)而退出。典型的例子:一个进程启动了读终端操作,而是使用者却离开该终端很长时间。这种情况下进程可能处于阻塞状态几个小时甚至数天,除非系统停机,否则我们不希望退出。所以有些系统引入了某些被中断系统调用的自动重启动。
函数
|
系统
|
保持安装信号处理程序
|
阻塞信号的能力
|
被中断后的系统调用自动重启动?
|
signal
|
BSD/LINUX/Free BSD/MAC OS X
|
yes
|
yes
|
默认
|
sigaction
|
BSD/LINUX/Free BSD/MAC OS X
|
yes
|
yes
|
可选
|
可见在大部分系统中signal函数注册的信号,被中断后的系统调用是默认自动重启动的,而sigaction是可选的。
下面是一个通过sigaction方式定义的Signal函数,它对大部分中断采取重启动(SIGALRM除外)
-
typedef void Sigfunc(int);
-
-
Sigfunc * Signal(int signo, Sigfunc *func)
-
{
-
struct sigaction act, oact;
-
-
act.sa_handler = func;
-
sigemptyset(&act.sa_mask);
-
act.sa_flags = 0;
-
-
if(signo == SIGALRM) {
-
#ifdef SA_INTERRUPT
-
act.sa_flags |= SA_INTERRUPT;
-
#endif
-
}else {
-
#ifdef SA_RESTART
-
act.sa_flags |= SA_RESTART;
-
#endif
-
}
-
-
if(sigaction(signo,&act,&oact) < 0) {
-
return SIG_ERR;
-
}
-
return oact.sa_handler;
-
}
理论介绍完毕,下面举一个具体的例子
1.使用signal函数
alarm.c 文件
-
#include <stdio.h>
-
#include <signal.h>
-
#include <unistd.h>
-
#define MAXLINE 4096
-
-
static void sig_alrm(int signo)
-
{
-
}
-
int main(void)
-
{
-
int n;
-
char line[MAXLINE];
-
alarm(10);
-
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
-
perror("signal");
-
if((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
-
perror("read");
-
alarm(0);
-
-
write(STDOUT_FILENO, line, n);
-
exit(0);
-
}
编译: gcc -g alarm.c -o a
运行: ./a
发现程序并没有像我们期待的中断退出,而是一直处于阻塞状态。
这个是什么原因呢?
没错,原因就是上面表格中描述的,对于linux系统,signal函数来注册信号,它会把SA_RESTART打开,这个就是自动重启动标志,显然,有了这个标志,导致read系统调用不返回,而是重新启动read操作,所以代码一直处于阻塞状态。
那如果要看到read被中断,怎么办?
对的,当然是要使用sigaction注册信号,并且把SA_RESTART标志关闭,上面新实现的Signal函数就实现了这个功能,代码修改如下:
-
#include <stdio.h>
-
#include <signal.h>
-
#include <unistd.h>
-
#define MAXLINE 4096
-
-
typedef void Sigfunc(int);
-
-
Sigfunc * Signal(int signo, Sigfunc *func)
-
{
-
struct sigaction act, oact;
-
-
act.sa_handler = func;
-
sigemptyset(&act.sa_mask);
-
act.sa_flags = 0;
-
-
if(signo == SIGALRM) {
-
#ifdef SA_INTERRUPT
-
act.sa_flags |= SA_INTERRUPT;
-
#endif
-
}else {
-
#ifdef SA_RESTART
-
act.sa_flags |= SA_RESTART;
-
#endif
-
}
-
-
if(sigaction(signo,&act,&oact) < 0) {
-
return SIG_ERR;
-
}
-
return oact.sa_handler;
-
}
-
-
static void sig_alrm(int signo)
-
{
-
}
-
int main(void)
-
{
-
int n;
-
char line[MAXLINE];
-
alarm(10);
-
if(Signal(SIGALRM, sig_alrm) == SIG_ERR)
-
perror("signal");
-
if((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
-
perror("read");
-
alarm(0);
-
-
write(STDOUT_FILENO, line, n);
-
exit(0);
-
}
编译: gcc -g alarm.c -o a
运行: ./a
过了10s后提示:
read: Interrupted system call
程序退出。
阅读(604) | 评论(0) | 转发(0) |