从一段代码看fork()函数及其引发的竞争
首先来看一段从《UNIX环境高级编程》中摘录的一段很有意思的代码。借此我们再来谈谈fork()函数的一些问题。
#include "apue.h" static void charatatime(char*); int main(void) { pid_t pid; if((pid=fork())<0){ err_sys("fork error"); }else if(pid==0){ charatatime("output from child\n"); }else{ charatatime("output from parent\n"); } exit(0); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout,NULL); /*set unbuffered*/ for(ptr=str;(c=*ptr++)!=0;) putc(c,stdout); }这段代码究竟干了些啥呢?事实上很easy,首先用fork()函数生成了一个子进程。
事实上子进程能够看成是父进程的一个复制。
那么在如上的一段代码中,怎么推断是子进程还是父进程在运行呢?这时候我们就要来看看fork()函数的返回值了。
fork()的返回值:
当fork被调用之后,父子进程都从fork()之后開始执行。当然,父子进程要干的事情是不一样的,可是前面说了,子进程就是父进程的一个复制。它们事实上是共享一个代码段的。
这个时候,我们就要依靠fork()的返回值来推断当前执行的程序是父进程还是子进程了。fork()函数是个很有意思的家伙。它仅仅被调用了一次,可是却有两个返回值,分别返回到父子进程中。
在父进程中,fork()返回的是子进程的进程ID。值得注意的是,仅仅有在fork()函数的时候。父进程才干得到子进程的ID。否则的话就没有机会了。由于一个父进程能够有多个子进程。想要通过一个函数,得到某个确切的子进程的进程ID显然是比較困难的。
与父进程不同的是。子进程通常仅仅有一个父进程。因此能够通过一个叫getppid()的函数,找到自己父进程的ID。
而fork()在子进程中的返回值是0.这又是为什么呢?由于0一般是系统保留的进程号,因此不可能出现子进程的进程号为0的情况。正如上面的代码显示的那样,当pid==0的时候传递给子函数的字符数是“output from child”,否则那就是在父进程中。传递的字符串自然也成了“output from parent”。
接下来另一个问题,那就是,当fork()之后。父子进程事实上能够看成是两个独立的进程了。
那究竟是先运行父进程呢?还是先运行子进程呢?因此我们接着来谈谈进程间的竞争问题。
进程间的竞争(race condition):
那父子进程究竟是谁先执行呢?事实上一般来说,这是无法预測的,这要看内核的调度算法等一系列其它的因素。
我们能够会过来看看上面的代码。在父子进程共同调用的charatatime函数中,我们首先用setbuf取消了标准I/O的缓冲。这样在以下的for循环中,仅仅要putc一次,就会有对应的字符显示在shell上。依据上面的分析,我们能够预測的是,两个字符串可能并不会依照先后顺序完整地输出。由于进程间非常可能进行切换,一个字符串可能还没输完。内核就转而执行还有一个字符串的输出了。因此显示的shell中显示的结果非常可能是交叉输出的字符串。以下的图就为我们展示了结果:
非常显然。输出的结果是不可预測的,有时候是比較规则的输出,但有些时候就凌乱了。而这,就是进程间的竞争(race condition)。当然,解决竞争的方法有非常多。我们能够通过信号(signal)以及进程间通信(IPC)等待方式。来解决竞争的问题。这些就放到以后再说啦!
參考文献:《Advanced Programming in the UNIX Environment》
相关文章
- x264代码剖析(十一):核心算法之宏块分析函数x264_macroblock_analyse()
- x264代码剖析(三):主函数main()、解析函数parse()与编码函数encode()
- Python中sort和sorted函数代码解析
- js中的string.format函数代码
- Scala控制抽象:将一段代码作为参数传递给高阶函数去执行
- Vs2010追踪服务器端的代码
- ZZNUOJ_C语言1053:正弦函数(完整代码)
- ZZNUOJ_C语言1024:计算字母序号(完整代码)
- ZZNUOJ_C语言1101:逆序数字(函数专题)(完整代码)
- ZZNUOJ_C语言1103:平均学分绩点(函数专题)(完整代码)
- 编程笔试(解析及代码实现):求和为N的正整数序列之实现一个函数,输入为一个正整数N (比如100),输出为所有和等于N的[连续]正整数序列
- 基于蒙特卡洛随机潮流研究(Matlab代码实现)
- SVM与基于马氏距离的径向基函数(MDRBF)核结合组合(Matlab代码实现)
- pycharm自动补全代码,kite神器!!!再也不用记住完整的函数这些了,插件替你完成!
- 10个常用的损失函数及Python代码实现
- 基于麻雀算法优化的Tsallis相对熵图像多阈值分割 -附代码
- 智能优化算法:学生心理学优化算法 -附代码
- 基于差分进化与优胜劣汰策略的灰狼优化算法-附代码
- 最小化JavaScript代码
- 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取注入的 libbridge.so 动态库中的 load 函数地址 并 通过 远程调用 执行该函数 )
- 【Android 逆向】代码调试器开发 ( ptrace 函数 | 读寄存器 | 写寄存器 )
- 目录遍历漏洞——本质上是因为php这样的后端代码实现中使用了include这样的模板函数导致
- 函数体的规模要小,尽量控制在 50 行代码之内
- c++职工管理系统主函数代码
- JDK8_01_JDK8新特性,Lambda表达式带来函数式编程,简化代码