操作系统信号处理

操作系统信号处理

一、基本概念

1、中断

概念:中止当前正在执行的任务,转而执行其他的任务,这叫做中断。
分类:硬件中断:来自硬件的中断 ;软件中断:来自软件的中断


2、信号是一种软件的中断

信号是一种软件中断,它为进程异步执行任务提供了一种机制


3、常见的信号

 SIGINT(2)  终端中断符信号 CTRL+C
 SIGQUIT(3) 终端退出符信号 CTRL+\ 
 SIGFPE(8)  算术异常信号 程序执行了除0操作
 SIGSEGV(11) 段错误信号  非法使用内存
 SIGSTOP(19) 暂停信号 不能被捕获、忽略,收到SIGCONT信号才能继续
 SIGCONT(18) 使停止的进程继续  

4、不可靠信号

在终端下输入kill -l可以显示所有的信号。

 1) SIGHUP	    2) SIGINT	   3) SIGQUIT	 4) SIGILL	  5) SIGTRAP
 6) SIGABRT	    7) SIGBUS	   8) SIGFPE	 9) SIGKILL	 10) SIGUSR1
11) SIGSEGV	    12) SIGUSR2	  13) SIGPIPE	14) SIGALRM	 15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	  18) SIGCONT	19) SIGSTOP	 20) SIGTSTP
21) SIGTTIN	    22) SIGTTOU	  23) SIGURG	24) SIGXCPU	 25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	  28) SIGWINCH	29) SIGIO	 30) SIGPWR
31) SIGSYS	

1-31 是不可靠信号,是建立在早期的机制上的,不支持排队。同一个信号产生多次但只处理一次(单位时间内处理一次),但是这种机制可能会导致信号丢失。


5、可靠信号(两种信号相互独立,互不干扰)

	34) SIGRTMIN	  35) SIGRTMIN+1	 36) SIGRTMIN+2 	37) SIGRTMIN+3     38) SIGRTMIN+4	  
	39) SIGRTMIN+5	  40) SIGRTMIN+6	 41) SIGRTMIN+7	 	42) SIGRTMIN+8 	   43) SIGRTMIN+9	 
	44) SIGRTMIN+10	  45) SIGRTMIN+11	 46) SIGRTMIN+12	47) SIGRTMIN+13    48) SIGRTMIN+14	
	49) SIGRTMIN+15	  50) SIGRTMAX-14	 51) SIGRTMAX-13	52) SIGRTMAX-12    53) SIGRTMAX-11
	54) SIGRTMAX-10	  55) SIGRTMAX-9	 56) SIGRTMAX-8		57) SIGRTMAX-7     58) SIGRTMAX-6
	59) SIGRTMAX-5	  60) SIGRTMAX-4	 61) SIGRTMAX-3		62) SIGRTMAX-2     63) SIGRTMAX-1	  
	64) SIGRTMAX	

34-64 是可靠信号,是一种新机制的信号,支持排队,不会丢失。


6、信号的处理方式

1、忽略
2、终止(直接死掉),信号(9)
3、终止+core(core文件里记录内存的使用情况)
4、捕获并处理,当信号发生是操作系统会调用一个预先注册好的的处理函数。

        ps:段错误的处理的调试技巧
		①执行ulimit -c unlimited ,是系统能够生成core文件
		②重新编译:gcc -g code.c 加入调试信息
		③重新执行程序,生成带调试信息的core文件
		④使用gdb a.out core进入调试
		⑤where显示在什么位置什么原因产生的错误

二、信号的捕获

函数: sighandler_t()
typedef void (*sighandler_t)(int);//信号处理函数的格式

sighandler_t signal(int signum, sighandler_t handler);
功能:向内核注册一个信号处理函数
signum:要捕获并处理的信号
handler:当信号发生时要执行的函数
注意:这个步骤一般放在程序的开始部分(项目的初始化阶段) 代码产生(除0、非法使用内存)的信号,这种通信能被捕获,但是当信号处理完后又会回到产生错误的位置,如果这个错误没有被消除,又会产生信号,会形成死循环。


三、发送信号

1、键盘发送信号

Ctrl+c SIGINT
Ctrl+\ SIGQUIT
Ctrl+z SIGSTOP

2、错误发送信号

除0操作 	SIGFPE
非法访问内存 SIGSEGV
硬件故障 SIGBUS 

3、命令发送信号
kill -sig 进程号 getpid
ps -aux
killall -sig(信号编号) a.out(程序名)
4、函数发送信号

int kill(pid_t pid ,int sig)

功能: 向指定的进程发送信号
pid:
pid > 0 向指定的进程发送信号。
pid = 0 向同组的进程发送信号。
pid = -1 向所有有权限发送信号的进程发送信号。
普通用户只能对自己的进程发送信号,而root可以向所有的进程发送信号。
pid < -1 向进程号等于pid绝对值的进程发送信号。
sig: 信号的编号
如果sig的值不存在,不会发送,但也不会返回错误,返回0;
当发送的进程不存在时,才会返回错误(-1),这个功能可以用来检查这个进程是否存在。


int raise(int sig);

