系列文章目录
进程【1】-理解子进程与父进程的竞争关系
进程【2】-理解进程fork的使用
信号【3】- 理解sleep函数
信号【4】-理解实时信号
信号【5】-理解sigsuspend
进程【6】-理解进程的终止:exit,_exit,return
进程【7】-关于EINTR错误的理解(可中断与不可中断的区别)
文章目录
前言
提示:解决以下几个问题:
- 进程的创建,使用
fork
之后,存在怎样的竞争条件 - 子进程和父进程的竞争关系如何产生?
- 子进程和父进程的竞争关系如何消除?
- 子进程和父进程的竞争关系典型的用例
一、进程的生命周期
要想弄清楚提出的问题,我们需要理解进程的生命周期,以及fork的具体过程。
一、原子操作和竞争条件
1.1 原子操作
所有系统调用都是以原子方式执行的。因为系统内核保证了系统调用的所有步骤会以独立操作的方式一次性执行完毕,中间不会为其他进程所中断。
所以原子操作针对的是进程或者线程不被中断的现象。
1.2 竞争状态
- 什么是竞争状态
操作共享资源的两个进程或线程,其结果取决于一个无法预期的顺序,即这些进程获取CPU使用权的相对先后顺序。 - 有哪些竞争条件
进程和进程之间,线程和线程之间,线程和信号处理器之间
二、进程的创建,使用fork
之后,存在怎样的竞争条件
- 产生竞争条件的原因
调用了fork()之后,无法确定是子进程和父进程先运行,而应用程序为了得到正确的结果或明或暗的需要依赖特定的顺序,就会出现竞争关系。 - 那么子进程和父进程是哪一个先执行
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int numChildren,j;
pid_t childPid;
if (argc > 1 && strcmp(argv[1],"--help") == 0)
_exit(1);
numChildren = atoi(argv[1]);
setbuf(stdout, NULL);
for (j = 0;j < numChildren; j++){
switch (childPid = fork())
{
case -1:
exit(1);
case 0:
printf("%d child\n",j);
exit(-1);
default:
printf("%d parent\n",j);
wait(NULL); /*wait for child to terminate*/
break;
}
}
return 0;
}
实验结果是:大部分的时候是先运行父进程,在运行子进程,3000次左右,会有一次先运行子进程。
通过查阅姊俩:历史上有过对内核对先运行子进程的改动,但是后来又舍弃"fork()之后先调度子进程"。
- fork()之后先调度子进程
理由:可考虑frok()之后立即调用exec()时的“写时复制技术”。一方面父进程调用fork()之后继续修改数据页和栈页,还要将自己的内存段复制给子进程。但是子进程一旦调用"exec()"之后,内存段的复制属于浪费过程了,所以先调度子进程,等下次调度父进程时,就无需再做无用的拷贝了。 - fork()之后先调度父进程
Linux2.6.32 改回来“fork()之后先调度父进程”,其论据基于如下发现:fork()之后,父进程再CPU中正处于活跃状态,并且其内存管理信息也被置于硬件内存管理单元的转译后备缓冲器中。所以,先运行父进程可提高程序的性能。
无论是先调度子进程还是父进程都不能事先假定,若需要先后顺序,必须采用同步技术。
三、 子进程和父进程的竞争关系如何消除?
信号量,消息队列,文件锁来进行协调。