fork函数详解_全纯函数是什么

fork函数详解_全纯函数是什么从最简单(基础)的一个例子说起,应该说是最基础而不是简单,下面的这个最基础的例子其实并不简单,因为有很多细节。我们需要从fork函数的定义开始说起:man手册官方定义thisfunctioncreatesanewprocess.Thereturnvalueisthezerointhechildandtheprocess-idnumberofthechildintheparent,or-1uponerror.这个函数创建一个新的进程。在子进

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

从最简单(基础)的一个例子说起,应该说是最基础而不是简单,下面的这个最基础的例子其实并不简单,因为有很多细节。
我们需要从fork函数的定义开始说起:

man 手册官方定义
this  function  creates a new process. The return value is the zero in
the child and the process-id number of the child in the parent,
or -1 upon error.
这个函数创建一个新的进程。在子进程中返回0,在父进程中返回子进程的进程id,发生错误则返回-1。

Jetbrains全家桶1年46,售后保障稳定

第一次看的时候非常的奇怪,一个函数返回两次?是的,在调用fork后,fork函数后面的所有代码会执行两遍。下面通过一个例子来解释fork函数定义的含义。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/** *最基础的fork例子 **/
int main(int argc, char const *argv[])
{ 
   
    pid_t pid;
    //判断1
    if ((pid=fork()) < 0)
    { 
   
        perror("fork error");
    }
    //判断2
    else if (pid == 0)//子进程
    { 
   
         printf("child getpid()=%d\n", getpid());
    }
    //判断3
    else if(pid > 0)//父进程
    { 
   
        printf("parent getpid()=%d\n", getpid());
    }

    return 0;
}

这是一个最基本的例子。我们先运行一下代码。

parent getpid()=13725
child getpid()=13726

非常的神奇,两个判断的代码都执行了。这是非常不可思议的,但fork函数确实实现了这样的功能。也就是在fork函数后面的代码都会执行2遍。 这就是为什么两个判断都会被执行的原因。
现在来梳理一下成功fork的执行流程
第一步: pid=fork(),如果成功那么pid就有一个非0正值。否则返回-1。
第二步: 因为pid>0,所以进入判断3。这是在父进程。
第三步: 父进程的代码执行完了,程序又会把fork后面的函数再执行一遍,此时pid的值变为0,所以进入判断2。

这里要解释下getpid()函数,先看下他的定义:

man手册官方定义:
DESCRIPTION
       The getpid() function shall return the process ID of the calling process.

RETURN VALUE
       The getpid() function shall always be successful and no return value is reserved to in‐
       dicate an error.

getpid()获取调用他的进程的id,如果失败不会有返回值。

也就是说哪个进程调用getpid,就返回这个进程的pid。所以如果你想要获得子进程的pid,那么只要在判断2里面调用getpid就可以了。

令人迷惑的pid_t pid变量

还有一个需要解释的就是我们自己定义的这个pid_t pid变量。这个变量非常具有迷惑性。因为在很多书上都取这个名字,好像这个变量就是进程的pid。这是错误的。
这个变量的真正含义应该是return value of the fork(),也就是fork函数的返回值,而且返回值并不一定就是pid,也可能是错误值-1。
下面是这个变量的一种错误用法,试图用这个变量来输出父子进程的pid。

int main(int argc, char const *argv[])
{ 
   
    pid_t pid;
    //判断1
    if ((pid=fork()) < 0)
    { 
   
        perror("fork error");
    }
    //判断2
    else if (pid == 0)
    { 
   
        printf("child pid=%d\n",pid);
    }
    //判断3
    else
    { 
   
        printf("parent pid=%d\n",pid);
    }

    return 0;
}

这个一个错误的例子,程序的目的是试图通过pid变量来获取父子进程的pid。
输出结果:

parent pid=15077
child pid=0

这种做法是完全错误的,不要这么干!因为这个pid变量的命名实在是太有迷惑性了。判断2里面的pid会永远输出0,而判断3里面的pid并不是父进程的pid,实际上是子进程的pid。正确的做法是通过第一个例子的getpid函数来获取。

pid_t pid这个变量的唯一作用就是用来做三个条件判断。
pid_t pid这个变量的唯一作用就是用来做三个条件判断。
pid_t pid这个变量的唯一作用就是用来做三个条件判断。
不要拿他做别的事情。也许取名叫process_status会比较好。

父子进程的调用流程

前面的例子展示了fork最基本的用法。下面通过一个例子来解释fork函数的调用细节。

int main(){ 
   
	fork();//fork1
	fork();//fork2
	printf("hello\n");
	return 0;
}

问printf一共打印了几次?创建了几个子进程?
第一个问题非常好回答,执行一下程序就知道了。一共是输出了4次hello字符串。为什么是4次呢?可以做下面的图分析:
在这里插入图片描述

假设我们的main进程pid是1001,注意看左边的1,2,4进程其实都是main进程1001。进程3,6是同一个进程1002。所有一共有1001,1002,1003,1004四个进程。也就是只要数叶子节点就行了。其中1个是main进程,其它3个是子进程。有多少个进程就输出多少次hello字符串。也就是只有4,5,6,7执行了printf。

