信号是给程序提供一种可以处理异步事件的方法,它利用软件中断来实现。 我们无法自定义信号,所有信号都是系统预定义的。
比如: socket通信或管道通信,如果读端已经关闭,再执行写操作(或者进行发送数据),将导致执行写操作的进程收到SIGPIPE信号(表示管道破裂)。 该信号的默认行为——终止该进程。
示例:
kill -HUP pid # 向PID为pid的进程发送SIGHUP信号#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myhandle(int sig) {
printf("Catch a signal : %d\n", sig);
}
int main(void) {
//设置信号处理函数
signal(SIGINT, myhandle);//如果收到SIGINT信号,就执行myhandle,如果未设置信号处理函数,则进行默认处理——终止。
while (1) {
sleep(1);
}
return 0;
}int kill(pid_t pid, int sig); //进程pid,信号类型信号名称 & 说明
SIGABORT—— 进程异常终止 SIGALRM ——超时告警 SIGFPE —— 浮点运算异常 SIGHUP ——连接挂断 SIGILL——非法指令 SIGINT ——终端中断 (Ctrl+C将产生该信号) SIGKILL ——*终止进程 SIGPIPE ——向没有读进程的管道写数据 SIGQUIT——终端退出(Ctrl+\将产生该信号) SIGSEGV ——无效内存段访问 SIGTERM ——终止 SIGUSR1——*用户自定义信号1 SIGUSR2 ——*用户自定义信号2 -------------------------------------->以上信号如果不被捕获,则进程接受到后都会终止! SIGCHLD——子进程已停止或退出 SIGCONT ——*让暂停的进程继续执行 SIGSTOP ——*停止执行(即“暂停") SIGTSTP——断挂起 SIGTTIN —— 后台进程尝试读操作 SIGTTOU——后台进程尝试写
信号的捕获是指,在接收到某种信号后,去执行指定的函数。 注意:
sighandler_t signal(int signum, sighandler_t handler);示例:
signal(SIGINT, myhandle);//myhandle为自定义信号处理函数与signal相比,sigaction更加健壮。
结构体sigaction:
struct sigaction {
//信号的响应函数
void (*sa_handler)(int);
//略...
void (*sa_sigaction)(int, siginfo_t *, void *);
//搁置信号集
sigset_t sa_mask;
//当sa_flags中包含 SA_RESETHAND时,接收到该信号并调用指定的信号处理函数执行之后,把该信号的响应行为重置为默认行为SIG_DFL
//即自定义的信号处理函数执行一次后,回到默认处理方式。
int sa_flags;
//略...
void (*sa_restorer)(void);
};补充:
函数sigaction:
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact//保存旧设置
);示例1:使用示例
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myhandle(int sig) {
printf("Catch a signal : %d\n", sig);
}
int main(void) {
struct sigaction act;
act.sa_handler = myhandle;
sigemptyset(&act.sa_mask);//清空搁置集
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
while (1) {
sleep(1);
printf("sleep 1 second.\n");
}
return 0;
} 示例2: 输入A主进程向子进程发送SIGUSR1信号,输出大写字符;输入a主进程向子进程发送SIGUSR2信号,输出小写字符。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int workflag = 0;
void work_up_handle(int sig) {
workflag = 1;
}
void work_down_handle(int sig) {
workflag = 0;
}
/*
看起来貌似一个sigaction结构体对象可以设置多个信号与信号处理函数对。
即,根据接收到的信号来到调用对应的信号处理函数。
*/
int main(void) {
pid_t pd;
char c;
pd = fork();
if (pd == -1) {
printf("fork error!\n");
exit(1);
} else if (pd == 0) {//子进程
char *msg;
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = work_up_handle;//结构体设置信号处理函数
sigemptyset(&act.sa_mask);
sigaction(SIGUSR1, &act, 0);
act.sa_handler = work_down_handle;//结构体设置信号处理函数
sigaction(SIGUSR2, &act, 0);
while (1) {
if (!workflag) {
msg = "child process work!";
} else {
msg = "CHILD PROCESS WORK!";
}
printf("%s\n", msg);
sleep(1);
}
} else {//父进程
while(1) {
c = getchar();
if (c == 'A') {
kill(pd, SIGUSR1);//给子进程pd发送信号 SIGUSR1
} else if (c == 'a') {
kill(pd, SIGUSR2);//给子进程pd发送信号 SIGUSR2
}
}
}
return 0;
}示例3:使用子进程定时给父进程发送SIGALRM信号。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int wakeflag = 0;
void wake_handle(int sig) {
wakeflag = 1;
}
int main(void) {
pid_t pd;
char c;
pd = fork();
if (pd == -1) {
printf("fork error!\n");
exit(1);
} else if (pd == 0) {
for(;;){
sleep(5);
//getppid()获取父进程id
kill(getppid(), SIGALRM);//给父进程发送SIGALRM信号
}
} else {
struct sigaction act;
act.sa_handler = wake_handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);//清空
sigaction(SIGALRM, &act, 0);//设置信号响应
for(;;){
pause(); //把该进程挂起,直到收到任意一个信号
if (wakeflag) {
printf("Alarm clock work!!!\n");
}
}
}
return 0;
}
unsigned int alarm(unsigned int seconds);#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
int wakeflag = 0;
void wake_handle(int sig)
{
wakeflag = 1;
}
int main(void) {
int ret;
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = wake_handle;//设置响应函数
sigemptyset(&act.sa_mask);
sigaction(SIGALRM, &act, 0);//设置信号-响应
printf("time =%ld\n", time((time_t*)0));
ret = alarm(5);
if (ret == -1) {
printf("alarm error!\n");
exit(1);
}
for(;;){
//挂起当前进程,直到收到任意一个信号
pause();
if (wakeflag) {
printf("wake up, time =%ld\n", time((time_t*)0));
}
}
return 0;
}前提:对应信号已绑定对应的信号处理函数。详见信号的处理。 某进程在执行某个信号对应的操作函数期间(即,该对应信号的安装函数),如果此时,该进程又多次收到同一个信号(同一种信号值的信号)。
某个进程正在执行某个信号对应的操作函数期间(该信号的安装函数),如果此时,该进程又收到一个信号(不同信号值的信号),则: 如果,该信号被包含在当前信号的signaction的sa_mask(信号搁置集)中,则不会立即处理该信号。直到当前的信号处理函数执行完毕后,才进行执行。 反之,如果该信号不在信号搁置集中,则中断当前信号处理函数,如果处于睡眠,比如sleep, 也会立即被唤醒,来执行新的这个信号处理函数,新的这个信号处理函数执行完毕后,再在返回至原来的信号处理函数继续执行。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myhandle(int sig)
{
printf("Catch a signal : %d\n", sig);
int i;
for (i=0; i<10; i++) {
sleep(1);
}
printf("Catch end.%d\n", sig);
}
int main(void)
{
struct sigaction act, act2;
act.sa_handler = myhandle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGUSR1);//将信号SIGUSR1加入到信号搁置集中
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);//处理SIGINT停止信号
//收到SIGINT信号,如果此时再收到SIGUSR1,则会执行完后在进行SIGUSR1信号处理。
act2.sa_handler = myhandle;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, &act, 0);//处理SIGUSR1信号
while (1) {
}
return 0;
}以上详解:


