Linux系统编程—-8(竞态条件,时序竞态,pause函数,如何解决时序竞态)

竞态条件(时序竞态):

pause 函数

调用该函数可以造成进程主动挂起,等待信号唤醒。调用该系统调用的进程将处于阻塞状态(主动放弃 cpu) 直 到有信号递达将其唤醒,等不到一直等
返回值:-1 并设置 errno 为 EINTR

返回值:
  1. 如果信号的默认处理动作是终止进程,则进程终止,pause 函数么有机会返回。
  2. 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause 函数不返回。
  3. 如果信号的处理动作是捕捉,则【调用完信号处理函数之后,pause 返回-1】 errno 设置为 EINTR,表示“被信号中断”。想想我们还有哪个函数只有出错返回值。
  4. pause 收到的信号不能被屏蔽如果被屏蔽,那么 pause 就不能被唤醒。
使用 pause 和 alarm 来实现 sleep 函数。

时序竞态

欲睡觉,定闹钟 10 分钟,希望 10 分钟后闹铃将自己唤醒。 正常:定时,睡觉,10 分钟后被闹钟唤醒。
异常:闹钟定好后,被唤走,外出劳动,20 分钟后劳动结束。回来继续睡觉计划,但劳动期间闹钟已经响过, 不会再将我唤醒。

时序问题分析

借助 pause 和 alarm 实现的 mysleep 函数。设想如下时序:

  1. 注册 SIGALRM 信号处理函数 (sigaction…) 2
  2. 调用 alarm(1) 函数设定闹钟 1 秒。
  3. 函数调用刚结束,开始倒计时 1 秒。当前进程失去 cpu,内核调度优先级高的进程(有多个)取代当前进程。 当前进程无法获得 cpu,进入就绪态等待 cpu。
  4. 1 秒后,闹钟超时,内核向当前进程发送 SIGALRM 信号(自然定时法,与进程状态无关),高优先级进程尚未 执行完,当前进程仍处于就绪态,信号无法处理(未决)
  5. 优先级高的进程执行完,当前进程获得 cpu 资源,内核调度回当前进程执行。SIGALRM 信号递达,信号设置 捕捉,执行处理函数 sig_alarm。
  6. 信号处理函数执行结束,返回当前进程主控流程, pause()被调用挂起等待。 (欲等待alarm函数发送的SIGALRM 信号将自己唤醒)
  7. SIGALRM 信号已经处理完毕,pause 不会等到。

如何解决时序问题(重点)

可以通过设置屏蔽 SIGALRM 的方法来控制程序执行逻辑,但无论如何设置,程序都有可能在“解除信号屏蔽” 与“挂起等待信号”这个两个操作间隙失去 cpu 资源。除非将这两步骤合并成一个“原子操作”。sigsuspend 函数具 备这个功能。在对时序要求严格的场合下都应该使用 sigsuspend 替换 pause。

挂起等待信号。
sigsuspend 函数调用期间,进程信号屏蔽字由其参数 mask 指定。
可将某个信号(如 SIGALRM)从临时信号屏蔽字 mask 中删除,这样在调用 sigsuspend 时将解除对该信号的屏 蔽,然后挂起等待, 当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值。 如果原来对该信号是屏蔽态, sigsuspend 函数返回后仍然屏蔽该信号。

总结

  1. 竞态条件,跟系统负载有很紧密的关系,体现出信号的不可靠性。系统负载越严重,信号不可靠性越强。
  2. 不可靠由其实现原理所致**。信号是通过软件方式实现(跟内核调度高度依赖,延时性强),每次系统调用结束后, 或中断处理处理结束后,需通过扫描 PCB 中的未决信号集,来判断是否应处理某个信号。**当系统负载过重时,会出 现时序混乱。
  3. 这种意外情况只能在编写程序过程中,提早预见,主动规避,而无法通过 gdb 程序调试等其他手段弥补。且由 于该错误不具规律性,后期捕捉和重现十分困难

文章知识点与官方知识档案匹配,可进一步学习相关知识CS入门技能树Linux入门初识Linux24898 人正在系统学习中

来源:阳光丿洒脱

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年7月15日
下一篇 2019年7月16日

相关推荐