竞争条件

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,我们认为发生了竞争条件. 如果fork之后的某
种逻辑显示或者隐式的依赖于在fork之后是父进程先运行还是子进程先运行,那么fork函数就会使竞争条件活跃的滋生地.通常 我们不
能预料到哪一个进程先运行,即使我们知道那一个进程先运行,在该进程开始运行后所发生的事情也依赖于系统负载以及内核的调度算
法.我们都知道如果一个进程fork了一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态知道父进程终止,实现这个
要求的诀窍就是调用fork两次. 相关代码我放到下面:
/*************************************************************************> File Name: fork.c> Author: ma6174> Mail: ma6174@163.com > Created Time: Thu 18 Jan 2018 09:54:34 PM PST************************************************************************/#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<errno.h>int main()
{pid_t pid;if((pid = fork()) < 0)perror("fork error");else if(pid == 0){if((pid = fork()) < 0)perror("fork error");else if(pid > 0)exit(0);sleep(2);printf("second child , parent pid = %d\n",(int)getppid());exit(0);}if(waitpid(pid,NULL,0) != pid)perror("waitpid error\n");exit(0);
} 

最后来看一下我们的结果是什么:

当第二个子进程打印其父进程ID时,我们看到了一个潜在的竞争条件. 如果第二个子进程在第一个子进程之前运行,则其父进程将会是
第一个子进程. 但是,如果第一个子进程先运行,并有足够的时间到达并执行exit.,则第二个子进程的父进程就是init. 即使在程
序中调用sleep,也不能保证什么, 如果系统负载很重,那么在slee返回之后,第一个子进程得到机会运行之前,第二个子进程可能恢复
运行. 这种形式的问题很难调试,因为在大部分时间,这种问题并不出现.
如果一个进程希望等待一个子进程终止,则它必须调用wait函数中的一个. 如果一个进程要等待其父进程终止,则可使用下列形式的循环:
while(getppid() != 1)
sleep(1);

这种形式的循环称为轮询,他的问题是浪费了cpu时间,因为调用者每隔一秒都被唤醒,然后进行条件测试.

为了避免竞争条件和轮询,在多个进程之间需要有某种形式的信号发送和接受方法. 在unix中可以使用信号机制。接下来我们就开始介
绍到!

优化版本的mysleep

我们再上一个博客信号捕捉当中实现了一个我们自己认为很不错的一个mysleep函数,并且可以正常运行! 但是其实我们实现的sleep
就会有竞争的情况出现,比如我们出现了下面的时序步骤:
1.注册SIGALRM信号的处理函数.
2.调用alarm(nsecs)设置闹钟.
3.内核调度优先级更高的进程取代了当前进程的执行,并且优先级更高的进程有好多个,每个都要执行很长时间.
4.nsecs秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态.
5.优先级更高的进程执行完了,内核要调度回这个进程执行,SIGALRM信号递达,执行处理函数sig_alrm之后再次进入内核.
6.返回这个进程的主控制流程,alarm返回,调用pause()挂起等待.
7.可是SIGALRM信号已经处理完了,还等待什么呢?
这个就是一个很标准的我们的竞态条件,出现这个问题的根本原因就是系统运行的时序并不像我们写程序时所设想的那些. 虽然alarm
紧接着的下一行就是pause(),但是无法保证pause()一定会调用alarm(nsecs)之后的nsecs秒之内被调用. 由于异步事件在任何时候
都有可能发送(优先级级更高)的进程,如果我们写程序时考虑不够周密,就有可能出现时序问题,而导致错误. 那么我们的sleep应该
如何解决的这个问题呢???
这时候有人提出来了既然你是因为在pause之前处理掉了SIGALRM信号,那么我可不可以让进程在调用pause函数之前,将SIGALRM信号
阻塞,当使用pause函数的时候再取消阻塞,这样想没有错! 但是!优先级更高的进程替换你是随时随刻的,比如刚刚取消信号的
阻塞,又被切换走了,还是有可能发生竞态条件. 如果能够让对信号取消阻塞和调用pause同时运行该有多好! 也就是一个原子操作
多好啊! 还别说! 真的有这种操作:

sigsuspend函数

#include <signal.h>int sigsuspend(const sigset_t *mask);
和pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR.
调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号的阻塞,然后挂起等待,
当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对信号是阻塞的,从sigsuspend返回后仍然是阻塞的. 
接下来,我们就使用这个函数对我们的sleep重新编写,让它更加强大!
/*************************************************************************> File Name: mysleep2.c> Author: ma6174> Mail: ma6174@163.com > Created Time: Thu 18 Jan 2018 11:29:18 PM PST************************************************************************/#include<stdio.h>
#include<signal.h>
#include<unistd.h>void sig_alrm(int sig)
{//DO SOMETHING
}unsigned int mysleep(unsigned int nsecs)
{struct sigaction new,old;sigset_t newmask,oldmask,suspendmask;unsigned int unslept = 0;new.sa_handler = sig_alrm;sigemptyset(&new.sa_mask);new.sa_flags = 0;sigaction(SIGALRM,&new,&old);sigemptyset(&newmask);sigaddset(&newmask,SIGALRM);sigprocmask(SIG_BLOCK,&newmask,&oldmask);alarm(nsecs);suspendmask = oldmask;sigdelset(&suspendmask,SIGALRM);sigsuspend(&suspendmask);unslept = alarm(0);sigaction(SIGALRM,&old,NULL);sigprocmask(SIG_BLOCK,&oldmask,NULL);return unslept;
}int main()
{while(1){mysleep(1);printf("1 seconds passed\n");}
}
这就是一种常见的解决竞争条件的方法,当然我们看到了最有效的方法就是让好几个操作变成一个原子性的集中操作. 我们所
学习到的sigsuspend只是其中的一个小函数存在这种功能,还有许许多多的函数需要我们去了解. 我们目前位置先掌握竞态
条件什么,我们再写程序的时候尽量避免竞态条件的生成,如果没有办法避免就要查找相关函数,尽量保证关键操作的原子性.

