Linux信号产生与处理机制学习笔记(一)
来源:互联网 发布:点播软件哪家好 编辑:程序博客网 时间:2024/05/01 07:21
一、信号基本概念:
中断:就是终止当前代码转而执行其他代码,中断有软件中断与硬件中断。
信号的本质:是一系列非负整数(操作系统就是这么爱用整数)。UNIX常用信号1–48,Linux常用信号1–64。每个信号都有一个宏名称(宏变量),宏变量都是以SIG(signal)开头。信号的产生时间是无规律的,不知道什么时候回来,因此对于信号的处理采取异步处理方式(函数指针信号中断)。信号是不连续的;有些信号是不存在的;不同的操作系统,对应的信号的值时不同的(宏名是一样的),故在开发时应使用宏而不是使用数字以更好的兼容不同版本LINUX/UNIX。
eg:(LINUX)SIGINT是信号2,Ctrl+c产生的信号就是SIGINT,交由操作系统处理。
kill -l:查看信号
前31个(1-31)为UNIX经典信号,后31个(34-64)为硬件驱动开发的实时信号man 7 signal查看信号章节对各信号的介绍eg:(对应上图)Ctrl + c:2 SIGINT 默认终止进程Ctrl + z:20 SIGTSTP 停止(fg + id继续运行,dg + id后台运行)Ctrl + \:3 SIGQUIT 默认终止进程段错误:11 SIGSEGV 非法操作内存总线错误:7 SIGBUS 非法操作文件映射浮点数例外:8 SIGFPE CPU不能除0(致命错误)管道信号:13 SIFPIPE 向一个没有读端的管道写操作
二、用kill函数实现kill命令:
1、kill()函数介绍:
kill函数:int kill(pid_t pid, int signal);pid > 0:指定pid进程pid == 0:与发送信号同组的进程pid < 0:发送给指定组gid = |pid|pid == -1:向所有权限发送信号的进程发送信号
还用一种用法:(kill -0 pid测试)指定pid,并发送0,用来测试当前用户是否拥有给某个进程发送信号的权限,如图(普通用户不具备给init进程发送信号的权力,称作为:不允许的操作):
2、kill命令实现:
用kill函数实现简单的kill命令:#include<stdio.h>#include<stdlib.h>#include<signal.h>#include<sys/types.h>int main(int argc, char *argv[]){ if(argc < 3){ printf("./mykill signal pid"); exit(EXIT_FAILURE); } if(kill((pid_t)atoi(arv[2]), atoi(argv[1])) < 0){ perror("kill"); exit(EXIT_FAILURE); } return 0; }/*Ctrl + z:进程暂停放到后台,或者启动时直接加&便启动到后台运行fg num:从后台启动到前台运行bg num:启动到后台运行*/
3、测试结果如图:
我们向一个死循环的后台进程发送SIGSEGV(11)信号,并且fg重新调到前台时发现报了个“段错误(核心已转储)”,其实是因为我们发送的是SIGSEGV信号而已,并非是因为段错误。
三、信号发送与处理方式:
1、信号发送方式:
我们可以在键盘上Ctrl+c、Ctrl+\来发送一些信号,在程序出错(段错误、总线错误、浮点数例外)时也会发送一些信号,另外我们常用的发送命令的方式除了键盘还有使用kill命令/kill()函数发送,当然发送信号除了以上一些方式,还有一些函数也可以实现:
int raise(int sig);函数:只能向自己发送信号void abort(void);函数:向自己发送指定信号SIGABRT(6)alarm()函数:可以发送信号,但不是为了发送信号而诞生的函数。
#include<unistd.h>unsigned int alarm(unsigned int seconds);/*只能给当前进程(调用alarm()函数的进程)发送一个SIGALRM信号的函数。*/
alarm(n); n秒之后会发送一个闹钟信号SIGALRM。闹钟函数alarm()不阻塞,SIGALRM默认处理方式是打印“闹钟”并结束进程。如果多次调用alarm(),新的闹钟会替代原有闹钟。当参数为0,表示闹钟取消。
eg:
alarm(10); //从现在起10秒后发送一个闹钟信号,默认打印“闹钟”
alarm(2); //从现在起2秒钟后发送一个信号,取消原有10中后发送
alarm(0); //取消原有闹钟
//所以最后不会打印“闹钟”两字返回值:如果以前没有设置闹钟,或者以前设置闹钟已经结束,那么现在(新的)alarm()函数返回0,;如果以前设置alarm()没有结束,那么新的alarm()函数返回之前alarm()到发送信号剩余的时间(类似于sleep返回的剩余时间)。
2、信号处理的方式:
SIG_IGN:忽略
SIG_DEL:默认
a signal handling function:捕捉,自定义处理函数
(1)、默认处理方式:
默认处理五种方式:
①Term : Default action is to terminate the process.
默认动作是终止进程
②Ign : Default action is to ignore the signal.
默认动作是忽略信号(与SIG_IGN不是一个层级的)
③Core : Default action is to terminate the process and dump core (seecore(5)).
默认动作是终止这个进程并产生一个core文件(Core Dump用于GDB调试)
④Stop : Default action is to stop the process.
默认动作是暂停进程
⑤Cont : Default action is to continue the process if it is currently stopped.
默认动作是继续进程(如果当前这个进程被暂停)
(2)、自定义捕捉信号:
signal、sigaction函数处理(先说signal):
#incldue<signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum,sighandler_t handler);
参数:
参数与返回值都是自定义函数指针(函数名作为指针),signum是具体的信号值。handler(处理方式)除了可以设为自定义函数指针,还可以设定为SIG_IGN(忽略信号)、SIG_DFL(默认处理方式)。返回值:
成功返回信号处理的前值(return previous value of the signal handler),错误失败返回宏SIG_ERR。
3、alarm()与signal()测试:
alarm()函数在5秒后发送一个SIGALRM(14)信号,而死循环中注册了一个SIGALRM信号,并以自定义处理方式注册。循环中每打印一次alarm n就睡眠一秒,所以5秒钟打印了5次,之后就被信号中断执行自定义信号处理函数,并且直接退出,所以返回值为exit()退出码,而不是main函数中return值:
四、信号集与信号集操作函数:
1、PCB中维护的信号集:
PCB中为信号维护了两个信号集:PEND未决信号集与Block阻塞信号集。未决信号集初始值每一位均为0,如果产生一个信号则对应bit位置为1,产生的信号如果阻塞信号集对应位置为1,则阻塞。如果不再阻塞进行下一步处理(忽略、默认、自定义)时,处理完毕则未决信号对应bit位会被重新置为0。未决信号集对应位为1时,信号状态为未决态(信号产生,没有被响应);递达态:信号产生并且被相应(不阻塞)。未决信号集由内核自动设置,用户可读,而阻塞信号集用户可以自己设置一屏蔽某些信号。信号SIGKILL和SIGSTOP不能被忽略/阻塞。并且前三十一个信号不支持排队机制,后三十二个支持排队机制。
关于PEND未决信号集与Block阻塞信号集 的图解如图所示:
2、信号集处理函数:
sigset_t为信号集类型,sizeof(sifset_t)=128,每个信号站一个bit位,剩余的为预留位置,程序员可以操作的信号集为阻塞信号集,又叫做信号屏蔽字)。
信号屏蔽:在执行某些核心代码,我们不希望被信号意外中断,可采用屏蔽信号的方法(信号到了但是延时处理),防止写如等操作被意外终止。解除信号屏蔽以后再处理来过的被屏蔽的信号。
信号集的功能函数:
①清空、删除所有信号(全置为0):
int sigemptyset(sigset_t * set);
②将所有信号全加入(全置为1):
int sigfillset(sigset_t * set);
③增加1个信号:
int sigaddset(sigset_t * set,int signum);
④删除一个信号:
int sigdelset(sigset_t * set,int signum);
⑤查找元素(判断是否是现有成员):
int sigismember(const sigset_t * set,int signum);
有返回1、无返回0设置信号屏蔽字(屏蔽信号集):
int sigprocmask(int how,sigset_t * new,sigset_t * old);
参数:
how是屏蔽的方式,有三种:
SIG_BLOCK:相当于或运算,在原有的基础上加上新的屏蔽信号
SIG_UNBLOCK:相当于与运算,在原有的基础上去除屏蔽的信号
SIG_SETMASK:直接重新赋值,与原有的无关
new:新的设置的信号屏蔽字,old保存之前的信号屏蔽字,如果old为NULL就是不保存原有的信号屏蔽字。当保存了原有信号屏蔽字到old,核心代码执行完毕重新设置为old信号屏蔽字时,就是取消屏蔽的过程。获取未决信号集:
程序员虽然不能操作未决信号集,但是能够进行未决信号集的读取:
int sigpending(sigset_t * set);
3、信号屏蔽测试:
/*sigprocmask.c*/#include<stdio.h>#include<signal.h>#include<sys/types.h>void print_sigset(const sigset_t * sig_get){ int i; for(i=1; i<32; i++){ if(sigismember(sig_get, i) == 1) putchar('1'); else putchar('0'); } printf("\n");}int main(void){ sigset_t set, get; printf("sizeof(sigset_t) = %d\n",sizeof(sigset_t)); sigemptyset(&set);//屏蔽字清空,即全部置为0 sigaddset(&set, SIGINT);//可以被阻塞 sigaddset(&set, SIGQUIT);//可以被阻塞 sigaddset(&set, SIGKILL);//不可以被阻塞,设置无效 sigprocmask(SIG_BLOCK, &set, NULL); while(1){ sigpending(&get); print_sigset(&get); sleep(1); } return 0;}
测试结果:
注意:一般,我们不对操作系统赋予了实际意义的信号进行自定义操作,而SIGUSR1与SIGUSR2这两个信号可用于用户自定义操作,因为这两个信号并没有实际意义,由程序员赋予他们意义,他们一般用于父子进程间通信,这点以后会提到。
五、可靠信号与不可靠信号:
信号分类:
1~64这62个信号分为两类:可靠信号与不可靠信号
①1–31都是不可靠信号,这种信号不支持排队,有可能丢失,是非实时信号
②34–64都是可靠信号,这种信号不支持排队,不会丢失,是实时信号
关于信号会丢失这点,我们可以做个测试:
/* *模拟一个需要屏蔽某种信号的环境 *运行代码 *重新打开一个终端 *kill -sig pid给该进程发送信号测试 * */#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>void accept_signal(int sig){ printf("接收到信号%d\n",sig);}int main(void){ /*2,3为不可靠信号,会丢失一部分*/ signal(SIGINT,accept_signal); signal(SIGQUIT,accept_signal); /*50为可靠信号,自行排队等待*/ signal(50,accept_signal); printf("pid = %d\n",getpid()); printf("执行普通代码,不屏蔽信号,但是未忽略的信号会中断sleep\n"); int ret = sleep(30); printf("ret = %d\n",ret); sigset_t set_new, set_old; sigaddset(&set_new,SIGINT); sigaddset(&set_new,SIGQUIT); sigaddset(&set_new,50); sigprocmask(SIG_SETMASK, &set_new, &set_old);/*设置屏蔽信号集*/ printf("执行关键代码,屏蔽信号,sleep不会被屏蔽的信号中断\n"); ret = sleep(30); printf("ret = %d\n",ret); printf("关键代码执行完毕,解除屏蔽\n"); sigprocmask(SIG_SETMASK, &set_old, NULL);/*解除屏蔽*/ return 0;}
存放来过的阻塞信号的数据结构,类似于一个栈(当然这个数据结构不是栈,因为如果取消屏蔽的是2而不是3和50,那么2号信号也出不来),对于不可靠信号产生,如果还未处理(屏蔽阻塞状态),之后再产生的同类信号则会丢失;而可靠信号将所有未处理的信号都保存,当解除屏蔽时再取出来。结果如下:
- Linux信号产生与处理机制学习笔记(一)
- Linux信号产生与处理机制学习笔记(二)
- linux 信号signal处理机制(一)
- linux信号signal处理机制(一)
- Linux信号机制与信号处理
- Linux信号机制与信号处理
- Linux信号机制与信号处理
- Linux程序设计学习笔记——异步信号处理机制
- 信号处理学习笔记一
- Linux信号机制学习笔记-----Linux信号机制的疑问?????
- (转)Linux网络编程(3):信号处理与定时机制简要学习
- Linux网络编程(3):信号处理与定时机制简要学习
- Linux下的信号(一)----信号的基本概念与产生
- Linux信号处理机制
- linux信号处理机制
- Linux 信号处理机制
- linux 信号处理机制
- linux信号处理机制
- 第一行代码笔记 RecycleView使用
- jsp有哪些动作
- windows下的selenium + python 环境搭建
- hdu2065(指数型母函数+枚举找规律)
- Redis
- Linux信号产生与处理机制学习笔记(一)
- 《一》平方根计算
- 输入参数是字符串类型。返回值类型是字符串类型。整数位每三位加入逗号分隔符
- GeoHash距离大致估计
- 华为OJ:名字的漂亮度
- 【BOI2007】逃跑问题 (BSOI2344)
- 第一部分:嵌入式操作系统
- ppp 完全理解(二)
- Java Web基础 --- Jsp 综述(上)