从一段代码看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》
从一段代码看fork()函数及其引发的竞争的更多相关文章
- 一段代码看 Java 引用类型
Java 中的操作数(不知道叫什么,相对于 bytecode 而言,类似 CPU 的操作码和操作数)分为值类型和引用类型: 值类型就是直接存储最终数值的,如 char, int, float, dou ...
- 127个常用的JS代码片段,每段代码花30秒就能看懂(上)
127个常用的JS代码片段,每段代码花30秒就能看懂(上) JavaScript 是目前最流行的编程语言之一,正如大多数人所说:“如果你想学一门编程语言,请学JavaScript.” FreeCode ...
- Linux多任务编程之二:fork()函数及其基础实验(转)
来源:CSDN 作者:王文松 转自Linux公社 fork()函数 在 Linux 中创建一个新进程的唯一方法是使用fork()函数.fork()函数是 Linux 系统中一个非常重要的函数,和咱们 ...
- Linux环境fork()函数详解
Linux环境fork()函数详解 引言 先来看一段代码吧, 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include ...
- 知识点查缺补漏贴02:Linux环境fork()函数详解
引言 先来看一段代码吧, #include <sys/types.h> #include <unistd.h> #include <stdio.h> #includ ...
- fork 函数的一点学习
昨天某位少年问了我一个问题,#include<stdio.h> int main() { fork(); fork(); fork(); printf("hello " ...
- fork函数详解(附代码)
虽然篇幅很长,但大多是易懂的代码,不用担心看不完 这里的所有操作,都将在下面的代码中有所体现 fork会拷贝当前进程的内存,并创建一个新的进程.如上图,fork函数会将整个进程的内存镜像拷贝到新的内存 ...
- 从linux0.11中起动部分代码看汇编调用c语言函数
上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一 ...
- js中闭包来实现bind函数的一段代码的分析
今天研究了一下bind函数,发现apply和call还可以有这样的妙用,顺便巩固复习了闭包. var first_object = { num: 42 }; var second_object = { ...
随机推荐
- 常用Git命令汇总
常用Git命令汇总 跟着R哥来到了新公司(一个从硬件向互联网转型中的公司),新公司以前的代码基本是使用SVN做版本控制,甚至有些代码没有做版本控制,所以R哥叫HG做了一次Git分享,准备把公司所有的代 ...
- js获取某个标签中的信息
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- Android 打造自己的个性化应用(五):仿墨迹天气实现续--> 使用Ant实现zip/tar的压缩与解压
上一篇中提到对于Zip包的解压和压缩需要借助Ant 实现,我经过参考了其他的资料,整理后并加上了一些自己的看法: 这里就具体地讲下如何使用Ant进行解压缩及其原因: java中实际是提供了对 zip ...
- 《JavaScript 闯关记》之原型及原型链
原型链是一种机制,指的是 JavaScript 每个对象都有一个内置的 __proto__ 属性指向创建它的构造函数的 prototype(原型)属性.原型链的作用是为了实现对象的继承,要理解原型链, ...
- django中使用原生sql
在Django中使用原生Sql主要有以下几种方式: 一:extra:结果集修改器,一种提供额外查询参数的机制 二:raw:执行原始sql并返回模型实例 三:直接执行自定义Sql ( 这种方式完全不依赖 ...
- 【转】Visual Studio 2010在数据库生成随机测数据
测试在项目中是很重要的一个环节,在Visual Studio 2010中,在测试方面已经有很好的支持了,比如有单元测试,负载测试等等.在数据测试的方面,Visual Studio 2010,还支持对数 ...
- iOS开发-单例模式的解读
现在网上的有很多人写单例模式,一个很基本的东西但是版本也有很多,新人看了难免有些眼花缭乱的感觉.自己最新比较闲,也过来写一些自己的心得. 在往下看之前,我们要明白一点,那就是在什么情况下我们才要用到单 ...
- c#窗体的传值方法
了解了窗体的显示相关知识,接着总结一下窗体的传值方法: .通过构造函数 特点:传值是单向的(不可以互相传值),实现简单 实现代码如下: 在窗体Form2中 int value1; ...
- Ubuntu14.04配置arm-linux-gcc 4.4.3交叉编译环境
首先下载交叉编译:不多说,直接贴地址了 http://arm9download.cncncn.com/mini2440/linux/arm-linux-gcc-4.4.3-20100728.tar.g ...
- 段的创建表user_segments
1.段的定义及类型 Oracle中的段(segment)是占用磁盘空间的一个对象,最常见的段类型包括: l 聚簇cluster l 表table l 表分区 tablepartition l ...