操作系统 — 竞争条件相关推荐

  1. rust键位失灵_用Rust写操作系统(四)——竞争条件与死锁

    一.概要说明 当多个任务访问同一个资源(数据)是就会引发竞争条件问题,这不仅在进程间会出现,在操作系统和进程间也会出现.由竞争条件引发的问题很难复现和调试,这也是其最困难的地方.本实验的目的在于了解竞 ...

  2. 操作系统原理实验(四)深渊:竞争条件与死锁(硬件中断)

    硬件中断 中断控制器 启动中断 处理定时器中断 死锁 竞争条件 HLT指令 键盘输入 中断控制器 中断提供了一种从附加硬件设备通知CPU的方法.这个英特尔8259是1976年推出的可编程中断控制器(P ...

  3. 【操作系统】竞争条件与临界区

    Race Condition 多个进程并发访问和操作同一数据且执行结果与访问发生的特定顺序有关,称为竞争条件(Race Condition). 为了防止上述竞争条件,需要确保一段时间内只有一个进程能操 ...

  4. Linux Kernel ‘install_user_keyrings()’竞争条件漏洞

    漏洞名称: Linux Kernel 'install_user_keyrings()'竞争条件漏洞 CNNVD编号: CNNVD-201303-141 发布时间: 2013-03-11 更新时间: ...

  5. 查询表授权给谁了_SQL Server 全局临时表竞争条件漏洞利用

    在网络和应用程序渗透测试期间,SQL Server 全局临时表通常不是关注的焦点.然而,它们被开发人员周期性地不安全地用来存储敏感数据和代码块,这些数据和代码块可以被非特权用户访问.在本博客中,我将介 ...

  6. 竞争条件(race condition)

    在一些操作系统中,协作的进程可能共享一些彼此都能读写的公用存储区.这个公用存储区可能在内存中(可能是在内核数据结构中),也可能是一个共享文件.这里共享存储区的位置并不影响通信的本质及其带来的问题.为了 ...

  7. 临界区,互斥,竞争条件

    临界区,互斥,竞争条件 公共资源 可以是公共内存.公共文件.公共硬件等,总之是被所有任务共享的一套资源. 临界区 程序要想使用某些资源,必然通过一些指令去访问这些资源,若多个任务都访问同一公共资源,那 ...

  8. linux内核远程漏洞,CVE-2019-11815:Linux内核竞争条件漏洞导致远程代码执行

    *本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担. 运行了Linux发行版的计算机设备,如果内核版本小于5.0.8的话,将有可能受到一 ...

  9. linux内核竞争条件漏洞,Linux内核竞争条件漏洞-导致远程代码执行

    原标题: Linux内核竞争条件漏洞-导致远程代码执行 导读*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担. 运行了Linux发行版 ...

最新文章

  1. 位操作-按位与之如何求二进制数的1个数
  2. AI教父争夺秘史:百度2.88亿天价求才,因中国身份惜败谷歌
  3. HDU1007 查找平面最近点对
  4. 【转】异步编程:.NET 4.5 基于任务的异步编程模型(TAP)
  5. oracle执行sql痕迹,Oracle 查询刚执行的SQL
  6. ★LeetCode(17)——电话号码的字母组合(JavaScript)
  7. IBM的人工智能“沃森”首次确诊罕见白血病,只用了10分钟!
  8. Java常用日志框架介绍(转载)
  9. Java中 … 三个点是什么意思?
  10. (商品评价页)商品星级评分html+css+js
  11. (谷歌)Chrome浏览器添加扩展程序白名单
  12. mysql datasource property_spring配置datasource三种方式
  13. 音视频流媒体————基本概念
  14. csp怎么给线条描边,插画师要失业了?还在纠结阴影怎么画?CSP软件能直接自动生成...
  15. cv2-特征点匹配(bf、FLANN)
  16. csv文件导入Mysql
  17. 微信可以打开淘宝链接,淘宝也在测试微信支付,但真的都很别扭
  18. 利用Github免费搭建个人主页(个人博客)
  19. mysql建表及DML语句
  20. 微信公众号开通步骤详解

热门文章

  1. java数组从小到大排序
  2. 山东省高校机器人大赛-智能避障避险小车经验分享
  3. 搜狗开源内部项目管理系统Cynthia,已被数十家企业采用
  4. 腾讯企业邮箱:授权密码
  5. 吃烧烤之命令模式学习笔记[C++版]
  6. Java笔记-08 异常
  7. NMEA报文解析程序(c语言)-命令解析
  8. 大噶好,今天是应景的二分法
  9. Spark基础学习笔记:搭建spark on yarn 集群
  10. mysql unknown_mysql碰到unknown variable 'xxxx' 的解决方法