【Linux】理解父子进程(系统调用创建进程,fork函数,写时拷贝)

       

目录

fork函数

返回值

内存分配


        父子进程是操作系统一个重要的概念,特别是在多任务处理和并发编程中,在Linux中,每个进程都有一个唯一的进程ID,并且每个进程都有可能创建其他进程。当一个进程创建了一个新的进程时,新创建的进程就成为了原始进程的子进程。

        同样用生活中的例子来理解。在一个家庭中(类比一个操作系统中),父母决定做晚饭(做饭就是一个进程),他们分配给孩子一个任务,让大儿子洗菜,小儿子烧水(洗菜和烧水就是两个子进程),父母和孩子之间相互协作,共同完成了这一顿晚饭,这就是父子进程之间相互协作独立执行任务的特性。

fork函数

“fork()”函数是在Linux中用于创建新进程的系统调用之一。调用fork()函数时,操作系统会创建当前进程的一个副本,即子进程。

函数原型

#include <unistd.h>
pid_t fork(void);

fork函数的定义在unistd.h头文件中完成。

返回值

考虑一个问题:fork函数的返回值是什么呢?我们用下面这段代码来验证一下

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
 {
    int ret = fork();
    printf("hello proc : %d!, ret: %d\n", getpid(), ret);
    sleep(1);
    return 0;
 }

结果如下:

诶?明明应该只有一行输出结果,为什么这里会有两行呢,而且结果还不一样

事实上,这两行输出就是父子进程并发执行的结果。第一行是父进程的运行结果,此时的进程id(PID)是“65781”,fork的返回值是子进程id(PPID)“65782”;第二行是子进程的运行结果,fork返回值是“0”。

下面这段代码就可以更清晰地观察父子进程:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
 {
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret == 0){ //child
        printf("I am child : %d!, ret: %d\n", getpid(), ret);
    }else{ //father
        printf("I am father : %d!, ret: %d\n", getpid(), ret);
    }
    sleep(1);
    return 0;
 }

运行结果如下: 

内存分配

        在fork函数调用之后,父进程和子进程都将拥有相同的内存空间映像,但它们是相互独立的。这意味着,当其中一个进程修改了内存中的数据,另一个内存不会受到影响。那么这是如何实现的呢?如果父子进程都指向同一块内存空间,那么数据的修改会互相影响,所以它们是指向两块不同的空间,只是这两块空间存放相同的数据,是这样吗?nonono,系统用了一个很聪明的方法,那就是写时拷贝。

写时拷贝是一种延迟复制技术,它使得在fork函数中进行子进程的创建时,实际上并不立即复制父进程的内存空间,而是等到子进程尝试修改其中某个页面时才进行复制。这样可以减少内存开销,提高效率,特别是当父子进程在大部分时间内只读取数据而不修改时。下面用一张图来加深理解:

 下面用代码来验证fork函数的写时拷贝:

#include <stdio.h>
#include <unistd.h>

int main() {
    int x = 1;
    pid_t pid = fork();

    if (pid == 0) {
        // 子进程
		sleep(1);
		printf("x的值:%d,x的地址:%p\n",x,&x);
		x = 10; // 修改子进程的 x 值
    } else if (pid > 0) {
        // 父进程
		x = 20; // 修改父进程的 x 值
		sleep(2);
		printf("x的值:%d,x的地址:%p\n",x,&x);
    } else {
        // fork() 调用失败
        fprintf(stderr, "fork() failed\n");
        return 1;
    }

    return 0;
}

这里的sleep()函数是为了达到 父进程修改变量-->显示子进程信息,子进程修改变量-->显示父进程信息 的进程效果,如果父子进程之间变量的修改没有互相影响,就说明进行了写时拷贝。

来看结果:

可以看出父子进程对x的修改并没有影响到对方,但是为什么这里x的地址是相同的呢?

因为在fork函数调用后,操作系统为父子进程分配了不同的栈空间,因此它们各自的变量‘x’都位于不同的栈空间之中。x的地址实际上是相对于各自的栈空间的偏移量,并不是指向相同的物理内存地址。实际上它们所指的是不同的物理内存地址。

换句话说,虽然地址看起来相同,但实际上它们处于不同的内存空间,所以这并不违背写时拷贝的原理。

一上就是父子进程与fork函数的相关知识了,欢迎在评论区留言,觉得俺的博客对你有帮助的可以点赞关注支持一波窝~😉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/493770.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程>环境变量地址空间进程调度

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.环境变量 1.1 基本概念 1.2 常见环境变量 1.3 查看环境变量方法 1.4 和环境变量相关的命令 1.5 环境变量的组织方式 1.6 通…

最“原始”的收音机长啥样?

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

vs code

vs code 下载安装 https://code.visualstudio.com/https://code.visualstudio.com/ 下载完后&#xff0c;下一步下一步就安装完了&#xff0c;安装好后可以下载各种好用的插件

【前端面试3+1】03深拷贝浅拷贝、let和var、css盒模型、【有效括号】

一、深拷贝浅拷贝 深拷贝和浅拷贝都是用于复制对象或数组的概念&#xff0c;但它们之间有着重要的区别&#xff1a; 1. 浅拷贝&#xff1a; 浅拷贝是指在拷贝对象或数组时&#xff0c;只会复制一层对象的属性或元素&#xff0c;而不会递归地复制嵌套的对象或数组。因此&#xf…

2024年第十二届计算机与通信管理国际会议(ICCCM 2024)即将召开!

