信号(signal)机制是UNIX系统中最为古老的进程之间的通信机制,它用在一个或多个进程之间传递异步信号,信号可以由各种异步事件产生,如:
键盘中断等等,在Linux 的shell 中,也可以使用信号将作业控制命令传递给它的子进程。在linux系统中,定义了一系列的信号,这些信号可以由
内核产生,也可以有系统中的其他进程产生,只要这些进程的权限足够大。进程可以屏蔽掉大多数的信号,除了SIGSTOP和SIGKILL,前者使一个
正在运行的进程暂停,后者则使一个正在运行的进程退出。应用程序收到信号后,有三种处理方式:忽略,默认,或捕捉。进程收到一个信号后,
会检查对该信号的处理机制。如果是SIG_IGN,就忽略该信号;如果是SIG_DFT,则会采用系统默认的处理动作,通常是终止进程或忽略该信号;
如果给该信号指定了一个处理函数(捕捉),则会中断当前进程正在执行的任务,转而去执行该信号的处理函数,返回后再继续执行被中断的任务。
1.信号截取函数 signal()
#include<signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
signal()是最简单的给进程安装信号处理器的函数,第一个参数指定信号,第二个参数为该信号指定一个处理函数。它的返回值为原信号处理函数,
或SIG_ERR
2.发送信号的kill和raise函数
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid, int sig);
int raise(int sig);
kill()发送信号给指定进程,raise()发送信号给进程本身。对kill()的pid,有如下描述:
pid > 0 将信号发送给ID为pid的进程
pid == 0 将信号发送给与发送进程属于同意个进程组的所有进程
pid < 0 将信号发送给进程组ID等于pid绝对值的所有进程
pid == -1 将信号发送给该进程有权限发送的系统里的所有进程
3.等待信号的pause函数
int pause(void);
pause()会使当前进程挂起,直到捕捉到一个信号,对指定为忽略的信号,pause()不会返回。只有执行了一个信号处理函数,并从其返回,
puase()才返回-1,并将errno设为EINTR。
4.信号集函数
int sigemptyset(sigset_t * set);
int sigfillset(sigset_t * set);
int sigaddset(sigset_t * set, int signum);
int sigdelset(sigset_t * set, int signum);
int sigismember(sigset_t * set, int signum);
信号集是Linux中的一种特殊的数据类型,由于无法确定信号的多少,所以不能用简单数据类型来包含所有可能的信号,所以系统就定义了一个sigset_t
的数据类型专门用于信号集。同时还定义了一族用于处理信号集的函数。这样用户可以不必关心信号集的实现,只要使用以上这组函数来处理信号集就可以了。
sigemptyset()和sigfillset()都用于初始化一个信号集,前者用于清空信号集中所有的信号,后者则用于设置信号集中所有的信号;信号集在使用前必须要经
过初始化,初始化后,就可以用sigaddset()和sigdelset()往信号集里添加删除信号了。sigismember()用于判断指定信号是否在信号集中。
5.修改信号屏蔽字的sigprocmask函数
int sigprocmask(int how, const sigset_t * set, sigset_t * oldset);
每个进程都会有一个信号屏蔽字,它规定了当前进程要阻塞的信号集。对于每种可能的信号,信号屏蔽字中都会有一位与之对应,如果该位被设置,该信号当前
就是阻塞的。进程可以通过sigprocmask()来获得和修改当前进程的信号屏蔽字。sigpromask()根据how指定的方式,设置进程的当前信号屏蔽字为set,并将
旧的信号屏蔽字保存在oldset中返回。如果set为NULL,则不修改当前信号屏蔽字,而将其通过oldset返回;如果oldset为NULL,则不会返回旧的信号屏蔽字。
how支持以下三种方式,见下表。
how |
说明 |
SIG_BLOCK
SIG_UNBLOCK
SIG_SETMASK
|
设置阻塞set指定的信号集
设置解除阻塞set指定的信号集
设置当前信号屏蔽字为set,在set中的信号都会被阻塞,不在set中的信号会被递送
|
6.获得未决的信号sigpending
int sigpending(sigset_t * set);
信号是由某些事件产生的,这些事件可能是硬件异常(如被零除),软件条件(如计时器超时),终端信号或调用kill()/raise()函数。信号产生时,内核通常会在进程表中
设置某种标志,表示当前信号的状态。当内核对信号采取某种动作时,我们说向进程递送(deliver)了一个信号,而在信号产生和递送之间的间隔内,该信号的状态是
未决的(pending)。 而sigpending函数在set中返回进程中当前尚未递送的信号集。
7.查询或设置信号处理方式的sigaction()函数
int sigaction(int signo,const struct sigaction *restrict act,
struct sigaction *restrict oact);
sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。
结构sigaction定义如下:
struct sigaction{
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flag;
void (*sa_sigaction)(int,siginfo_t *,void *);
};
sa_handler字段包含一个信号捕捉函数的地址
sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。
sa_flag是一个选项,主要理解两个
SA_INTERRUPT 由此信号中断的系统调用不会自动重启
SA_RESTART 由此信号中断的系统调用会自动重启
SA_SIGINFO 提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针
SA_INTERRUPT 由此信号中断的系统调用不会自动重启 SA_RESTART 由此信号中断的系统调用会自动重启
SA_SIGINFO 提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针
|
最后一个参数是一个替代的信号处理程序