0前言

上周都在看都在学习unix环境高级编程的第八章——进程控制。也就是这一章中。让我理解了unix中一些进程的原理。以下我就主要依照进程中最重要的三个函数来进行解说。让大家通过阅读这一篇文章彻底明确进程这点事。希望对大家有所帮助。

1进程环境

         在学习进程之前。一定要了解一下unix的进程环境。系统怎样对进程终止,和一个程序启动终止,程序运行的原理等,这些都有助于你理解进程的运行原理。这些内容都在我的上一篇文章中,请关注:http://blog.csdn.net/wallwind/article/details/6968323。文章中讲的较为具体。

2进程概念:

一个进程。主要包含三个元素:

a)        一个能够运行的程序。

b)        和该进程相关联的全部数据(包含变量,内存空间。缓冲区等等);

c)        程序的运行上下文(execution context)。

最好还是简单理解为,一个进程表示的,就是一个可运行程序的一次运行过程中的一个状态。操作系统对进程的管理。典型的情况,是通过进程表完毕的。进程表中的每个表项,记录的是当前操作系统中一个进程的情况。对于单 CPU的情况而言,每一特定时刻仅仅有一个进程占用 CPU。可是系统中可能同一时候存在多个活动的(等待运行或继续运行的)进程。

3 fork()函数

         fork()函数是进程的核心函数。

当调用fork的时候,系统将创建一个新的进程,成为子进程(child process)。Fork函数定义形式例如以下:

  1. #include<unistd.h>  
  2.   
  3. Pid_t fork(void);//返回值:子进程返回0。父进程中返回子进程ID,出错则返回-1  

从返回值我们能够看到,Fork函数调用了一次,可是返回两次。其差别在于在子进程中返回值是0,而父进程的返回值则是新子进程的进程ID.

         上边的概念可能对刚開始学习的人比較模糊。那么我们怎么理解fork呢?

         当你看到fork的时候。你能够把fork理解成“分叉”。在分叉的同一时候,生成的一个子进程复制了父进程的基本是全部的东西,包含代码、数据和分配给进程的资源。也就是子进程差点儿是和父进程是一模一样的。可是子进程可能会依据不同情况调用其它函数。

比方exec函数。

         以下我们用一个经典及比較典型简单的样例来看看。

    

  1.     #include"apue.h"  
  2.   
  3.   
  4. int glob=6;  
  5.   
  6. char buf[]="a write to stdout\n";  
  7.   
  8.    
  9.   
  10. int main(void)  
  11.   
  12. {  
  13.   
  14.        int var=88;  
  15.   
  16.        pid_t pid;  
  17.   
  18.    
  19.   
  20.        if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)  
  21.   
  22.                 printf("writeerror");  
  23.   
  24.    
  25.   
  26.        printf("before fork with  换行符\n");  
  27.   
  28.        printf("before fork without换行符");     
  29.   
  30.        //printf("\n");  
  31.   
  32.    
  33.   
  34.        if((pid=fork())<0){  
  35.   
  36.                 printf("fork error");  
  37.   
  38.    
  39.   
  40.        }else if(pid==0){  
  41.   
  42.                 printf("I am is  child process,pid=%d\n",getpid());  
  43.   
  44.                 printf("my parentprocess's pid=%d\n",getppid());  
  45.   
  46.                 glob++;  
  47.   
  48.                 var++;  
  49.   
  50.        }else{  
  51.   
  52.                 printf("this is parentprocess,pid=%d\n",getpid());  
  53.   
  54.                 //var++;  
  55.   
  56.        }  
  57.   
  58.    
  59.   
  60.        printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);  
  61.   
  62.        exit(0);  
  63.   
  64. }  

输出结果:                             

希望新手能够将以上代码自己敲一遍,然后自己运行一遍。

从上边的结果我们一步步分析。

首先我们看到两行文字是仅仅输出一遍的。

既”a write to stdout”

         “Befork forkwhit h换行符”。

也就是仅仅有一个进程运行这两个输出语句。我们先暂且不分析不带换行符的。

当程序调用fork的时候。这个时候程序就出现了一个进程。也就是两个进程在运行。

请看下边的简易图形

在上边我们已经讲过fork函数会由于不同调用返回不同的函数值:

1)在父进程中,fork返回新创建子进程的进程ID;

