惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
P
Proofpoint News Feed
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
博客园 - Franky
V
V2EX
爱范儿
爱范儿
J
Java Code Geeks
小众软件
小众软件
Last Week in AI
Last Week in AI
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
酷 壳 – CoolShell
酷 壳 – CoolShell
The Register - Security
The Register - Security
GbyAI
GbyAI
Vercel News
Vercel News
Y
Y Combinator Blog
腾讯CDC
F
Fortinet All Blogs
I
InfoQ
N
Netflix TechBlog - Medium
B
Blog RSS Feed
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
量子位
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
H
Help Net Security
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
Scott Helme
Scott Helme
T
Tor Project blog
U
Unit 42
Google Online Security Blog
Google Online Security Blog
PCI Perspectives
PCI Perspectives
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
aimingoo的专栏
aimingoo的专栏
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Stack Overflow Blog
Stack Overflow Blog
Security Archives - TechRepublic
Security Archives - TechRepublic

博客园 - woodfish

偏序集的Dilworth定理 XJOJ历经2个月终于完成啦~~ 实现google那种输入框提示的功能 POJ 3691 安徽第二题 有限状态自动机+DP 哈尔滨赛区网络预选赛总结 [计算几何]点集中的点能组成多少个正方形 [计算几何]POJ 1375 点对圆的切线+线段重叠 [计算几何]POJ 1556 判断线段相交+Dijkstra [计算几何] POJ 1873 暴力+凸包 [计算几何]POJ 1266 三角形的外接圆 圆的参数方程 POJ 1026 置换群 [计算几何]POJ 1031 计算点对多边形的偏转角度 [计算几何]POJ2079 求点集中面积最大的三角形 [计算几何]POJ3608 求2个不相交凸包的最短距离 [计算几何]凸包的旋转卡壳算法 C++禁止一个类被继承的技术 C与汇编的接口技术 计算位数的3种方法 避免使用条件分支
fork()调用的一个趣题
woodfish · 2008-12-27 · via 博客园 - woodfish

经常看到有人问到这样一个问题:


#include 
<stdio.h>
#include 
<sys/types.h>
#include 
<unistd.h>int main() {
  
int pid=0;
  
for(int i=0;i<5;i++) {
    pid
=fork();
    
if(pid==0) {
      printf(
"pid:%d\n",getpid());
    }
  }
  
return 0;
}

问最后打印了多少行pid:xxx.很多人一看,认为很简单,不就产生了5个子进程嘛,答案就是5个,这样回答可以说压根没有理解Linux/Unix中fork()系统调用是怎么实现的。上面的问题等价于问这个程序总共产生了多少个进程(算自身)an,最后的答案就是an-1,因为最开始的进程不会打印这条信息,如果把程序这样改一下:


#include 
<stdio.h>
#include 
<sys/types.h>
#include 
<sys/wait.h>
#include 
<unistd.h>int main() {
  
int pid=0;
  
for(int i=0;i<5;i++) {
    pid
=fork();
    wait(NULL);
  }
  printf(
"pid:%d\n",getpid());
  
return 0;
}

 那么答案很明显就是所有的进程个数an,上面加入wait调用的目的是使各个进程不交叉输出信息。

为了求an,先简要的介绍一下fork()系统调用,在linux中,fork()调用会调用clone(),而clone()最终会调用 do_fork()系统调用来产生子进程,关键是这个子进程怎么产生的。在linux/unix中,fork()产生的子进程相当于复制了整个父进程,首先复制了PCB,然后将内存页表共享到父进程的页面(写时复制)。通俗一点,子进程和父进程看起来是完全一样的,一样的代码段,一样的数据段,一样的进程控制块,但是他们是独立的,并且从内核返回到用户态时,系统调用对原进程返回子进程的pid,对子进程返回0,这样就可以区分父子进程了。

 回到上面的问题,为什么答案5是错的,举个例子:父进程i=0的时候fork()了一个子进程p1,但是p1现在和父进程的状态是一样的,也就是会继续接着循环,从i=1来fork()一个p2,而p2又会继续从i=3开始来fork()其他的子进程,这样就会产生很多很多子进程了。

现在来求解具体的产生的进程的个数。

设f(n)表示程序中循环会执行n次时整个程序会产生的进程数,很容易得到递推公式:

 f(n)=1+f(n-1)+f(n-2)+f(n-3)+...+f(0)

比如for i=0;i<n;i++

因为i=0时fork()的子进程下次会继续循环n-1次,i=1时 fork()的子进程下次会仅需循环n-2 次。。。。

 其中常数1是进程本身。

边界条件,f(0)=1

这样,我们就得到了问题的答案:

f(n)=1+f(n-1)+f(n-2)+...+f(0)

f(0)=1

这个可以求出闭形式:

f(0)=1

f(1)=2

f(2)=4

...

用数学归纳法可以得到f(n)=2^n

所以对于程序一,会打印出2^5-1=31行信息。

对于程序二,总共会产生2^5=32个进程。