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

news2024/11/27 14:56:11

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()返回时发生了写时拷贝,所以同一个变量会有两个返回值!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

opengl草稿复习,承上启下(一)

目录 1、链接文件夹中的cpp 2、链接资源到输出目录 3、多编译目标 4、cmakelist添加库 4、添加glfw和glad 5、glfw运行 6、NDC、VBO、VAO 7、渐变三角形 8、渲染两个三角形 9、渲染两个三角形&#xff0c;同时基于原来颜色进行渐变 10、三角形渲染模块化 11、纹理渲…

深度学习中的模型蒸馏技术:实现流程、作用及实践案例

在深度学习领域&#xff0c;模型压缩与部署是一项重要的研究课题&#xff0c;而模型蒸馏便是其中一种有效的方法。 模型蒸馏&#xff08;Model Distillation&#xff09;最初由Hinton等人在2015年提出&#xff0c;其核心思想是通过知识迁移的方式&#xff0c;将一个复杂的大模型…

适合新手小白的wordpress详细安装教程

1、下载程序 到wordpress官方网站下载wordpress程序&#xff0c;官方下载地址&#xff1a;Download | WordPress.org China 简体中文。 下载最新版的wordpress程序 https://cn.wordpress.org/latest-zh_CN.zip 2、上传程序 上传程序前先确认主机是否符合安装的环境要求&…

javaWeb项目-学生考勤管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JAVA技术 JavaSc…

PS从入门到精通视频各类教程整理全集,包含素材、作业等(3)

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 中级教程 https://www.alipan.com/s/unii5YxtM8B 提…

【JavaEE初阶系列】——多线程案例三——定时器

目录 &#x1f6a9;定时器是什么 &#x1f6a9;标准库中的定时器 &#x1f6a9;自定义定时器 &#x1f388;构造Task类 &#x1f4dd;相对时间和绝对时间 &#x1f388;构造MyTime类 &#x1f4dd;队列空和队列不为空 &#x1f4dd;wait(带参)解决消耗资源问题 &#…

方案研发公司服务的特点

一、服务特点&#xff1a; 1、有协助厂商在产品开发上解决问题的实践经验。 2、与国内半导体商合作&#xff0c;专营应用消费性IC&#xff0c;在供货上能以较有效率方式出货&#xff0c; 配合客户之需求。 3、长期从事专业的设计工作&#xff0c;能以较有效率方式、较专业的…

安达发|APS计划排产系统帮助纺织业实现企业数字化管理

APS&#xff08;高级计划排产系统&#xff09;是一种基于供应链管理和约束理论的计划排产工具&#xff0c;它通过模拟和优化企业的生产、物流等运作过程&#xff0c;帮助企业实现精细化管理。在纺织业中&#xff0c;APS的应用可以极大地推动企业数字化管理的进程&#xff0c;具…

【ROS 笔记1】Topic message通俗理解

前言: topic 能够将所有的独立的模块, 进行有序的交流,链接。 可以想象, roscore, 假设是一个铁路系统的总的开关,当打开总的开关(run roscore), 铁路路就可以畅通起来, 铁路畅通后, 如何进行北京站(机器人recognition)与上海站(机器人抓取)的交流。 那么我们可以从…

love 2d Lua 俄罗斯方块超详细教程

源码已经更新在CSDN的码库里&#xff1a; git clone https://gitcode.com/funsion/love2d-game.git 一直在找Lua 能快速便捷实现图形界面的软件&#xff0c;找了一堆&#xff0c;终于发现love2d是小而美的原生lua图形界面实现的方式。 并参考相关教程做了一个更详细的&#x…

第十四章 MySQL

一、MySQL 1.1 MySql 体系结构 MySQL 架构总共四层&#xff0c;在上图中以虚线作为划分。 1. 最上层的服务并不是 MySQL 独有的&#xff0c;大多数给予网络的客户端/服务器的工具或者服务都有类似的架构。比如&#xff1a;连接处理、授权认证、安全等。 2. 第二层的架构包括…

【2024系统架构设计】案例分析- 2 系统开发基础

目录 一 基础知识 二 真题 一 基础知识 1 结构化的需求分析 结构化特点:自顶向下,逐步分解,面向数据。 三大模型:

仓库规划(plan)

明天就要考试了&#xff0c;但是我正处于一点都不想学的状态 高考前我也是这样的 逆天 代码如下&#xff1a; #include<vector> #include<cstdio> using namespace std; int n, m; struct Node{int id;vector<int> d;bool operator<(const Node &t…

算法题2两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 开…

动态规划之方格取数

方格取数 动态规划&#xff0c;数字三角形模型 题目链接 https://www.luogu.com.cn/problem/P1004 题目描述 解法一 O ( n 4 ) O(n^4) O(n4) #include<bits/stdc.h> using namespace std; int n, i, j, l, k, x, y, s; int d[55][55], f[55][55][55][55]; int main()…

等保测评-Oracle数据库

安全计算环境 身份鉴别 a)应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换 select limit from dba_profiles where profileDEFAULTand resource_namePASSWORD_VERIFY_FUNCTION; //查看密码复杂度是否开启…

web基础07-Vue

目录 一、Vue 1.概述 2.MVC与MVVM 3.快速入门 4.Vue工程的创建 &#xff08;1&#xff09;基于vue-cli &#xff08;2&#xff09;基于Vite&#xff08;推荐&#xff09; 5.Vue3核心语法 6.setup &#xff08;1&#xff09;概述 &#xff08;2&#xff09;返回值方式…

【测试工具】JMeter接口测试的简单使用

事先声明&#xff1a;博主的JMeter是3.3版本的&#xff0c;可能和最新版本的操作有些许差别 测试前的准备工作 1、先添加一个线程组&#xff1a;右击“测试计划”&#xff0c;点击“添加”—》“Threads(Users)”—》“线程组” 2、再添加一个HTTP请求&#xff0c;右击“线程…

Redis 和 Mysql 数据库数据如何保持一致性????

1、前言 我们在实际项目中经常会使用到Redis缓存用来缓解数据库压力&#xff0c;但是当更新数据库时&#xff0c;如何保证缓存及数据库一致性&#xff0c;一般我们采用延时双删策略。 目前系统中常用的做法是一个查询接口&#xff0c;先查询Redis&#xff0c;如果不存在则查询…

esp32中vscode的开发环境

vscode中安装esp32开发环境请参考&#xff1a;CSDN 1、调出esp32的控制面板View ->Command Paletter&#xff0c;或者快捷键&#xff08;ctrshitp&#xff09; 调出esp-idf的样例工程 选择ESP-IDF所在的安装路径 选择一个样例工程&#xff0c;作为工程模板 创建的新工程如…