LInux: fork()究竟是如何工作的?为何一个变量能够接受两个返回值?

LInux: fork函数究竟是如何工作的?为何一个变量能够接受两个返回值?

  • 前言
  • 一、fork()用法
  • 二 、fork()应用实例展示
  • 三、fork()工作原理
    • 3.1 为什么要创建子进程?
    • 3.2 fork()究竟干了些什么?
    • 3.3 fork为什么会存在两个返回值?
    • 3.5 为何fork函数中父进程返回子进程的pid、子进程返回0?
    • 3.5 父进程和子进程谁先运行?
    • 3.6 为何同一个变量接收两个返回值

前言

 在Linux中,创建进程有两种方式:

  1. 在命令行中直接启动进程。例如在命令行中输入pwd、tar等命令后,操作系统会直接加载,运行相应的进程。
  2. 通过代码创建进程 —— fork()函数。

 在Linux中,查看进程最常用的两种方式:

  1. 使用ps axj 或 ps aux指令查看进程。
  2. 使用ls /proc指令查看当前已加载到内存中所有的进程。

一、fork()用法

 fork()是一个系统调用接口函数

在这里插入图片描述

 fork()的原型是pid_t fork(void),其中pid_t是一个整型int的别名
 使用fork(),系统会以当前进程为模板,创建子进程(创建子进程时,OS会将当前进程的大部分属性来初始化子进程,而对于子进程的pid、ppid等属性则是单独实现)。同时对于父进程,fork()函数会返回子进程的pid;而对于子进程来说,fork()函数直接返回0!并且文档中明确表示fork()函数创建进程通常是成功的,所有返回值为负数一般忽略。

二 、fork()应用实例展示

 下面我们在process.c中有这样一段代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main()
{//getpid()、getppid()都是系统调用,分别返回当前进程的pid和ppid
     printf("我是一个进程,我的pid:%d, ppid:%d\n", getpid(), getppid());
	 pid_t id = fork();
     if(id == 0)
     {
       while(1)
        {
             printf("我是子进程,我的pid:%d, ppid:%d\n", getpid(), getppid()); 
             sleep(1);
         }
     }
     else if(id > 0)
     {
         while(1)
         {
            printf("我是父进程,我的pid:%d, ppid:%d\n", getpid(), getppid());
             sleep(1);
         }
     }
     else
     {//返回失败
     	return 1:
     }
     return 0;
}

 我们用Makefile、make对上述代码进行编译、运行看看会发生什么?

【动画展示】:
请添加图片描述

 我们发现fork()前的代码只执行了一次(一定是父进程执行),但fork()后的两个判断条件中的代码都执行了(即两个while循环)这也进一步说明了fork()后,操作系统会创建一个子进程!!

