【Linux】Linux信号基础
Linux信号基础1.linux中定义了哪些信号2.linux中提供跟信号有关的接口函数2.1 发送信号2.2 捕捉信号并改变信号的响应动作2.2.1 signal的三种用法2.2.1.1 改变信号的响应2.2.1.2 忽略信号2.2.1.3 恢复默认动作2.3 另外一组信号发送和接收2.3.1 发送示例代码2.3.2 捕捉信号示例代码示例代码2.4 阻塞进程等待信号的到来示例代码2.5 阻塞信号(屏蔽信号)2.5.1 相关函数示例代码示例代码2.6 raise和alarm示例代码p1进程把自己的id号通过有名管道发送给p2进程p2进程使用kill函数发送信号控制p1暂停继续退出1.linux中定义了哪些信号kill-l查看当前系统中定义的所有信号1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL5)SIGTRAP6)SIGABRT7)SIGBUS8)SIGFPE9)SIGKILL10)SIGUSR111)SIGSEGV12)SIGUSR213)SIGPIPE14)SIGALRM15)SIGTERM16)SIGSTKFLT17)SIGCHLD18)SIGCONT19)SIGSTOP20)SIGTSTP21)SIGTTIN22)SIGTTOU23)SIGURG24)SIGXCPU25)SIGXFSZ26)SIGVTALRM27)SIGPROF28)SIGWINCH29)SIGIO30)SIGPWR31)SIGSYS34)SIGRTMIN35)SIGRTMIN136)SIGRTMIN237)SIGRTMIN338)SIGRTMIN439)SIGRTMIN540)SIGRTMIN641)SIGRTMIN742)SIGRTMIN843)SIGRTMIN944)SIGRTMIN1045)SIGRTMIN1146)SIGRTMIN1247)SIGRTMIN1348)SIGRTMIN1449)SIGRTMIN1550)SIGRTMAX-1451)SIGRTMAX-1352)SIGRTMAX-1253)SIGRTMAX-1154)SIGRTMAX-1055)SIGRTMAX-956)SIGRTMAX-857)SIGRTMAX-758)SIGRTMAX-659)SIGRTMAX-560)SIGRTMAX-461)SIGRTMAX-362)SIGRTMAX-263)SIGRTMAX-164)SIGRTMAX19)SIGSTOP --》让进程暂停执行18)SIGCONT --》让进程继续执行2)SIGINT --》键盘输入ctrlc默认就是给当前进程发送了SIGINT信号9)SIGKILL --》把进程杀死2.linux中提供跟信号有关的接口函数注意有些版本的ubuntugetpid打印的ID号跟实际ID号小了65536写代码的时候需要加上655362.1 发送信号#includesignal.hintkill(pid_tpid,intsig);参数pid--》你要发送信号的那个进程的id号 sig--》你要发送哪个信号(信号的名字或者序号都行)2.2 捕捉信号并改变信号的响应动作比如正常情况下进程收到INT信号应该终止进程但是我们需要改变这种动作#includesignal.hvoid(*signal(intsig,void(*func)(int)))(int);返回值也是一个函数指针void(*)(int)参数sig--》要捕捉的信号void(*func)(int)--》捕捉到该信号以后需要改变的动作int的参数表示捕捉到信号的序号2.2.1 signal的三种用法2.2.1.1 改变信号的响应signal(信号,函数指针)#includesignal.h#includestdio.h#includeunistd.h// 改变信号的响应voidfun(intsigno){printf(收到信号%d\n,signo);}intmain(intargc,charconst*argv[]){signal(SIGINT,fun);while(1){printf(当前进程正在运行...,pid: %d\n,getpid());sleep(1);}return0;}2.2.1.2 忽略信号当外界给进程发送这个信号的时候进程选择忽略signal(信号,SIG_IGN)#includesignal.h#includestdio.h#includeunistd.h// 忽略SIGINT信号使程序无法通过CtrlC终止// signal(信号,SIG_IGN)intmain(intargc,charconst*argv[]){signal(SIGINT,SIG_IGN);while(1){printf(当前进程正在运行...,pid: %d\n,getpid());sleep(1);}return0;}2.2.1.3 恢复默认动作signal(信号,SIG_DFL)#includesignal.h#includestdio.h#includeunistd.h// 恢复默认动作intmain(intargc,charconst*argv[]){inti;//最开始选择忽略INT信号//默认情况SIGINT收到之后会导致当前进程终止signal(SIGINT,SIG_IGN);for(i0;i20;i){printf(忽略INT当前进程正在运行%d\n,getpid());sleep(1);}//恢复INT信号的默认动作signal(SIGINT,SIG_DFL);for(i0;i20;i){printf(恢复默认动作之后当前进程正在运行%d\n,getpid());sleep(1);}return0;}2.3 另外一组信号发送和接收2.3.1 发送intsigqueue(pid_tpid,intsig,constunionsigval value);//kill的升级版发送信号的时候可以携带额外数据参数前面两个跟kill一样unionsigval value--》保存你需要发送的额外数据unionsigval{intsival_int;//你需要发送的整型数据void*sival_ptr;//你需要发送的地址};示例代码#includestdio.h#includestdlib.h#includesignal.hintmain(intargc,char**argv){if(argc3){fprintf(stderr,用法: %s pid sig\n,argv[0]);return-1;}unionsigval val;val.sival_int666;if(sigqueue(atoi(argv[1]),atoi(argv[2]),val)-1){perror(sigqueue失败);return-1;}return0;}2.3.2 捕捉信号intsigaction(intsig,conststructsigaction*act,structsigaction*oact)//signal的升级版参数structsigaction{void(*)(int)sa_handler//信号的响应函数sigset_tsa_mask//intsa_flags//标志位sa_flags设置为SA_SIGINFO表示我想使用sa_sigaction作为信号的响应函数 sa_flags设置为0表示我想使用sa_handler作为信号的响应函数void(*)(int,siginfo_t*,void*)sa_sigaction//信号的响应函数参数int--》保存捕捉到信号的序号siginfo_t--》保存接收到额外数据{intsi_int;//保存发送过来的整数void*si_ptr;//保存发送过来的指针}}oact--》做备份的保存当前进程原本设置的信号响应信息示例代码#includestdio.h#includestring.h#includeunistd.h#includesignal.h/* sigaction可以实现跟signal一模一样的功能 */voidfun(intsig){printf(我捕捉的信号 %d\n,sig);}intmain(intargc,char**argv){structsigactionmyaction;bzero(myaction,sizeof(myaction));//myaction.sa_handlerfun; 改变INT的响应动作myaction.sa_handlerSIG_IGN;//忽略INT信号//myaction.sa_handlerSIG_DFL; //恢复默认动作myaction.sa_flags0;//开关;//捕捉INT信号sigaction(SIGINT,myaction,NULL);while(1){printf(进程%d在执行!\n,getpid());sleep(1);}return0;}示例代码#includestdio.h#includesignal.h#includestrings.h#includeunistd.h/* sigaction使用另外一个响应函数 */voidfun(intsig,siginfo_t*info,void*arg){printf(我捕捉的信号 %d 携带的额外数据是: %d\n,sig,info-si_int);}intmain(intargc,char**argv){structsigactionmyaction;bzero(myaction,sizeof(myaction));myaction.sa_sigactionfun;//改变INT的响应动作myaction.sa_flagsSA_SIGINFO;//开关;//捕捉INT信号sigaction(SIGINT,myaction,NULL);while(1){printf(进程%d在执行!\n,getpid());sleep(1);}return0;}/* 测试 用sigqueue编译出的代码执行如./sig6 12345 2 12345执行sigaction的进程 22号信号 会输出 我捕捉的信号 2 携带的额外数据是: 666 */2.4 阻塞进程等待信号的到来intpause(void);示例代码#includestdio.h#includeunistd.h#includesys/types.h/* pause可以阻塞进程让外界有充足的时间可以给当前进程发送信号 */intmain(){printf(当前进程正在运行,你能把我怎么样! ID %d\n,getpid());pause();return0;}2.5 阻塞信号(屏蔽信号)2.5.1 相关函数sigset_t类型的变量(本质上是个数组)--》linux中专门用来保存阻塞信号称之为信号阻塞掩码集intsigemptyset(sigset_t*set);//清空集合intsigfillset(sigset_t*set);//把linux中62个信号一起添加到set集合中intsigaddset(sigset_t*set,intsignum);//把signum信号添加到set集合中intsigdelset(sigset_t*set,intsignum);//把signum信号从集合中剔除intsigismember(constsigset_t*set,intsignum);//判断signum在不在set集合中在集合中返回1不在0intsigprocmask(inthow,constsigset_t*set,sigset_t*oset);参数how--》SIG_BLOCK//设置信号阻塞把set和oset两个集合中的所有信号都阻塞SIG_SETMASK//设置信号阻塞只阻塞set这个集合中的所有信号SIG_UNBLOCK//解除信号阻塞set--》保存你想要阻塞的所有信号 oset--》备份保存之前你设置的阻塞信号示例代码#includestdio.h#includeunistd.h#includesys/types.h#includesignal.h/* 信号的阻塞/屏蔽 */intmain(){printf(进程ID %d\n,getpid());//第一步定义集合变量存放你要阻塞的所有信号sigset_tmyset;sigemptyset(myset);//清空集合//添加你想阻塞的信号--》SIGINT和SIGQUIT 屏蔽sigaddset(myset,SIGINT);sigaddset(myset,SIGQUIT);//第二步设置阻塞/* SIG_BLOCK //设置信号阻塞把set和oset两个集合中的所有信号都阻塞 SIG_SETMASK //设置信号阻塞只阻塞set这个集合中的所有信号 */sigset_toldset;sigprocmask(SIG_BLOCK,myset,oldset);//sigprocmask(SIG_SETMASK,myset,oldset);//阻塞进程等待信号的到来pause();return0;}示例代码#includestdio.h#includeunistd.h#includesys/types.h#includesignal.h#defineUSE_SIGSET/* 信号的忽略信号发给进程进程收到之后不响应直接丢弃该信号 信号的阻塞/屏蔽暂时把信号挂起(把信号挡住,信号没有被丢弃还存在的),如果你解除阻塞依然可以响应 */intmain(){inti;printf(进程ID %d\n,getpid());#ifdefUSE_SIGSET//第一步定义集合变量存放你要阻塞的所有信号sigset_tmyset;sigemptyset(myset);//清空集合//添加你想阻塞的信号--》SIGINT和SIGQUIT 屏蔽sigaddset(myset,SIGINT);sigaddset(myset,SIGQUIT);//第二步设置阻塞sigprocmask(SIG_BLOCK,myset,NULL);#elsesignal(SIGINT,SIG_IGN);signal(SIGQUIT,SIG_IGN);#endiffor(i0;i20;i){printf(这20秒的时间你给我发送INT、QUIT信号是没有用的!\n);sleep(1);}#ifdefUSE_SIGSET// 解除阻塞sigprocmask(SIG_UNBLOCK,myset,NULL);#elsesignal(SIGINT,SIG_DFL);signal(SIGQUIT,SIG_DFL);#endiffor(i0;i20;i){printf(这20秒的时间刚才的信号已经解除阻塞了!\n);sleep(1);}//阻塞进程等待信号的到来pause();return0;}2.6 raise和alarmintraise(intsig);//自己给自己发送信号 kill(getpid(),信号)unsignedalarm(unsignedseconds);//定时器间隔seconds秒会给当前进程发送SIGALRM信号参数seconds--》秒数示例代码#includestdio.h#includeunistd.h#includesys/types.h#includesignal.h/* alarm函数的使用 */voidfun(intsig){printf(收到了信号 %d\n,sig);}intmain(){signal(SIGALRM,fun);//调用alarm函数alarm(5);//间隔5秒钟alarm会自动给当前进程发送SIGALRMprintf(主函数接着往后执行!\n);pause();return0;}p1进程把自己的id号通过有名管道发送给p2进程p2进程使用kill函数发送信号控制p1暂停继续退出// p1进程#includestdio.h// printf, perror#includeunistd.h// access, write, close, getpid, sleep, F_OK#includesys/types.h// pid_t#includesys/stat.h// mkfifo#includefcntl.h// open, O_RDWR/* p1进程把自己的id号通过有名管道发送给p2进程p2进程使用kill函数发送信号控制p1暂停继续退出 */intmain(){intret;intfd;charbuf[100];//判断有名管道是否已经存在if(access(/home/myfifo,F_OK)!0)//不存在{//创建一个有名管道retmkfifo(/home/myfifo,0777);if(ret-1){perror(新建有名管道失败了!\n);return-1;}}//打开刚才新建的有名管道--》openfdopen(/home/myfifo,O_RDWR);if(fd-1){perror(打开有名管道失败了!\n);return-1;}//使用有名管道把自己的ID发给p2pid_tidgetpid();write(fd,id,sizeof(id));while(1){printf(进程1接着往后执行!\n);sleep(1);}//关闭有名管道close(fd);return0;}// p2进程#includestdio.h// printf, perror#includeunistd.h// access, write, close, getpid, sleep, F_OK#includesys/types.h// pid_t#includesys/stat.h// mkfifo#includefcntl.h// open, O_RDWR/* p1进程把自己的id号通过有名管道发送给p2进程p2进程使用kill函数发送信号控制p1暂停继续退出 */intmain(){intret;intfd;pid_tid;intnum;//判断有名管道是否已经存在if(access(/home/myfifo,F_OK)!0)//不存在{//创建一个有名管道retmkfifo(/home/myfifo,0777);if(ret-1){perror(新建有名管道失败了!\n);return-1;}}//打开刚才新建的有名管道--》openfdopen(/home/myfifo,O_RDWR);if(fd-1){perror(打开有名管道失败了!\n);return-1;}//从有名管道中读取p1的IDread(fd,id,sizeof(id));//发送信号来控制进程1while(1){printf(请输入信号的序号!\n);scanf(%d,num);//发送信号kill(id,num);}//关闭有名管道close(fd);return0;}