2024年第十二届计算机与通信管理国际会议&#xff08;ICCCM 2024&#xff09;将2024年7月19-21日在日本鹿儿岛召开。会议由鹿儿岛大学主办。此次会议旨在为业界建立一个广泛、有效的交流合作平台&#xff0c;让我们及时了解行业发展动态、掌握最新技术&#xff0c;拓宽研究视野…

修改Jupyter Notebook的默认路径,以及在PowerShell中自定义其启动路径

修改Jupyter Notebook的默认路径&#xff0c;以及在PowerShell中自定义其启动路径 设置 Jupyter Notebook 配置文件&#xff0c;修改默认路径要在PowerShell中设置自定义的启动脚本&#xff0c;以确保Jupyter Notebook能够自动定位到当前路径设置后的效果 在使用Jupyter Notebo…

【C语言】编译和链接

文章目录 一、编译环境和运行环境二、翻译环境2.1 预处理2.2 编译2.2.1 词法分析2.2.2 语法分析2.2.3 语义分析 2.3 汇编2.4 链接 三、运行环境 一、编译环境和运行环境 在ANSIC的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代…

C++ STL教程

C STL教程 文章目录 C STL教程1.1 std::vector1.1.1vector的定义1.1.2vector容器的初始化1.1.3vector容器内元素的访问和修改1.1.4vector中的常用函数 1.2 std::string1.2.1string的定义1.2.2string的初始化1.2.3string中元素的访问和修改1.2.4string中连接字符串1.2.5string中…

阿里云服务器安装MySQL(宝塔面板)

只写关键步骤 1. 创建一个云服务器实例 2 修改密码&#xff0c;登录服务器 3. 安装宝塔面板 进入https://www.bt.cn/new/index.html 进入宝塔面板地址 4. 安装Mysql 5. 创建数据库&#xff08;可导入数据库&#xff09; 6. 测试连接数据库 打开Navicat&#xff08;或其他数据…

[Qt] QString::fromLocal8Bit 的使用误区

QString::fromLocal8Bit 是一个平台相关的函数。默认情况下在 Windows 下 就是 gbk 转 utf-8 ,在 Linux就应该是无事发生。因为Linux平台默认的编码方式就是 utf-8 可以通过 void QTextCodec::setCodecForLocale(QTextCodec *c)来修改 Qt默认的编码方式。如下 第一输出乱码的…

DreamPolisher、InternLM2 、AniArtAvatar、PlainMamba、AniPortrait

本文首发于公众号&#xff1a;机器感知 DreamPolisher、InternLM2 、AniArtAvatar、PlainMamba、AniPortrait DreamPolisher: Towards High-Quality Text-to-3D Generation via Geometric Diffusion We present DreamPolisher, a novel Gaussian Splatting based method wit…

kubernetes负载均衡资源-Ingress

一、Ingress概念 1.1 Ingress概念 使用NodePort类型的Service可以将集群内部服务暴露给集群外部客广端,但使用这种类型Service存在如下几个问题。 1、一个端口只能一个服务使用,所有通过NodePort暴露的端口都需要提前规划;2、如果通过NodePort暴露端口过多,后期维护成本太…

中国土壤厚度空间分布数据

土壤层次分为覆盖层 林溶层 淀积层 母质层&#xff0c;其中在林溶层中的最上面那层就是我们通常说的土壤厚度在这一层中&#xff0c;这一层也被称为腐殖层&#xff0c;是肥力性质最好的一层&#xff0c;植物根系和微生物也集中在这一层。至于覆盖层在森林土壤中比较常见&#x…

【LeetCode: 2580. 统计将重叠区间合并成组的方案数 + 合并区间】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

计算机组成原理 3 运算器

定点补码加/减法运算 补码加减法的实现 补码加法 &#xff1a; [X &#xff0b; Y] 补 [X] 补 &#xff0b; [Y] 补 和的补码 补码的和 补码减法 &#xff1a; [X−Y] 补 [X] 补 &#xff0b; [−Y] 补 [X] 补 −[Y] 补 差的补码 补码的差 求补公式 &#xff1a; [−…

qemu快速入门

1.环境 win10系统上 -》 通过vmware装 -》 CentOS 7.4 -》装qemu虚拟出一台指定cpu的CentOS 7.9 2.安装基本命令 yum install -y net-tools yum install -y wget 3.安装基础依赖 yum groupinstall Development Tools -y yum groupinstall "Virtualization Host"…

MySQL数据库高级语句

文章目录 MySQL高级语句older by 排序区间判断查询或与且&#xff08;or 与and&#xff09;嵌套查询&#xff08;多条件&#xff09;查询不重复记录distinctcount 计数限制结果条目limit别名as常用通配符嵌套查询&#xff08;子查询&#xff09;同表不同表嵌套查询还能用于删除…

Redis中的客户端(一)

客户端 概述 Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接&#xff0c;每个客户端可以向服务器发送命令请求&#xff0c;而服务器则接收并处理客户端发送的命令请求&#xff0c;并向客户端返回命令回复。通过使用由IO多路复用技术实现的文件…

C++ explicit隐式类型转换

单参数构造函数支持隐式类型的转换 什么意思&#xff1f; 简单来理解就是&#xff1a; 一个类对象的构造函数的参数只有一个&#xff0c;就可以直接进行赋值传参 例如构造函数的参数为int&#xff0c;且只有一个int 就可以直接将int类型的整型数据转换成类对象 也就是说从int类…

MySQL中的日历/时间/时间戳

一&#xff0c;日历 MySQL 使用通常所说的 proleptic 阳历。 每个将日历由朱利安改为阳历的国家在改变日历期间都不得不删除至少10天。 为了了解其运作&#xff0c;让我们看看1582年10月&#xff0c;这是由朱利安日历转换为阳历的第一次: 周一 周二 周三 周四 周五 周六…