Linux系统——fork()函数详解(看这一篇就够了!!!)_fork函数-程序员宅基地

技术标签: linux  

fork()函数详解(包看包会!!!)

1.fork()简介

函数原型:

  • pid_t fork(void);//pid_t为int类型,进行了重载
  • pid_t getpid();// 获取当前进程的 pid 值。
  • pid_t getppid(); //获取当前进程的父进程 pid 值。

用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化;
在这里插入图片描述
如图所示 :我们将A 进程, 也就是调用 fork 的进程称之为父进程, 而新的进程(B 进程)称之为子进程

我们来看个例子:

int main()
{
    
	pid_t fpid; //fpid表示fork函数返回的值
	int count = 0;
	fpid = fork();
	if (fpid < 0)
		printf("error in fork!");
	else if (fpid == 0) {
    
		printf("i am the child process, my process id is %d/n", getpid());
		printf("我是爹的儿子/n");//对某些人来说中文看着更直白。
		count++;
	}
	else {
    
		printf("i am the parent process, my process id is %d/n", getpid());
		printf("我是孩子他爹/n");
		count++;
	}
	printf("统计结果是: %d/n", count);
	return 0;
}

输出结果:
i am the child process, my process id is 5574
我是爹的儿子
统计结果是: 1
i am the parent process, my process id is 5573
我是孩子他爹
统计结果是: 1

为什么两个进程的fpid不同呢,这与fork函数的特性有关。

2.fork()特性

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

  1. 在父进程中,fork返回新创建子进程的进程ID;
  2. 在子进程中,fork返回0;
  3. 如果出现错误,fork返回一个负值;

因此我们可以通过fork返回的值来判断当前进程是子进程还是父进程。(注: fork 调用生成的新进程与其父进程谁先执行不一定,哪个进程先执行要看系统的进程调度策略

举个例子来解释fpid的值为什么在父子进程中不同:“相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.

看到这里大家对fork()有个大致了解了,让我们来看个例题:

int main(void)
{
    
	int i = 0;
	printf("i son/pa ppid pid  fpid/n");
	//ppid指当前进程的父进程pid
	//pid指当前进程的pid,
	//fpid指fork返回给当前进程的值
	for (i = 0; i<2; i++){
    
		pid_t fpid = fork();
		if (fpid == 0)
			printf("%d child  %4d %4d %4d/n", i, getppid(), getpid(), fpid);
		else
			printf("%d parent %4d %4d %4d/n", i, getppid(), getpid(), fpid);
	}
	return 0;
}

运行结果是:
i son/pa ppid pid fpid
0 parent 2043 3224 3225
0 child 3224 3225 0
1 parent 2043 3224 3226
1 parent 3224 3225 3227
1 child 1 3227 0
1 child 1 3226 0

我们来一步步分析:

第一步: 在父进程中,指令执行到for循环中,i=0,接着执行fork,fork执行完后,系统中出现两个进程,分别是p3224和p3225(后面我都用pxxxx表示进程id为xxxx的进程)。可以看到父进程p3224的父进程是p2043,子进程p3225的父进程正好是p3224。

我们用一个链表来表示这个关系: p2043->p3224->p3225

第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id)

所以打印出结果:
0 parent 2043 3224 3225
0 child 3224 3225


第二步: 假设父进程p3224先执行,当进入下一个循环时,i=1,接着执行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。
对于子进程p3225,执行完第一次循环后,i=1,接着执行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出可以看到p3225原来是p3224的子进程,现在变成p3227的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了fork,该进程就变成了父进程了,就打印出了parent。

所以打印出结果是:
1 parent 2043 3224 3226
1 parent 3224 3225 3227


第三步: 第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。

以下是p3226,p3227打印出的结果:
1 child 1 3227 0
1 child 1 3226 0