例如:结构体sigaction中的参数——sigset_t sa_mask,这个sa_mask为信号搁置集。
什么是信号屏蔽字?
如何修改进程的信号屏蔽字?
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myhandle(int sig) {
printf("Catch a signal : %d\n", sig);
printf("Catch end.%d\n", sig);
}
int main(void) {
struct sigaction act, act2;
act.sa_handler = myhandle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
sigset_t proc_sig_msk;//当前的信号屏蔽字
sigset_t old_mask;//旧的信号屏蔽字
sigemptyset(&proc_sig_msk);//清空信号集
sigaddset(&proc_sig_msk, SIGINT);//添加信号到信号屏蔽集中
sigprocmask(SIG_BLOCK, &proc_sig_msk, &old_mask);//修改进程的信号屏蔽字,以增加的方式。
sleep(5);
printf("had delete SIGINT from process sig mask\n");
sigprocmask(SIG_UNBLOCK, &proc_sig_msk, &old_mask);//删除proc_sig_msk中的信号
while (1) {
}
return 0;
}int sigpending(sigset_t *set);#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myhandle(int sig) {
printf("Catch a signal : %d\n", sig);
printf("Catch end.%d\n", sig);
}
int main(void) {
struct sigaction act, act2;
act.sa_handler = myhandle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR1, &act, 0);//给当前进程安装SIGUSR1信号
sigset_t proc_sig_msk;//当前的信号屏蔽字
sigset_t old_mask;//旧的信号屏蔽字
sigemptyset(&proc_sig_msk);//清空信号集
sigaddset(&proc_sig_msk, SIGUSR2);//添加信号SIGUSR2到信号屏蔽集中
sigprocmask(SIG_BLOCK, &proc_sig_msk, &old_mask);//修改进程的信号屏蔽字,以增加的方式。
sigset_t tmp_sig_msk;//临时的信号屏蔽字——用于sigsuspend函数
sigaddset(&tmp_sig_msk,SIGUSR1);
while (1) {
printf("waitting...\n");
sigsuspend(&tmp_sig_msk);//此时,当前进程无法响应SIGUSR1中
printf("running...\n");
}
return 0;
}