1.基本概念
信号是事件发生时对进程的通知机制,一个进程可以想另一个进程发送信号,此做法可以实现进程间的同步,然而给进程发信号的通常都是内核
(1)内核一般都会在发生以下事件时给进程发信号
.硬件发生异常
.用户键入了能够产生信号的终端特殊字符
.发生了软件事件
针对每一个信号,都定义了一个唯一的整数
(2)如果内核要调度该进程,等待信号就会马上送达,然而有时进程不想中断代码,进程可以将该信号添加到进程的信号掩码中,此信号将处于等待状态,知道进程将其从信号掩码中解除出来
(3)信号到达后,进程根据具体信号执行如下操作
.忽略信号:内核将信号丢弃,信号对进程不产生影响终止进程:进程异常终止
.停止进程:暂停进程的执行
具体的信号都是由信号处理函数来执行的
2.信号类型和默认行为
信号名称 | 描述 |
---|---|
SIGABRT | 终止进程 |
SIGALRM | 实时定时器过期 |
SIGBUS | 内存访问出错 |
SIGCHLD | 终止子进程 |
SIGCONT | 若停止则继续 |
SIGEMT | 硬件错误 |
SIGFPE | 算数异常 |
SIGHUP | 刮起 |
SIGTLL | 非法指令 |
SIGINT | 终端中断 |
SIGIO | I/0时可能产生 |
SIGKILL | 必杀 |
SIGPIPE | 管道断裂 |
SIGPROF | 性能分析定时器过时 |
SIGQUIT | 终端退出 |
SIGSEGV | 无效的内存访问 |
SIGSTKFLT | 协议处理栈错误 |
SIGSTOP | 确保停止 |
SIGSYS | 无效的系统调用 |
SIGUSR | 用户自定义信号 |
3.自定义信号处理函数signal()
#include<signal.h>
sighandler_t signal(int sig,sighandler_t handler);
第一个参数sig,标注希望修改的信号编号,第二个参数handler,则指明信号到达时,所调用的处理函数,singal的返回值为之前改信号的处理函数
特别注意:我们可以用SIG_IGN来取代handler参数,它的作用是忽略该信号,如果信号专为此进程而生,则内核会丢弃此信号
4.信号处理器
信号处理器程序(信号捕捉器)是指当指定信号传递给进程时会调用一个函数
调用信号处理器程序会打断主程序流程,内核代表进程来处理程序,当处理完之后,主程序会从打断的位置恢复执行
具体实例
//为SIGINT信号重设的处理函数
#include<stdio.h>
#include<signal.h>
void sigHanler(int sig)
{
printf("hello,world\n");
}
int main()
{
int i;
if(signal(SIGINT,sigHanler) == SIG_ERR)
{
printf("error\n");
}
for(i=0;i<3;i++)
{
printf("haha\n");
sleep(2);
}
}
运行结果如下:
5.发送信号:kill()
#include<signal.h>
int kill(pid_t,int sig);
//成功返回0,失败-1
该调用将会杀掉指定pid的进程,sig则指定了要发送的信号
.如果pid大于0,那么信号发给pid指定的进程
.pid等于0发给同组的每个进程
.pid小于-1,发送给pid绝对值下属的所有进程
.pid等于-1,调用进程有权将信号发给每个进程
6.用kill()检查进程的存在
若将sig参数置为0,则无信号发送,相反kill()仅回去执行错误检查,查看是否可以向进程发送信号。这意味着,可以用其来检查一个进程是否存在,若调用成功那么进程存在。但是这并不能保证该pid的进程就一定存在,有可能该pid指的进程已不是先前的那个进程了
判断进程存在与否的其他方法:
(1)wait()系统调运:监控子进程是否存在
(2)信号量和文件锁:如果进程持有这些,那么如果不能取得这些,就以为这进程存在
(3)/proc/PID接口:如果进程存在,目录proc/12345将存在
7.显示信号描述
#include<signal.h>
char *strsignal(int sig);
//返回可打印字符串
8.信号集
当系统调用需要一组不同的信号时,就用到了信号集,信号集的数据结构为sigset_t
(1)信号集的初始化
#include<signal.h>
int sigemptyset(sigset_t *set);
(2)向set中添加或删除单个信号
#include<signal.h>
int sigaddset(sigset_t *set,int sig);
int sigdelset(sigset_t *set,int sig);
(3)判断sig是否在set中
#include<signal.h>
int sigismember(const sigset_t *set,int sig);
9.信号掩码
内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其对进程的传递。如果将遭阻塞的信号发送给进程,对该信号的传递将延后,直至从信号掩码中移除该信号
sigprocmask系统调用:
#include<signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
sigprocmask()调运即可修改进程的信号掩码,又可获取现有掩码,或者俩重功效兼具,how参数指定给信号掩码带来的变化
how参数 | 具体描述 |
---|---|
SIG_BLOCK | 将set指向信号集中的指定信号添加到信号掩码中 |
SIG_UNBLOCK | 将set指向信号集中的信号从信号掩码中删除 |
SIG_SETMASK | 将set指向的信号集赋给信号掩码 |
10.等待状态的信号以及排队
若进程接收了一个正在阻塞的信号,则它会将该信号添加到进程的等待信号集中,不管该信号被发了多少此,在该信号被解除阻塞时,都只会给进程传递一次
11.改变信号处置:sigaction()
#include<signal.h>
int sigaction(int sig,const struct sigaction *act,struct sigaction *oldact);
.sig参数标识想要获取或改变的信号编号
.act参数是指针,指向描述信号新处置的数据结构
.oldact参数用来返回之前信号处置的相关信息,若不想获取设为NULL
sigaction结构定义如下
struct sigaction
{
void (*sa_handle)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);//不给应用程序使用
}
其中sa_flags字段是一个位掩码,指定用于控制信号处理过程的各种选项(可以相或)
sa_flag字段 | 具体描述 |
---|---|
SA_NOCLDSTOP | 若sig为SIGCHLD(终止子进程),则恢复或停止子进程时,将不再产生此信号 |
SA_NOCLDWAIT | 若sig为SIGCHLD信号,则当子进程终止时,不会将其转换为僵尸 |
SA_NODEFEP | 捕获该信号时,不会在执行处理器程序时将该信号自动添加到进程掩码中 |
SA_ONSTACK | 针对此信号调用处理器函数时,使用了由sigaltstack()安装俄被选栈 |
SA_RESTART | 自动重启由信号处理器程序中断的系统调用 |
12.pause()
#include<unistd.h>
int pause(void)
调用pause()函数将暂停进程的执行,直至信号处理函数中断该调用为止