这里不免有个问题:到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程,在p3224和p3225执行完第二个循环后,main函数就该退出了,也即进程该死亡了,因为它已经做完所有事情了。p3224和p3225死亡后,p3226,p3227就没有父进程了,这在操作系统是不被允许的,所以p3226,p3227的父进程就被置为p1了,p1是永远不会死亡的。

3.fork()的执行过程

  1. 申请PID
  2. 申请PCB结构
  3. 复制父进程的PCB
  4. 将子进程的运行状态设置为不可执行的
  5. 将子进程中的某些属性清零,某些保留,某些修改
  6. 复制父进程的页(用到了写时拷贝技术)

写实拷贝技术: 父子进程在初始阶段共享所有的数据(全局、 栈区、 堆区、 代码), 内核会将所有的区域设置为只读。 当父子进程中任意一个进程试图修改其中的数据时, 内核才会将要修改的数据所在的区域(页) 拷贝一份。

画个图就很好理解了:
在这里插入图片描述

写时拷贝后:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cckluv/article/details/109169941

智能推荐

Latex常用命令_latex命令合集-程序员宅基地

文章浏览阅读431次。插入公式中的连字符\operatorname_latex命令合集

【Tensorflow+Keras】tf.keras.layers.LSTM的解析与使用-程序员宅基地

