早期的UNIX系统的一个特性:如果进程在执行一个低速系统调用而阻塞期间,捕捉到一个信号,则该系统调用就被中断而不再继续执行。该系统调用返回出错,其error被设置为EINTR。即信号中断系统调用的执行。
这样处理的理由是:因为一个信号发生了,进程捕捉到了它,那么意味着已经发生了某种事情,所以是个唤醒 被阻塞的系统调用 的机会。
为了支持系统调用被信号打断而不再执行的特性,将系统调用分为两类:低速系统调用和其他系统调用。
低速系统调用是可能会使进程永远阻塞的一类系统调用,他们包括:
1. read()、readv(),在读某些类型的文件(管道、终端设备以及网络设备)时,如果数据并不存在则可能会使调用者永远阻塞;
2. write()、writev(),在写这些类型的文件时,如果不能立即接受这些数据,则也可能会使调用者永远阻塞;
3. 打开某些类型的文件,在某种条件发生之前也可能会使调用者阻塞(例如,打开终端设备,他要等待直到所连接的调制解调器应答了电话)--->这种类型还没有实际例子测试
4. pause()和wait();
5. 某些ioctl;
6. 某些进程间通信函数;
什么是系统调用的自动重启动?
当系统调用被信号中断时,并不返回,而是继续执行。如果read()阻塞等待,当进程接受到信号时,并不将read返回,而是继续阻塞等待。
为什么要引入自动重启动的?
有时用户并不知道所使用的输入、输出设备是否是低速设备。如果编写的程序可以用交互方式运行,则他可能读、写低速终端设备。
如果在程序中捕捉到信号,而系统调用并不提供重启动功能,则对每次读、写系统调用都要进行是否出错返回的测试,如果是被信号中断,则再调用读、写系统调用。
什么时候引入系统调用的自动重启动?
4.2BSD支持某些被中断系统调用的自动重启动。
4.3BSD允许进程基于每个信号禁用自动重启动功能(因为也存在某些应用程序并不希望系统调用被中断后自动重启)
默认自动重启动的系统调用包括:ioctl(),read(),readv(),write(),writev(),wait(),waitpid();其中前5个函数只有在对低速设备进行操作时才会被信号中断。而wait和waitpid在捕捉到信号时总是被中断。
我的测试环境是4.3BSD,下面以read()为例来说明自动重启动和被中断。
自动重启动:
restart.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <signal.h> 6 #include <errno.h> 7 8 #define BUFSIZE (1024) 9 10 static void sig_alrm(int signo) 11 { 12 printf("caught alarm... "); 13 } 14 15 void sig_usr(int signo) 16 { 17 if (signo == SIGUSR1) 18 printf("received SIGUSR1 "); 19 else if (signo == SIGUSR2) 20 printf("received SIGUSR2 "); 21 else 22 printf("received signal %d ", signo); 23 } 24 25 int main(int argc, char** argv) 26 { 27 int nSize = 0; 28 char acBuf[BUFSIZE] = {0}; 29 30 signal(SIGUSR1, sig_usr); 31 signal(SIGALRM, sig_alrm); 32 33 alarm(5); 34 35 // while(1) 36 { 37 38 memset(acBuf, '