int main(){ 
   
	fork();//fork1
	fork();//fork2
	fork();//fork3
	printf("hello\n");
	return 0;
}

如果程序改成这样,结果是类似的,一共有8个进程,其中一个main进程,7个子进程。如果在程序最后加上sleep函数让进程一直存在,那么你可以在进程管理器里面查看到对应的进程和pid,如下图。
在这里插入图片描述

进程管理

既然生成了子进程,那么就需要管理这些子进程,那么谁来管呢?当然是谁生成谁负责。这其中有非常多的细节。看下面这个基本例子。通过getppid(有两个p)获取父进程的pid。

int main(){ 
   
	fork();//fork1
	fork();//fork2
	printf("ppid is %d\n",getppid());
	printf("hello\n");
	return 0;
}

输出结果

ppid =4564
hello 
ppid =26134
hello 
ppid =26135
ppid =26134
hello 
hello 

这个结果顺序是随机的,我们发现第一个ppid好像有点奇怪,另外三个pid都是差不多的。这个进程实际是main进程,他的parent是shell,因为我们的程序是在shell里面执行的。而shell的pid是4564(每次系统启动可能发生变化)。这还是比较好理解的。但有时候输出可能是下面这种情况。

ppid =4564
hello 
ppid =26570
ppid =26570
hello 
hello 
ppid =1
hello 

最后一个ppid是1,这是怎么回事呢?这是因为父进程在子进程结束之前先结束了。子进程没有了父进程,变成了孤儿进程。这时候init进程就会把这个孤儿进程收为他的“养子”,而init进程就成了孤儿进程的养父。在Linux系统中,init进程的id为1。这也就是ppid为1的原因。
可见父进程是没有办法在自己消亡的时候回收子进程的。

参考:Unix/Linux fork前传

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/220322.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Android浏览器调用APP「建议收藏」

    Android浏览器调用APP「建议收藏」有时我们想通过点击浏览器中某些广告链接来启动或下载APP,以启动APP来说,我们知道APP可以定义一个scheme,如果我们在浏览器中定义一个URL,这个URL使用定义的scheme,这样点击后我们就可以打开我们的客户端了,但目前市面上有些浏览器支持性不好,或者直接就不支持,认为这个打开是一个有害的链,那么我们还有没有其它的办法来结合,下面我们以web服务做为功能基础来实现我们知道如果在地址栏

  • 5G网络注册中的切片选择「建议收藏」

    5G网络注册中的切片选择「建议收藏」UE->gNB开机以后,UE会发送RequestedNSSAI注册申请,会带有请求的切片ID,即这次的注册我想请求哪些切片ID,发送给基站,gNB->InitialAMF基站RequestedNSSAI注册申请,基站会根据之前的(),去选择一个AMF,初始AMFInitaialAMF可以看到手机带上来的切片ID到底是什么(例如1号),3.InitialAMF<–>UDM同时InitaialAMF会拿手机UE的IMSI信息,在5G里面是SUPI

  • php微信自动回复机器人,微信自动回复机器人功能怎么实现?[通俗易懂]

    php微信自动回复机器人,微信自动回复机器人功能怎么实现?[通俗易懂]原标题:微信自动回复机器人功能怎么实现?微信自动回复机器人功能怎么实现?最近有不少小伙伴都在询问这个问题。很多人在微信营销的过程中,都会有这样的问题,微信好友太多,想要都在第一时间回复,实在没有精力。下面小编就给大家分享如何使用微信自动回复机器人,大家再也不用担心回复不过来而忙的焦头烂额啦。首先通过米云微信软件,扫码将所以微信号登录,就可以将所有微信号的对话集成,全部聊天里整合了所有微信号的聊天…

  • dedecms幻灯片调用图片模糊的解决办法

    dedecms幻灯片调用图片模糊的解决办法

  • APP抓包——Fiddler工具

    APP抓包——Fiddler工具Fiddler简介:Fiddler是强大且好用的Web调试工具之一,它能记录客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据。Fiddler的运行机制其实就是本机上监听8888端口的http代理。对于PC端Fiddler启动的时候默认IE的代理设为了127.0.0.1:8888,而其他浏览器是需要手动设置的,所以如果需要监听PC端Chrome网络请求,…

  • 安捷伦示波器连接电脑没反应_安捷伦示波器升级

    安捷伦示波器连接电脑没反应_安捷伦示波器升级调试或者测试电路的过程中,示波器是比不可少的东西,且常需要保存示波器的波形,可以通过一条网线把示波器与电脑连接起来,这样就可以在电脑上方便的保存波形图,怎么实现示波器和电脑连接,我们以安捷伦示波器DSO7052B为例,其他示波器类似。1.首先通过一条网线连接示波器和电脑,在电脑上找到:网络和共享中心->以太网状态->以太网属性->TCP/IPv4协议,点击属性2…

    2022年10月12日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号