2)在子进程中。fork返回0;

3)假设出现错误,fork返回一个负值;

如今我们看程序里的几个函数。

Write函数是将buf内容写到标准流中。此处仅仅作为输出用。

我们关心的是getpid()函数和getppid()函数。

getpid()函数是用来输出当前成进程的ID,getppid()则得到当前进程的父进程的ID,从输出结果比对能够看到。子进程得到的父ID和父进程得到自己的ID是相同的。

再来看一下全局变量glob。glob是函数外的变量。子进程将glob++处理。这个时候输出的是7,父进程没有进行处理直接输出6。我们能够知道,这两个glob变量显示是两份的。不相干的。

局部变量var也是相同的结果。。。

如今我们来解释一下为什么

  1. printf("before fork with  换行符\n");  
  2.   
  3. printf("before fork without换行符");     

上一句仅仅输出了一次。而下边这句话就输出了两次。

我们能够发现printf("before fork with  换行符\n");这句带有\n既换行符的意思。而

  1. printf("before fork without换行符");     

是没有带的。

这个我们要知道printf函数是有缓冲机制的,相似于我们使用的write函数。但我们将想要的东西输出的时候。系统仅仅是把内容放到stdout标准输出的缓冲队列的。

当遇到“\n”的时候,系统就把缓冲里的东西给清掉,输出到屏幕上。

        

  1. printf("beforefork with  换行符\n");  

运行后,缓冲里没有了数据。自然子进程再次运行的时候没有内容可输出了。可是printf("before fork without换行符");的时候,子进程也会把stdout的内容再次输出来。也就是导致了内容出处了两边。假设换一下书序的,结果是不一样的哦。

3 fork难度进阶

csdn看到一篇比較好的解说fork的文章。

较为深入的解说。

以下我就经过自己的调试和理解将呈现给大家。首先看一段代码:

  1. #include "apue.h"  
  2.   
  3.    
  4.   
  5. int main(void)  
  6.   
  7. {  
  8.   
  9.        int i=0;  
  10.   
  11.        pid_t pid;  
  12.   
  13.    
  14.   
  15.        for(i=0;i<2;i++)  
  16.   
  17.        {  
  18.   
  19.                 if((pid=fork())<0){  
  20.   
  21.                         printf("forkerror\n");  
  22.   
  23.                 }else if(pid==0){  
  24.   
  25.                        printf("%d,childself's pid=%d,parent's pid=%d,returnid=%d\n",i,getpid(),getppid(),pid);  
  26.   
  27.                }else{  
  28.   
  29.                        printf("%d,parentself's pid=%d,parent's father's pid=%d,returnid=%d\n",i,getpid(),getppid(),pid);  
  30.   
  31.                         sleep(2);//inorder tochild output first  
  32.   
  33.                 }  
  34.   
  35.        }  
  36.   
  37.        exit(0);  
  38.   
  39. }  

输出的结果为:

好好的分析一下我们的结果。0開始的输出为2行,1开头的输出4行。

然后我们開始观察PID的结果。从pid的结果我们找到一个关系

既9278-》30361-》30362-》30363

9278-》30361-》30364

要知道。fork函数子进程调用的时候返回0,而父进程调用的时候则返回子进程的ID。

程序运行第一步:

  1. if((pid=fork())<0){  
  2.   
  3.                         printf("forkerror\n");  
  4.   
  5.                 }else if(pid==0){  
  6.   
  7.                        printf("%d,childself's pid=%d,parent's pid=%d,returnid=%d\n",i,getpid(),getppid(),pid);  
  8.   
  9.                 }else{  
  10.   
  11.                        printf("%d,parentself's pid=%d,parent's father's pid=%d,returnid=%d\n",i,getpid(),getppid(),pid);  
  12.   
  13.                         sleep(2);//inorder tochild output first  
  14.   
  15.                 }  

输出的结果为:

这是第一次循环输出输出的。

Childpid和return 均为父ID为30361生成的30361。

此时已经有了两个进程了。

进程ID分别为30362和30361。

第二次循环输出。

我们知道了此时两个进程30362和30361。

这两个进程分别运行自己的代码。

ü  这个时候pid为30362的进程进行一次:

循环。得到结果为:

     
生成了一个子进程30363

ü  30361进程再次运行自己的代码,也就是上述循环的部分。

