mysleep()的多个版本实现及竟态条件的认知

这里写图片描述
以上为信号的捕捉,捕捉到信号后,在合理的时机递达信号,这个合理的时机就是从内核模式切换到用户模式前,先检测有无信号,有便处理,分三种处理动作:(1)忽略,修改pending对应信号的值后返回用户模式。(2)默认,一般来说,都是终止进程,无需返回。(3)自定义。调回用户模式,执行处理函数。如图中所示。
具体的步骤如下:
1. ⽤用户程序注册了SIGQUIT信号的处理函数sighandler。 2. 当前正在执⾏行main函数,这时发⽣生中断或异常切换到内核态。
3. 在中断处理完毕后要返回⽤用户态的main函数之前检查到有信号SIGQUIT递达。
4. 内核决定返回⽤用户态后不是恢复main函数的上下⽂文继续执⾏行,⽽而是执⾏行sighandler函 数,sighandler和main函数使⽤用不同的堆栈空间,它们之间不存在调⽤用和被调⽤用的关系, 是 两个独⽴立的控制流程。
5. sighandler函数返回后⾃自动执⾏行特殊的系统调⽤用sigreturn再次进⼊入内核态。
6. 如果没有新的信号要递达,这次再返回⽤用户态就是恢复main函数的上下⽂文继续执⾏行了。

先介绍两个函数:
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
头文件为:signal.h
sigaction函数可以读取和修改与指定信号相关联的处理动作。调⽤用成功则返回0,出错则 返回- 1。signo是指定信号的编号。若act指针⾮非空,则根据act修改该信号的处理动作。若oact指针⾮非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结 构体:
这里写图片描述
对结构体进行介绍:
sa_handler:可赋值为SIG_IGN,表示忽略信号;赋值为SIG_DFL,表示默认动作;也可赋值为一个函数指针,表示自定义的函数捕捉信号,该函数返回值为void,一个int的形参,可得捕捉到的信号编号。
sa_mask:当屏蔽掉当前信号时,希望有其他的信号同时被屏蔽,该字段说明这些需要额外屏蔽的信号,当信号处理函数返回时,自动恢复原来的信号屏蔽字。
sa_flags:不做介绍,以下使用中都置为0.
举例说明:
这里写图片描述
结果为:
这里写图片描述
int pause(void);
头文件:uinstd.h
pause函数使调⽤用进程挂起直到有信号递达。如果信号的处理动作是终⽌止进程,则进程终 ⽌止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause 不返回;如果信号的处理动作是捕捉,则调⽤用了信号处理函数之后pause返回-1,errno设置为 EINTR, 所以pause只有出错的返回值(想想以前还学过什么函数只有出错返回值?)。错误码 EINTR表 ⽰示“被信号中断”。
下面用alarm和pause实现mysleep函数:
第一个版本:
代码为:
这里写图片描述
这里写图片描述
结果如下:
这里写图片描述
设想以下,如果程序在执行到alarm(),刚设置好闹钟,接着被调度优先级高的进程取代,切那个进程执行很长时间,已经超过了闹钟的设置时间,然后再切换回来,继续执行pause(),那么进程就一直处于被挂起状态,并不想预期的那样,一段时间后收到SIGALRM信号,终止进程。
出现这个问题的根本原因是系统运行的的时序(Timing)并不像我们写程序时所设想的那样。 虽然alarm(nsecs)紧接着的下⼀一⾏行就是pause(),但是⽆无法保证pause()⼀一定会在调⽤用 alarm(nsecs)之 后的nsecs秒之内被调⽤用。由于异步事件在任何时候都有可能发⽣生(这⾥里 的异步事件指出现更⾼高优 先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题 ⽽而导致错误,这叫做竞态条件 (Race Condition)。
那么如何解决这个问题呢?
方法一:
1.屏蔽SIGALRM信号;
2. alarm(nsecs);
3. 解除对SIGALRM信号的屏蔽;
4. pause();
在解除信号屏蔽与pause()之间依旧有可能被别的进程切入。
方法二:
1.屏蔽SIGALRM信号;
2. alarm(nsecs);
3. pause();
4. 解除对SIGALRM信号的屏蔽;
这样更不行了,pause()调用前,SIGALRM信号一直被屏蔽着,这样进程还是一直挂起着。
下面介绍一个解决这个问题的函数:
int sigsuspend(const sigset_t *sigmask);
头文件为:signal.h
对该函数进行说明:
sigsuspend没有成功返回值,只有执⾏行了⼀一个信号处理函数之后 sigsuspend才返回,返回值为-1,errno设置为EINTR。
调⽤用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时 解除对某 个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原 来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题
下面为优化后的mysleep()
这里写图片描述
这里写图片描述
结果如下:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值