【Linux】信号的基础知识&mysleep的实现

1、基本信号
1.1产生信号的条件:  
1)键盘
sigint--ctrl+c<终止,前台程序>   
sigouit--ctrl+\<调试>    
sigstp--ctrl+z<block>
2)硬件异常(SIGSEGV)
3)kill指令
 kill函数给一个指定的进程发送指定的信号。 int kill(pid_t pid, int signo); 
 raise函数给当前进程发送指定的信号(自己给自己发信号)。  int raise(int signo);
 abort函数使当前进程接收到SIGABRT信号而异常终止.void abort(void);abort函数(同exit)总是会成功,所以没有返回值。
4)函数。
1.2处理信号(动作)的方式:
1)忽略
2)执行默认动作
3)捕捉信号(信号处理函数)。
1.3收到信号的处理动作:
1)不立即处理先保存
2)终止进程处理信号
3)信号捕捉。
1.4信号的处理流程:

2、阻塞信号
实际执行信号的处理动作称为信号递达(Delivery)。
信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
pending未决
  0-未产生,1-产生
<pending信号集> 未决
block
  0-未阻塞,1-阻塞
<阻塞信号集,信号屏蔽字 > 
handler
SIG_DFL(default),,,SIG_IGN(ignor),,,
UserSpace(用户自定义)
<>  
3、signal相关函数的练习
sigemptyset--初始化信号集sig,并清空。sigaddset--添加有效信号
sigprocmask--信号屏蔽字。设置阻塞信号集,阻塞SIGINT(ctrl+c)信号
sigpending--获取未决信号集。
sigismember--判断指定信号是否在目标集合中。1-在,0-不在。
ctrl+c被阻塞处于未决状态,不能终止程序;ctrl+z和ctrl+\ 未被阻塞,可以终止程序。

4、myslepp的实现
4.1基础知识点:
1)SIGALRM :
   时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
2)unsigned int alarm(unsigned int seconds);
   alarm也称为闹钟函数。它可以在进程中设置一个定时器,当定时器指定的时间到时,它向进程发送SIGALRM信号。如果忽略或者不捕获此信号,则其默认动作是终止调用该alarm函数的进程。
3)int pause(void);
   pause函数使调用进程挂起直到有信号递达。pause只有出错的返回值。errno设置为EINTR表示“被信号中断”
   如果信号的处理动作是终止进程,则进程终止, pause函数没有机会返回;
   如果信号的处理动作是忽略,则进程继续处于挂起状态, pause不返回;
   如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回- 1; 
4)sigaction:
sigaction函数:成功返回0,失败返回-1
sigaction的结构体:

sa_handler:赋值为常数SIG_IGN传给sigaction表示忽略信号;赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号。 向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
sa_mask:进程的信号屏蔽字。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
sa_flags:sa_flags字段包含一些选项,本文代码把sa_flags设为0。
sa_sigaction是实时信号的处理函数。

4.2程序代码及运行结果:

4.3程序详解:
1、main函数调用my_sleep函数,后者调用sigaction注册了SIGALRM信号的处理函数handler。
2、调用alarm(times)设定闹钟。
3、调用pause等待,内核切换到别的进程运行。
4、times秒之后,闹钟超时,内核发SIGALRM给这个进程。
5、从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是handler。
6、切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程。
7. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。
4.4思考:
程序虽然按照要求执行,但SIGALRM信号处理完成,程序为什么没有结束?
出现这个问题的根本原因:系统运行的时序并不像我们写程序时所设想的那样。
虽然alarm(times)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用alarm(times)之后的secs秒之内被调用。
由于异步事件在任何时候都有可能发生,如果写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件。
4.5程序改善:
sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题。
#include <signal.h>    int sigsuspend(const sigset_t *sigmask);
在对时序要求严格的场合下都应该调用sigsuspend而不是pause。
如果在调用my_sleep函数时SIGALRM信号没有屏蔽:
1)调用sigprocmask(SIG_BLOCK,&newmask, &oldmask)时,屏蔽SIGALRM。
2)调用sigsuspend(&suspmask)时,解除对SIGALRM的屏蔽,然后挂起等待。
3)SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。
4)调用sigprocmask(SIG_SETMASK, &oldmask, NULL)时,再次解除对SIGALRM的屏蔽。
代码修改如下:


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值