生成了一个子进程30364.

从程序和输出结果能够看到,事实上我们的程序得到了6份的拷贝。

通过这个样例,fork了三次,产生了三个子进程,输出了6次。你能够深刻的理解了fork工作原理了。

再来一个样例:

这个是典型的循环样例:

  1. #include <unistd.h>  
  2.   
  3. #include<stdio.h>  
  4.   
  5.    
  6.   
  7. int main(void)  
  8.   
  9. {  
  10.   
  11.        pid_t pid;  
  12.   
  13.        int i=0;  
  14.   
  15.        int c_cout=0;  
  16.   
  17.        int p_cout=0;  
  18.   
  19.        for(i=0;i<5;i++)  
  20.   
  21.        {  
  22.   
  23.                 if((pid=fork())<0){  
  24.   
  25.                         printf("forkerror\n");  
  26.   
  27.                 }else if(pid==0){  
  28.   
  29.                         c_cout++;  
  30.   
  31.                }else{  
  32.   
  33.                         p_cout++;  
  34.   
  35.                 }  
  36.   
  37.    
  38.   
  39.        }  
  40.   
  41.        printf("c_cout=%d,p_cout=%d,pid=%d\n",c_cout,p_cout,getpid());  
  42.   
  43.    
  44.   
  45. }  

输出结果为:c_cout=5,p_cout=0,pid=1559

c_cout=4,p_cout=1,pid=1558

c_cout=4,p_cout=1,pid=1560

c_cout=3,p_cout=2,pid=1557

c_cout=4,p_cout=1,pid=1562

c_cout=3,p_cout=2,pid=1561

c_cout=4,p_cout=1,pid=1566

c_cout=3,p_cout=2,pid=1565

c_cout=3,p_cout=2,pid=1569

c_cout=2,p_cout=3,pid=1568

c_cout=4,p_cout=1,pid=1574

c_cout=3,p_cout=2,pid=1573

c_cout=3,p_cout=2,pid=1577

c_cout=2,p_cout=3,pid=1576

c_cout=3,p_cout=2,pid=1581

c_cout=2,p_cout=3,pid=1580

c_cout=2,p_cout=3,pid=1584

c_cout=1,p_cout=4,pid=1583

c_cout=3,p_cout=2,pid=1563

c_cout=2,p_cout=3,pid=1556

c_cout=2,p_cout=3,pid=1564

c_cout=2,p_cout=3,pid=1570

c_cout=1,p_cout=4,pid=1555

c_cout=3,p_cout=2,pid=1575

c_cout=2,p_cout=3,pid=1572

c_cout=2,p_cout=3,pid=1578

c_cout=1,p_cout=4,pid=1571

c_cout=2,p_cout=3,pid=1582

c_cout=1,p_cout=4,pid=1579

c_cout=1,p_cout=4,pid=1585

c_cout=0,p_cout=5,pid=1554

通过推理:引用网络文章可得到:

设f(n)表示程序中循环会运行n次时整个程序会产生的进程数,非常easy得到递推公式:

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

比方for i=0;i<N;I++< p>

由于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个进程。

只是,我还是不知道为什么两个变量的结果怎么是那种形式的输出,求指导啊!!!

以下贴一下比較有意思的一段代码:

  1. #include <stdio.h>   
  2.   
  3. #include <unistd.h>   
  4.   
  5. int main(int argc, char* argv[])   
  6.   
  7. {   
  8.   
  9. fork();   
  10.   
  11. fork() && fork() || fork();         
  12.   
  13. fork();   
  14.   
  15. return 0;   
  16.   
  17. }   

我们要知道&& 和||运算符。&&有个短路现象。

A&&B,假设A=0,就没有必要继续运行&&B了。A非0,就须要继续运行&&B。

A||B。假设A非0。就没有必要继续运行||B了,A=0。就须要继续运行||B。

通过绘图我们能够得到例如以下

加上前面的fork和最后的fork,总共4*5=20个进程,除去main主进程。就是19个进程了。

很多其它文章,欢迎关注http://blog.csdn.net/wallwind 版权归博主全部~~禁止商业用途

