1. 每个信号都有一个名字,都以SIG开头。 信号被定义为正整数。 不存在编号为0的信号。
头文件是:signal.h
2. 信号是异步事件。产生信号的事件对进程而言是随机的,进程必须告诉内核,当此信号发生时候,请执行以下操作。
3. 信号处理方式,有三种:
1)忽略信号。 但是SIGKILL和SIGSTOP不能忽略。
2)捕捉信号。 通知内核在某个信号发生时调用一个用户函数处理,但是SIGKILL和SIGSTOP不能捕捉。
例如:捕捉到SIGCHLD信号,表示一个子进程结束,在用户函数中调用wait/waitpid结束子进程。
3)执行系统默认动作。
4. 当一个进程fork时候,其子进程继承父进程的信号处理方式,信号捕捉函数的地址在子进程中是有意义的。
5. 可以使用signal和sigaction处理信号
1)signal函数
void signal(int signo, handler hander);
其中:
signo--信号名字
hander有三种选择:1、SIG_IGN 忽略信号 2、SIG_DFL 执行默认动作 3、 捕捉信号后调用的函数地址(回调函数) 例如:typedef void (*handler)(int)
例如:
void handler(int signo)
{
//处理信号
}
int main()
{
if(signal(SIGUSR1, handler) == SIG_ERR) //捕捉SIGUSR1信号
{
cout<<"error"<<endl;
return 1;
}
for(;;)
pause(); //挂起程序,直到捕捉到任意一个信号后返回
}
./a.out & //后台执行程序
kill -USR1 PID //捕捉SIGUSR1信号,执行handler函数。
2)sigaction函数
1、信号集的概念
用sigset_t定义信号集。头文件是signal.h
处理信号集的函数:
sigemptyset:初始化信号集;
sigaddset:将信号加入信号集中; sigdelset:从信号集中删掉信号
sigismember:判断信号是否是信号集的成员
sigpending(sigset_t *set):获取当前已经送到进程,但是被阻塞的所有信号,在set指向的信号集中返回结果。
2、屏蔽信号(阻塞,延迟信号送到。信号解除阻塞后继续传递)
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how有三种选择:
SIG_BLOCK:阻塞set信号集
SIG_UNBLOCK:将set信号集解除阻塞
SIG_SETMASK:更新当前阻塞信号集为set指向的信号集。
例如:验证sigprocmask可以阻塞信号
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1); //将信号加入信号集
sigaddset(&set, SIGUSR2);
signal(SIGUSR1, handler); //捕捉信号
signal(SIGUSR2, handler);
sigprocmask(SIG_BLOCK, &set, NULL); //阻塞set信号集
kill(getpid(), SIGUSR1); //发送信号SIGUSR1和SIGUSR2
kill(getpid(), SIGUSR2);
sleep(2); //为了证明信号被阻塞了,handler不打印内容。
sigprocmask(SIG_UNBLOCK, &set, NULL); //解除阻塞,handler立即打印内容。
3、头文件:signal.h
int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);
注意:signo可以指定除了SIGKILL和SIGSTOP之外的所有的信号;
act:非空,则为要修改的动作;
oldact:非空,返回该信号的上一个动作;
注意:sigaction既可以设置信号的处理方式,也可检验信号的目前设置的处置方式。
4、sigaction结构体:
struct sigaction{
void (*sa_handler)(int);
void (*sa_sigaction)(//); //sa_handler和sa_sigaction为信号捕捉函数的地址。不要同时指定两个字段值,默认用第一个。
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void); //已经不再使用
};
说明:
sa_handler:可以是处理信号的三种方式:忽略、执行系统默认动作、捕捉信号调用函数的地址
sa_mask:指定信号处理函数执行过程中应被阻塞的信号,缺省下,当前信号本身被阻塞。
例如:
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGINT); //在handler执行期间阻塞SIGINT信号,直至handler返回
例如:
void handler(int signo)
{
//处理SIGUSR1消息
cout<<"SIGUSR1"<<endl;
kill(getpid(), SIGUSR2); //发送SIGUSR2信号,但是之前设置了阻塞,所以信号会被阻塞
sleep(2); //为了验证SIGUSR2信号确实被阻塞了。
}
void handler_sig(int signo)
{
//处理SIGUSR2消息
cout<<"SIGUSR2"<<endl;
}
int main()
{
struct sigaction act,oldact;
//设置捕捉SIGUSR1
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGUSR2); //handler函数执行期间,阻塞信号SIGUSR2,直至handler返回
act.sa_flags = 0;
sigaction(SIGUSR1, NULL, &oldact);
if(oldact.sa_handler != SIG_IGN) //之前处理方式不是忽略掉信号
sigaction(SIGUSR1, &act, NULL); //安装信号处理,捕捉SIGUSR1信号
//设置捕捉SIGUSR2
act.sa_handler = handler_sig;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR2, NULL, &oldact);
if(oldact.sa_handler != SIG_IGN) //非忽略处置方式
sigaction(SIGUSR2, &act, NULL);
kill(getpid(), SIGUSR1); //发送SIGUSR1信号
return 0;
}
5、头文件signal.h
int kill(pid_t pid, int signo); //发信号给进程
例如:kill(getpid(), SIGINT);
6、可重入函数:即可被中断的函数,即在函数执行任何时候都可被中断的函数。注:malloc是非可重入函数。
7、头文件signal.h
int sigsuspend(const sitset_t *mask); //挂起进程,等待信号
接收到某个信号前,临时用mask替换进程的信号掩码,挂起进程,直到信号到来。
注意:sigsuspend是原子操作,执行以下四步:(要么都执行,要么都不执行)
1)用mask替换当前信号掩码,阻塞进程
2)收到信号,执行相应处理函数
3)函数返回后,恢复原先的mask
4)sigsuspend返回
实例:
int main()
{
sigset_t newset,old,wait; //三个信号集
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL); //捕捉SIGINT信号
sigaction(SIGQUIT, &act, NULL); //捕捉SIGQUIT信号
sigemptyset(&newset);
sigaddset(&newset, SIGINT); //SIGINT信号加入newset信号集中
sigemptyset(&wait);
sigaddset(&wait, SIGQUIT); //SIGQUIT信号加入wait信号集中
sigprocmask(SIG_BLOCK, &newset, &old); //阻塞SIGINT信号,保存之前的信号集
if(sigsuspend(&wait) != -1) //说明:程序挂起;用wait信号集替换newset,即阻塞SIGQUIT信号,而放通SIGINT信号。当SIGQUIT信号过来,阻塞掉;当SIGINT信号 过来,调相应函数处理,函数返回后,用newset信号集替换wait,即阻塞SIGINT信号,而放通SIGQUIT信号了。同时sigsuspend返回。
cout<<"error"<<endl;
sigprocmask(SIG_SETMASK, &old, NULL); //恢复之前的信号集
return 0;
}