功能: 向当前进程发送信号(自己杀死自己)
返回值: 成功返回0;失败返回-1(内核出问题)

int pause(void);

功能: 使进程进入休眠状态,直到有信号来来临(信号处理函数执行完毕)才醒来。
返回值: 函数结束返回-1,否则不结束进程一直休眠。


按s休眠函数

unsigned int sleep(unsigned int seconds);

功能: 让调用者进入休眠,当信号来临时休眠会被中止提前返回(信号处理函数执行完毕后)
seconds: 要休眠的秒数
返回值: 当睡够足够的秒数返回0,如果提前结束休眠则会返回剩余的秒数。


微妙休眠函数

int usleep(useconds_t usec);

功能: 以微妙的方式进行休眠,10^-6 s
返回值: 提前结束返回-1,正常结束返回0;


闹钟函数

unsigned int alarm(unsigned int seconds);

功能: 在多少s后产生闹钟信号
返回值: 返回0成功设置,如果之前已经设置过还没到时间,则返回剩余的时间。

四、信号集与信号屏蔽

1、信号集:多个信号的集合,sigset_t(结构体类型)是一个128位的二进制整数,每一位代表一个信号。
2、信号集相关的函数:

int sigemptyset(sigset_t *set);

功能: 将信号集设置为空,所有二进制为设置为0


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,错误信号返回-1;


3、信号屏蔽
递送概念:当信号产生时,系统内核会在其维护的进程表中,为特定的进程设置一个与该信号相对应的标志位,这个过程叫递送
未决概念:信号从产生到完成递送之间存在一段时间的间隔,这段时间的信号叫未决
每个进程都有一信号掩码,他实际上是一个信号集,其中包含了所要屏蔽的信号
当处理一些敏感操作是不希望被中断(更新数据库、加载文件、保存文件),可以使用sigprocmask暂时屏蔽一些信号(不是忽略),让这些信号处于未决状态,当这些任务处理完成后,在使用sigpending获取这段时间发生的信号。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

功能: 设置信号屏蔽掩码
how:
SIG_BLOCK :屏蔽掩码 = 旧 并 新
SIG_UNBLOCK :屏蔽掩码 = 旧 交 新
SIG_SETMASK :屏蔽掩码 = 新
oldset:返回旧的屏蔽掩码

   	 //添加新的掩码
	sigprocmask(SIG_SETMASK,&set,NULL)
	//获取旧的掩码
	sigprocmask(SIG_SETMASK,NULL,&set)

int sigpending(sigset_t *set);
功能: 获取未决状态的信号集


五、带附加值的参数的捕获和发送

 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

功能: 设置信号处理方式
act: 新设置信号处理方式
oldact: 获取旧的信号处理方式,为NULL就不获取

 	struct sigaction 结构体成员
 	{
    	void     (*sa_handler)(int); 						//信号处理函数1
    	void     (*sa_sigaction)(int, siginfo_t *, void *);	//信号处理函数2
        sigset_t   sa_mask;									//信号处理过程中的屏蔽掩码
        int        sa_flags;								//标志位
        		  SA_NOCLDSTOP  如果signum值是SIGCHLD,当子进程暂停时不通知父进程
        		  SA_NODEFER 不屏蔽正在处理的信号
				  SA_RESETHAND/ SA_ONESHOT:当信号处理一次后,恢复默认处理方式
				  SA_RESTART 如果信号中断了系统调用,当信号处理完后,重启系统调用
				  SA_SIGINFO 使用信号处理函数2
        void     (*sa_restorer)(void);						//预留位置,暂时没有使用
        
       };

int sigqueue(pid_t pid, int sig, const union sigval value);

功能: 发送带参数的信号
pid: 要发的进程
sig: 要发的信号
value: 附加的数据,指针或整数

六、计时器

计时器是用来记录进程的运行时间,系统会为每个进程记录三个计时器:
真是计时器:程序实际运行的时间
虚拟计时器:程序在用户态所消耗的时间
实用计时器:程序在内核态和用户态所消耗的时间之和
程序实际运行时间=用户时间+内核时间+睡眠时间
使用计时器来设置定时操作:
先设置一个时间a,表示时钟信号在a时间后发生,再设置一个时间b,表示以后每隔b时间再来一次。

int getitimer(int which, struct itimerval *curr_value);

功能: 设置当前的时钟
which: 使用哪个计时器 I
TIMER_REAL /SIGALRM 真实计时器
ITIMER_VIRTUAL/ SIGVTALRM 虚拟计时器
ITIMER_PROF /SIGPROF 实用计时器

     itimerval:结构体             
    	struct itimerval {
           	struct timeval it_interval; /* next value */ 	信号之间的间隔
           	struct timeval it_value;    /* current value */ 首次发生的时间
       		};
       		
    	struct timeval {
           long tv_sec;                /* seconds */ 		秒数
           long tv_usec;               /* microseconds */	微妙(算总和)
       	   };

int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);

功能: 设置新的时钟
which: 选择计时器和信号类型

猜你喜欢

转载自blog.csdn.net/weixin_38663899/article/details/86136682
今日推荐