深入浅出--UNIX多进程编程之fork()函数的更多相关文章

  1. Linux下多进程编程之exec函数语法及使用实例

    exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...

  2. Linux编程之fork函数

    在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...

  3. Python 多进程编程之fork()

    Python实现多进程可以用系统fork()方法和python的multiprocessing类 1,fork()方法是Unix/Linux操作系统提供的,在python的os模块中自带fork(). ...

  4. 多进程编程之system()函数

    1.system函数: 使用函数system,在程序中执行一个shell命令字符串很方便.它是一个和操作系统紧密相关的函数,用户可以使用它在自己的程序中调用系统提供的各种命令,执行系统的命令行,其实也 ...

  5. Linux/Unix C编程之的perror函数,strerror函数,errno

    #include <stdio.h> // void perror(const char *msg); #include <string.h> // char *strerro ...

  6. 深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  7. [Cocoa]深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  8. Python 多进程编程之multiprocessing--Pool

    Python 多进程编程之multiprocessing--Pool ----当需要创建的子进程数量不多的时候,可以直接利用multiprocessing 中的Process 动态生成多个进程, -- ...

  9. Python 多进程编程之multiprocessing--Process

    Python 多进程编程之multiprocessing 1,Process 跨平台的进程创建模块(multiprocessing), 支持跨平台:windowx/linux 创建和启动      创 ...

随机推荐

  1. 设置 WPF 的内容支持触摸直接滚动

    在滚动内容上设置属性 ScrollViewer.PanningMode 的值即可. 另外可重写 OnManipulationBoundaryFeedback 方法来替换系统默认的滚动到最上最下时触发的 ...

  2. Atitit s2018.2 s2 doc list on home ntpc.docx  \Atiitt uke制度体系 法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别 讯飞科大 语音云.docx \Atitit 代码托管与虚拟主机.docx \Atitit 企业文化 每日心灵 鸡汤 值班 发布.docx \Atitit 几大研发体系对比 Stage-Gat

    Atitit s2018.2 s2 doc list on home ntpc.docx \Atiitt uke制度体系  法律 法规 规章 条例 国王诏书.docx \Atiitt 手写文字识别   ...

  3. django聚合查询

    聚合¶ Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法.然而,有时候你需要获取的值需要根据一组对象聚合后才能得到.这份指南描述通过Django 查询来生成和返回聚 ...

  4. HTTPS和SSL握手过程(转载)

    https介绍 HTTPS = HTTP + 一组对称.非对称和基于证书的加密技术 HTTPS是最常见的HTTP安全版本.它得到了很广泛的应用,所有主要的商业浏览器和服务器都提供HTTPS.HTTPS ...

  5. Airtest iOS测试环境部署

    [本文出自天外归云的博客园] 简介 这个Airtest IDE是通过iOS-Tagent来操作iPhone的,你可以在Airtest IDE里录制脚本来实现自动化操作iPhone 前提 1. 得有个i ...

  6. 四大中三家已面向客户推出机器人业务解决方案?别逗了,先用机器人自我革命吧! post by 上海嘉冰信息技术

    近日,四大会计师事务所推出的机器人财务及业务解决方案的话题引爆朋友圈.鉴于该话题的前沿性以及对财务及业务领域从业人员未来职业发展有巨大的影响,引起热门讨论在所难免.小编先来汇总下目前国际四大会计师事务 ...

  7. 搭建Pypi转发服务

    有时候有些正式环境的机器,不能访问外网,就只能在能访问外网的机器上搭建一个转发服务. 一.安装包 pip install flask_pypi_proxy flask_pypi_proxy 二.启动 ...

  8. Oracle同义词(synonym)

    oracle的同义词总结   从字面上理解就是别名的意思,和视图的功能类似.就是一种映射关系.   同义词拥有如下好处:   节省大量的数据库空间,对不同用户的操作同一张表没有多少差别;   扩展的数 ...

  9. OpenJDK和JDK区别

    OpenJDK和JDK区别 OpenJDK与JDK的区别分析 Sun的JDK7.OpenJDK及IcedTea释疑 简介(ps): 简单来说jdk从7开始,弄出一个可以自由使用的公共版本(openjd ...

  10. HTML5在手机端实现视频全屏展示

    最近做项目,遇到一个问题,在手机上要实现视频的全屏播放功能.测试了很久,终于找到解决办法. 第一种:将视频放大来控制. 视频在播放的时候,全屏是根据高度来的,如果设置视频 video 标签的宽度是 1 ...