1. 信号通信
1) 概念
信号是软件中断,信号机制是UNIX系统最为古老的进程之间的通信机制,它用于一个或多个进程之间传递异步信号
2) 主要的信号源:
异常:进程运行过程中出现异常
其他进程:一个进程可以向另一个进程发送信号
终端中断:ctr+C
作业控制
分配额
通知
报警:计时器到期
3) 常见信号
4) 信号的发送和捕捉
A. Kill和raise函数
kill()不仅可以中止进程,也可以向进程发送其他信号。
与kill函数不同的是,raise()函数运行向进程自身发送信号。
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signo) ;
int raise(int signo) ;
两个函数返回:若成功则为0,若出错则为-1。
kill的pid参数有四种不同的情况:
n pid>0 将信号发送给进程ID为pid的进程。
n pid == 0 将信号发送给其进程组I D等于发送进程的进程组ID,而且发送进程有许可权向其发送信号的所有进程。
n pid < 0 将信号发送给其进程组ID等于pid绝对值,而且发送进程有许可权向其发送信号的所有进程。如上所述一样,“所有进程”并不包括系统进程集中的进程。
n pid ==-1 POSIX.1未定义此种情况。
B. alarm和pause函数
使用alarm函数可以设置一个时间值(闹钟时间),在将来的某个时刻该时间值会被超过。当所设置的时间值被超过后,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止该进程。
#include <unistd.h>
unsigned int alarm(unsigned int seconds) ;
返回:0或以前设置的闹钟时间的余留秒数
参数seconds的值是秒数,经过了指定的seconds秒后会产生信号SIGALRM。
每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的值返回。以前登记的闹钟时间则被新值代换。
pause函数使调用进程挂起直至捕捉到一个信号。
#include <unistd.h>
int pause(void);
返回:-1,errno设置为EINTR
只有执行了一个信号处理程序并从其返回时,pause才返回。
5) 信号处理
A. 方式:
l 使用简单的signal函数
l 使用信号集函数组
B. Signal函数
#include<signal.h>
void (*signal (int signo,void (*func)(int)))(int)
返回:成功则为以前的信号处理配置,若出错则为SIG_ERR
func的值是: (a)常数SIG_IGN,或(b)常数SIG_DFL,或(c)当接到此信号后要调用的函数的地址。如果指定SIG_IGN ,则向内核表示忽略此信号(有两个信号SIGKILL和SIGSTOP不能忽略)。如果指定SIG_DFL,则表示接到此信号后的动作是系统默认动作。当指定函数地址时,我们称此为捕捉此信号。我们称此函数为信号处理程序(signalhandler)或信号捕捉函数(signal-catchingfunction)。
signal函数原型太复杂了,如果使用下面的typedef,则可使其简化。
typedef voidsign(int);
sign *signal(int,handler *);
C. 信号集函数
信号集函数组包含几大模块: 创建函数集、登记信号集、检测信号集。
l 创建信号集函数
#include <signal.h>
int sigemptyset(sigset_t * set) ;
int sigfillset(sigset_t * set) ;
int sigaddset(sigset_t * set,int signo) ;
int sigdelset(sigset_t * set,int signo) ;
四个函数返回:若成功则为0,若出错则为-1
int sigismember(const sigset_t * set, int signo) ;
返回:若真则为1,若假则为0。
sigemptyset:初始化信号集合为空。
sigfillset:初始化信号集合为所有信号的集合。
sigaddset:将指定信号添加到现存集中。
sigdelset:从信号集中删除指定信号。
sigismember:查询指定信号是否在信号集合中。
l 登记信号集
登记信号处理机主要用于决定进程如何处理信号。首先要判断出当前进程阻塞能不能传递给该信号的信号集。这首先使用sigprocmask函数判断检测或更改信号屏蔽字,然后使用sigaction函数改变进程接受到特定信号之后的行为。
u一个进程的信号屏蔽字可以规定当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改(或两者)进程的信号屏蔽字。
# include <signal.h>
int sigprocmask(int how, const sigset_t * set, sigset_t * oset) ;
返回:若成功则为0,若出错则为-1
oset是非空指针,进程的当前信号屏蔽字通过oset返回。其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。
用sigprocmask更改当前信号屏蔽字的方法,how参数设定:
n SIG_BLOCK该该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号。
n SIG_UNBLOCK该该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集的交集。set包含了我们希望解除阻塞的信号。
n SIG_SETMASK该该进程新的信号屏蔽是set指向的值。
如果set是个空指针,则不改变该进程的信号屏蔽字, how的值也无意义。
usigaction函数的功能是检查或修改(或两者)与指定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。
#include <signal.h>
int sigaction(int signo, const struct sigaction * act,struct sigaction *oact) ;
返回:若成功则为0,若出错则为- 1
参数signo是要检测或修改具体动作的信号的编号数。若act指针非空,则要修改其动作。如果oact指针非空,则系统返回该信号的原先动作。此函数使用下列结构:
struct sigaction {
void (*sa_handler)(intsigno);
sigset_t sa_mask;
int sa_flags;
void (*sa_restore);
sa_handler是一个函数指针,指定信号关联函数,可以是自定义处理函数,还可以SIG_DFL或 SIG_IGN。
sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被阻塞。
sa_flags中包含许多标志 三位,是对信号进行处理的各种选项。具体如下:
n SA_NODEFER\SA_NOMASK:当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动阻塞此信号。
n SA_NOCLDSTOP:进程忽略子进程产生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信号
n SA_RESTART:可让重启的系统调用重新起作用。
n SA_ONESHOT\SA_RESETHAND:自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作。
l 检测信号集
usigpending返回对于调用进程被阻塞不能递送和当前未决的信号集。
#include <signal.h>
int sigpending(sigset_t * set) ;
返回:若成功则为0,若出错则为-1