文章浏览阅读2.1w次,点赞24次,收藏96次。1 作用原理实现LSTM网络原理介绍:长短期记忆模型(Long-Short Term Memory,LSTM)2 参数tf.keras.layers.LSTM(units,activation=“tanh”,recurrent_activation=“sigmoid”,#用于重复步骤的激活功能use_bias=True,#,是否图层使用偏置向量kernel_initializer=“glorot_uniform”,#kernel权重矩阵的 初始化程序,用于输入的线性转换recurren_tf.keras.layers.lstm

《操作系统实验》C++实现生产者-消费者问题_用线程模拟实现生产者和消费者的程序。缓冲区大小为5(可以定义数组来作为存储产品-程序员宅基地

文章浏览阅读1w次,点赞14次,收藏178次。生产者-消费者问题1 实验内容及要求2 实验环境3 实验设计3.1 问题描述3.2 基本思想3.2.1 生产者线程3.2.2 消费者线程3.2.3 同步的实现3.3 数据结构4 实验源码5 实验总结1 实验内容及要求1、模拟生产者—消费者问题;2、程序要添加适当的注释,程序的书写要采用缩进格式;3、程序要具在一定的健壮性,即当输入数据非法时,程序也能适当地做出反应;4、程序要做到界面友好,在程序运行时用户可以根据相应的提示信息进行操作。2 实验环境windows 10,Visual Studi_用线程模拟实现生产者和消费者的程序。缓冲区大小为5(可以定义数组来作为存储产品

#####Docker私有仓库的搭建和加认证####_registry-cli-程序员宅基地

文章浏览阅读328次。1.下载registry镜像[root@server1 docker]# docker pull registry:2: Pulling from library/registryc87736221ed0: Pull complete 1cc8e0bb44df: Pull complete 54d33bcb37f5: Pull complete e8afc091c171: Pull c..._registry-cli

java nio系列教程(2)---channel(通道介绍)和使用_messageutil.bytebuffertoarray-程序员宅基地

文章浏览阅读2.3k次。大家推荐个靠谱的公众号程序员探索之路,公众号内点击网赚获取彩蛋,大家一起加油 ​package com.zzh.buffer;import com.google.common.collect.Lists;import org.junit.jupiter.api.Test;import java.io.FileInputStream;import java.io.FileOut..._messageutil.bytebuffertoarray

官宣 | Apache Flink 1.12.0 正式发布,流批一体真正统一运行!_cannot resolve org.apache.flink:flink-web-socket:1-程序员宅基地

文章浏览阅读5.7k次,点赞9次,收藏10次。‍‍翻译 | 付典Review |徐榜江、朱翥Apache Flink 社区很荣幸地宣布 Flink 1.12.0 版本正式发布!近 300 位贡献者参与了 Flink 1.12.0 ..._cannot resolve org.apache.flink:flink-web-socket:1.12.0

随便推点

ucore lab7_ucorelab7-程序员宅基地

文章浏览阅读541次。练习1: 理解内核级信号量的实现和基于内核级信号量的哲学家就餐问题(不需要编码)比较Lab6和Lab7,发现区别主要在/kern/sync这个文件夹,其中就是实现信号量的一些代码。Baidu百科哲学家就餐问题可以这样表述,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两_ucorelab7

数学表达式学习一_数学表达式i∈1到n-程序员宅基地

文章浏览阅读244次。学数学,吃透概念理论确实是基本的,但更要能精确表达出来,数学表达式,在此之前其实没什么接触,在闵老师的介绍之下,对论文的书写是必不可少,所以从陌生到熟悉到掌握,这个流程得一步一脚印,踏实走,走出舒适区,把文字转化成符号,转变心态,目前的问题就是不熟悉各字体的符号,相同字体的不同形态的使用,上下标什么时候标识等基本的,所以一边用一边学,照猫画虎。_数学表达式i∈1到n

java小白,基础薄弱,必看_java基础薄弱怎么办-程序员宅基地

文章浏览阅读159次。本教程涵盖了Java基础入门部分-基础进阶部分-项目实战部分,环环紧扣,循序渐进,是学习Java的不二路径,小白零基础都可以入手。基础开发环境开发工具:Eclipse(MyEclipse、idea、sts)我这里用的是Eclipse(你们应该也是用的这个,是吧?)Java版本:JDK 1.8 Tomcat7数据库:MySQL 5.7 数据库编译工具navicat一共三部分:基础入门(基础入门(以下二选一即可))入门视频教程1(精讲,耗时短,快速入门)48讲Java入门视频教程及课堂源码入门_java基础薄弱怎么办

We’re sorry but XXX doesn’t work properly without JavaScript enabled(解决方案汇总)_doesn't work properly without javascript enabled-程序员宅基地

文章浏览阅读10w+次,点赞16次,收藏25次。我遇到的问题:本地开发环境,访问接口状态为200,但没有正确但返回数据,返回信息提示为“We’re sorry but template doesn’t work properly without JavaScript enabled. Please enable it to continue.”网上搜索的解决方法没有解决问题,最终是在服务代理发现了问题,代理信息写错了,修改代理信息就改好了。以下汇总了其他的情况的解决方法1、mode类型前端修改方式:将mode类型由history改成hash;_doesn't work properly without javascript enabled

鸿蒙系统与应用场景,华为鸿蒙操作系统正式发布-程序员宅基地

文章浏览阅读627次。原标题:华为鸿蒙操作系统正式发布深圳湾现场报道,2019 年 8 月 9 日,华为全球开发者大会在东莞正式举行,会上,华为正式发布操作系统,这是一个基于微内核、面向全场景的分布式操作系统。随着华为全场景智慧生活战略的不断完善,鸿蒙 OS 将作为华为迎接全场景体验时代到来的产物,发挥其轻量化、小巧、功能强大的优势,率先应用在智能手表、智慧屏、车载设备、智能音箱等智能终端上,着力构建一个跨终端的融合共..._鸿蒙操作系统发布

HTNL5--<video>/<audio>随笔-程序员宅基地

文章浏览阅读174次。WHATWG 致力于 web 表单和应用程序,而 W3C 专注于 XHTML 2.0。在 2006 年,双方决定进行合作,来创建一个新版本的 HTML——HTML5。为 HTML5 建立的一些规则:新特性应该基于 HTML、CSS、DOM 以及 JavaScript。减少对外部插件的需求(比如 Flash)更优秀的错误处理更多取代脚本的标记HTML5 应该独..._var myvideo = document.getelementbyid("video");