tips:

  • 上述展示视频中左边是一个监视进程的脚本,可以不断循环打印目标进程的相关属性信息。脚本如下:(如果需要监视其他进程信息,只需将脚本中的myprocess改成目标可执行程序即可!
while :; do ps axj | head -1 && ps axj | grep myprocess | grep -v "grep"; sleep 1; echo "#########################################################"; done

三、fork()工作原理

3.1 为什么要创建子进程?

父进程创建子进程通常是希望子进程来协助父进程来完成一些工作,这些工作是单进程无法实现的。

 例如:用户在下载某一款软件时,同时存在播放该款软件的官方宣传视频的需求。这就需要子进程来协助父进程来达到边下载边播放的目的。即通过一定手段(通常时fork()的返回值),让父子进程分别执行不同的代码!

3.2 fork()究竟干了些什么?

 fork()创建进程后,OS中会多一个进程(子进程)。在创建过程中,Linux操作系统会为子进程创建对应的PCB,并用父进程的大部分属性来初始化子进程的相关属性(如子进程的pid、ppid、所在路径等属性信息则是单独实现)。最后将该进程链入到运行队列中,等待CPU的调度!!

 而进程 = 代码 + 内核数据结构和数据,并且进程间时是相互独立的。而进程中的代码和数据等是操作系统在创建该进程时,从磁盘上加载拷贝到内存中的。但创建的子进程是直接在内存中创建的,子进程并没有相应的代码和数据,那要怎么解决这个问题呢?

 实际上,代码只是用于读取的。所以Linux中fork()创建的子进程和父进程共用同一段代码。但对于数据来说,父/子进程间必然会存在差异(比如pid、ppid等)。同时为了保证父/子进程间的独立性,必须在父/子进程中各自独立私有一份。而在Linux中采用了写时拷贝的方式来解决这个问题

 下一个问题就是为啥我们在前面动画展示中,fork()创建出的子进程只执行fork()后的代码?在fork()前的代码子进程能否“看见”呢?

 答案是子进程能看见fork()前的所有代码!至于为啥子进程只执行fork()后的代码,这是由于代码运行过程中,存在诸如epi、pc等寄存器。这些寄存器中会保存当前指令要执行的下一条指令的地址。而fork()创建子进程过程中,父进程中pc、epi等寄存器的结果也“继承”给了子进程。所以才出现子进程只运行执行fork()函数后的代码!

3.3 fork为什么会存在两个返回值?

 fork()是一个函数,其存在返回值。fork()在执行是,操作系统内核做了如下工作:分配新的内存块和数据结构给子进程、将父进程的部分内核数据结构拷贝给子进程、添加子进程到系统进程列表中,调度器开始调度。

fork()函数执行完后,已经完成了创建子进程、将子进程添加到调度队列中等工作。当父进程和子进程在调度队列中被调度时,两个进程都需要执行return语句,都需要返回一个值!所以fork存在两个返回值!(操作系统通过寄存器来实现返回值返回两次)(真正原因其实在于地址空间的实现)

3.5 为何fork函数中父进程返回子进程的pid、子进程返回0?

 对于一个进程,其父进程是唯一确定的,但其子进程可能存在多个。就像我们生活中,一个孩子的爹是唯一确定的;但对于一个父亲,其可能存在多个孩子。
 而父进程和子进程之间是管理和被管理的关系。父进程为了更好的管理好子进程,所以fork函数在创建子进程后返回子进程pid;对于子进程来说,其只需管理好自身即可,所以返回0。

3.5 父进程和子进程谁先运行?

 我们已经看到了fork()函数会创建一个子进程。创建完子进程后,子进程会被加载链接到系统进程列表中等待CPU调度运行。
 至于父进程和子进程谁先被CPU调度是不确定的。进程被调度的先后顺序由各自的PCB中的调度信息(时间片 + 优先级等)+ 调度器算法确定。换句话说,父进程和子进程谁先运行是由操作系统决定的!

3.6 为何同一个变量接收两个返回值

 我们前面已经提到过了进程是相互独立的,为例保存fork()创建出的子进程和父进程之间的独立性,我们所采用的解决办法是:代码共享,数据采用写时拷贝的方式在父进程和子进程中各自维护一份。
 我们知道在执行pid_t id = fork();语句时,本质上是将fork()的返回值写入变量id。而变量id是父进程创建的,而fork()返回时发生了写时拷贝,所以同一个变量会有两个返回值!

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

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

相关文章

文件上传漏洞-黑名单检测

黑名单检测 一般情况下&#xff0c;代码文件里会有一个数组或者列表&#xff0c;该数组或者列表里会包含一些非法的字符或者字符串&#xff0c;当数据包中含有符合该列表的字符串时&#xff0c;即认定该数据包是非法的。 如下图&#xff0c;定义了一个数组$deny_ext array(.a…

如何在Linux系统部署ONLYOFFICE协作办公利器并实现多人实时编辑文档

文章目录 1. 安装Docker2. 本地安装部署ONLYOFFICE3. 安装cpolar内网穿透4. 固定OnlyOffice公网地址 本篇文章讲解如何使用Docker在本地服务器上安装ONLYOFFICE&#xff0c;并结合cpolar内网穿透实现公网访问。 Community Edition允许您在本地服务器上安装ONLYOFFICE文档&…

DSVPN实验报告

一、分析要求 1. 配置R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;所有地址均配为公有IP地址。 - 在R5上&#xff0c;将接口配置为公有IP地址&#xff0c;并确保只进行了IP地址配置。 2. R1和R5之间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff1b;R2于R5之间…

Figma使用问题(更新自己遇到的问题)

文章目录 前言一、如何安装插件&#xff1f;方法1&#xff1a;Figma Community / Figma中文社区方法2&#xff1a;菜单栏 二、图片倾斜插件使用1.Angle Mockups前提&#xff1a;执行过程&#xff1a; 三.中文字体插件&#xff08;宋体等&#xff09;Chinese Font Picker前提&am…

【算法题】三道题理解算法思想——二分查找算法

二分查找算法 本篇文章中会带大家从零基础到学会利用二分查找的思想解决算法题&#xff0c;我从力扣上筛选了三道题&#xff0c;难度由浅到深&#xff0c;会附上题目链接以及算法原理和解题代码&#xff0c;希望大家能坚持看完&#xff0c;绝对能有收获&#xff0c;大家有更好…

阿里云2核4G服务器租用价格,支持多少人在线?

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

[项目实践]---RSTP生成树

[项目实践] 目录 [项目实践] 一、项目环境 二、项目规划 三、项目实施 四、项目测试 |验证 ---RSTP生成树 一、项目环境 Jan16 公司为提高网络的可靠性&#xff0c;使用了两台高性能交换机作为核心交换机&#xff0c;接入层交 换机与核心层交换机互联&#xff0c;形成冗…

数据恢复宝典:揭秘分区合并后的数据拯救之路

在计算机存储管理中&#xff0c;分区合并是一项常见的硬盘操作。它通过将两个或多个相邻的磁盘分区合并成一个更大的分区&#xff0c;来扩展存储空间或简化磁盘管理。然而&#xff0c;这个看似简单的操作背后&#xff0c;却隐藏着数据丢失的巨大风险。许多用户在尝试分区合并时…

【Linux系统】信号量实现同步和互斥

一.回顾 在这之前已经讲解了System V版本的信号量&#xff0c;主要内容为以下3点&#xff1a; 信号量本质是一把计数器申请信号量本质就是预订资源PV操作(申请和释放)是原子的 今天我们要学习的是POSIX版本的信号量&#xff0c;以上三点同样遵循 二.信号量VS互斥锁 1.联系&…

蓝桥杯23年第十四届省赛真题-三国游戏|贪心,sort函数排序

题目链接&#xff1a; 1.三国游戏 - 蓝桥云课 (lanqiao.cn) 蓝桥杯2023年第十四届省赛真题-三国游戏 - C语言网 (dotcpp.com) 虽然这道题不难&#xff0c;很容易想到&#xff0c;但是这个视频的思路理得很清楚&#xff1a; [蓝桥杯]真题讲解&#xff1a;三国游戏&#xff0…

OpenHarmony系统开发之应用接口文件转换工具介绍

简介&#xff1a; 应用接口文件转换工具是根据异构格式接口文件(.h 文件)转换生成 OpenHarmony 系统应用层需要的 TS(type-script)接口文件(*.d.ts)的工具。若某个服务实现方式为 c&#xff0c;且供应用层访问的接口已在.h 文件中定义&#xff0c;此时&#xff0c;NAPI 接口开…

23年蓝桥杯javaB组

23年蓝桥杯java-b组 前言&#xff1a; 23年蓝桥杯当时并没有参加&#xff0c;不过打算参加24年的蓝桥杯&#xff0c;于是打算复习下23年的题目&#xff0c;哦&#xff0c;不做不知道&#xff0c;做了几道题后评价一下&#xff0c;真的是老&#x1f437;上&#x1f3e0;&#…

13 完全分布式搭建-集群配置

1.集群部署规划 NameNode 和 SecondaryNameNode 不要安装在同一台服务器 ResourceManager 也很消耗内存&#xff0c;不要和 NameNode、SecondaryNameNode 配置在 同一台机器上。 在文章中与教材上有区别&#xff0c;在理论课上已讲解。 masterslave01slave02HDFS NameNode D…

HashMap关键源码带读

文章目录 目录 文章目录 前言 1 . 成员变量 灵魂五问 第一问: 默认初始化容量为啥是16? 第二问: 最大容量为什么必须是2的幂? 第三问: 链表转红黑树的阈值为什么是8? 第四问: 红黑树转链表的阈值为什么是6? 第五问: 默认加载因子为什么是0.75? 2. 成员方法 eq…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记15:PWM输出

系列文章目录 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记01&#xff1a;赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记02&#xff1a;开发环境安装 嵌入式|蓝桥杯STM32G431&#xff08;…

Linux:TCP协议的三次握手和四次挥手

文章目录 三次握手四次挥手为什么要进行三次握手&#xff1f;三次握手也不安全 本篇解析的主要是TCP的三次握手和四次挥手的过程 三次握手 如图所示&#xff0c;在TCP要进行链接的时候&#xff0c;其实是要进行三次握手的 第一次握手是指&#xff0c;此时客户端要给服务器发送…

AI学习-Pandas数据处理分析

文章目录 1. Pandas概述2. Series用法2.1 Series的创建2.2 Series的取值2.3 Series的相关方法 3. DataFrame用法3.1 DataFrame创建3.2 DataFrame取值3.3 DataFrame相关方法 1. Pandas概述 ​ Pandas 是一个开源的数据分析处理库&#xff0c;它应用在数据科学、统计分析、机器学…

python中的deque详解

文章目录 摘要示例1&#xff1a;基本使用示例2&#xff1a;使用maxlen限制队列长度示例3&#xff1a;使用deque实现滑动窗口算法示例 4: 使用 deque 实现旋转数组示例 5: 使用 deque 实现最大/最小栈示例 6: 使用 deque 实现广度优先搜索&#xff08;BFS&#xff09; 摘要 deq…

力扣56. 合并区间

Problem: 56. 合并区间 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.将数组按内部的一维数组的第一项按从小到大的顺序排序&#xff1b; 2.创建二维结果数组merged&#xff0c;并将排序后的数组中的第一个一维度数组存入到merged中&#xff1b; 3.从后面的一…

理解游戏服务器架构-部署架构

目录 前言 我所理解的服务器架构 什么是否部署架构 部署架构的职责 进程业务职责 网络链接及通讯方式 与客户端的连接方式 服务器之间连接关系 数据落地以及一致性 数据库的选择 数据访问三级缓存 数据分片 读写分离 分布式数据处理 负载均衡 热更新 配置更新 …