【Linux】初识进程概念与 fork 函数的应用

server/2025/2/25 8:53:51/

在这里插入图片描述

Linux相关知识点可以通过点击以下链接进行学习一起加油!
初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G++编译器
make与Makefile自动化构建GDB调试器与Git版本控制工具Linux下进度条冯诺依曼体系与计算机系统架构

进程是操作系统中资源分配和调度的核心单位,而 fork() 函数是创建子进程的关键工具。本节将简要介绍进程的概念,并通过 fork() 探讨其实际应用。

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、进程概念
    • 1.1 进程理解
  • 二、如何描述和管理进程
    • 2.1 进程本质
    • 2.2 PCB描述进程状态
      • 2.2.1 task_struct
      • 2.2.2 task_ struct内容分类
    • 2.3 组织进程
      • 2.3.1 不同数据结构配合进程需求
  • 四、查看进程状态
    • 4.1 查看所有进程指令
    • 4.2 /proc 目录
  • 五、通过系统调用获取进程标识符
    • 5.1 父子进程
      • 5.1.2 父进程意义
    • 5.3 使用系统调用接口getpid、getppid
  • 六、系统调用创建进程
    • 6.1 fork函数(分叉/创建进程)
    • 6.2 fork函数返回值
      • 6.2.1 fork() 的返回值
      • 6.2.1 为什么fork可以返回两次
      • 6.2.3 变量同时接收两个返回值
    • 6.3 fork()函数后进程关系
    • 6.4 父子进程运行顺序
    • 6.5 创建子进程意义
    • 6.6 进程具有独立性
    • 6.7 Bash父进程简介

一、进程概念

进程是计算机操作系统中的一个基本概念,指的是一个程序在计算机中的一次执行过程。它是操作系统中资源分配和调度的基本单位。进程和程序的主要区别在于:程序是静态的代码,而进程是程序在运行时的动态实例。

1.1 进程理解

  • 课本概念:程序的一个执行实例,正在执行的程序等
  • 内核观点:已经加载到内存中程序,担当分配系统资源(CPU,内存)的实体

当我们运行程序,需要数据和代码加载到内存中,内存会对进程中数据和代码进行处理。其中操作系统中不单单只存在一个进程,而是同时存在多个进程,操作系统需要对这些进程进行管理,那么就需要用到"先描述,在组织",进行更好地管理。

二、如何描述和管理进程

2.1 进程本质

我们将磁盘中可执行程序的数据和代码被加载到内存,就可以说明"进程是可执行程序中数据和代码"吗?这里有一系列问题:占据内存多少空间、被调用多少时间、当前进程状态如何?

在这里插入图片描述

结果很显然,单从内存只有可执行程序的代码和数据完全不足描述上述需要的信息。这说明了"操作系统将可执行程序加载到内存中,被加载到内存中的代码和数据,根本不是进程,只是进程对应的代码和数据"

2.2 PCB描述进程状态

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

操作系统为了管理已经被加载进来所有进程,需要创建用于描述进程的结构体对象"PCB进程控制块(process ctrl block)",本质是对于进程属性进行描述。

2.2.1 task_struct

在Linux中描述进程的结构体是"task_struct"。task_struct属于Linux中内核数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

在这里插入图片描述

2.2.2 task_ struct内容分类

PCB是对操作系统学科的统称,其中"struct task_struct"对Linux进程块具体的称呼

  1. 标示符】: 描述本进程的唯一标示符,用来区别其他进程。
  2. 状态】: 任务状态,退出代码,退出信号等
  3. 优先级】: 相对于其他进程的优先级
  4. 程序计数器】: 程序中即将被执行的下一条指令的地址
  5. 内存指针】: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针(可执行程序执行需要对应的数据和代码,所有PCB对象必须需要一个指针指向这块空间)
  6. 上下文数据】: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器
  7. I/O状态信息】: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表
    记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  8. 其他信息

总结】:进程 = 内核数据结构对象PCB + 对应代码和数据

2.3 组织进程

操作系统做管理并不是把你的可执行程序加载进来做直接管理,而是对于pcb进行管理。

在这里插入图片描述

操作系统对进程进行管理,本质上是对PCB做管理,并不关心进程加载内存中代码和数据。只需要找到PCB对象,通过PCB内存指针找到对应的代码和数据,交给CPU处理就行。

每个进程都有属于自己的PCB,为了更好地管理不同进程的PCB,一般通过指针将PCB进行连接,形成链表。根据不同进程的需求,会采用不同的数据结构进行存储,通过对应指针信息来将它们更好地管理。

其中在Linux中"task_struct"主要是以"双向链表形式组织"

2.3.1 不同数据结构配合进程需求

进程管理的关键在于将其放入适合的组织数据结构中。不同的数据结构具有独特的特性,决定了其背后的算法选择,而算法的差异又对应于不同的应用场景。因此,合理的数据结构选择是优化进程管理的基础

相关场景】:调度运行进程,本质就是让进程控制块"task_struct"(简历)进行排队,而不是这个代码和数据在排队,跟面试投简历一般,面试官查看每一份简历

在这里插入图片描述

在操作系统中,可能存在存储进程指针的运行队列和等待队列。如果需要将某个进程移动到特定队列,可以通过修改队列指针的链接结构来实现,从而达到更灵活的进程管理目的。

四、查看进程状态

开机时操作系统(OS)从外部存储设备(如硬盘或固态硬盘)加载到内存中,这是启动过程的一个重要步骤

4.1 查看所有进程指令

ps ajx 是用于查看系统中所有进程状态的命令。

具体参数含义如下:

  • ps:显示当前系统中的进程信息。
  • a:显示所有用户的进程(不仅仅是当前用户的进程)
  • j:显示进程的控制终端、进程组等信息。
  • x:显示没有控制终端的进程(包括后台进程)

在这里插入图片描述

场景演示】这里想拿到头部属性信息和进程信息,可以使用指令级联。通过指令ps axj | head -1 && ps axj | grep myprocess查看可执行程序该进程状态信息。
在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题】:为什么会出现grep进程信息

如果指令需要被执行需要变成进程才能运行,grep指令被执行本质商也是进程被执行,所以grep本身这个进程也被查出来。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.2 /proc 目录

/proc 目录中的文件是内存级文件,关机时会消失,开机后会重新生成。它主要提供动态运行进程的可视化信息。

其中,蓝色表示目录文件,因为一个进程可能包含许多相关信息。我们可以尝试进入这些目录查看具体内容。

在这里插入图片描述

删除执行中程序

如果删除可执行程序,会发现这个进程依旧运行。

在这里插入图片描述

因为原则上一个程序要被变成进程调度,会在内存里面有存在一份。就算你将可执行程序从磁盘上删除了,由于内存存在备份,所以进程还是可以运行。

总结

  1. exe 表示当前进程对应的可执行文件路径,说明进程能够找到自己需要执行的代码(已被可视化展现)。

  2. cwd 表示当前进程的工作目录(Current Working Directory)。因此,当你使用 fopen 创建新文件时,默认路径就是当前目录,而这个默认位置正是由该进程的 cwd 决定的。说明这个启动的myprocess进程,是由这个指定的绝对路径下的这个程序加载形成的。

五、通过系统调用获取进程标识符

在内核结构体 task_struct 中,存在一个 pid 属性,用于表示进程的唯一标识符。

  • pid 是无符号整数类型(unsigned int pid),用于区分不同的进程。
  • 它是操作系统封装的一种数据类型,实际对应无符号整数。

用户无法直接访问操作系统内核中与进程相关的 PCB(进程控制块)数据结构。如果用户需要获取进程的 pidppid,必须通过操作系统提供的系统调用"getpid()、getppid()"来完成。

在这里插入图片描述

5.1 父子进程

如果是pid属性表示进程的唯一标识符,那么ppid属性表示什么呢?

ppid 的含义: Parent Process ID

在这里插入图片描述

通过结果显示,ppid 属性表示父进程的唯一标识符。可以观察到,在我们的可执行程序中,父进程的 ppid 通常不会发生变化,但是pid会发生变化,因为它表示的是基础命令行进程(如 bash)的父进程标识。

5.1.2 父进程意义

在 Linux 权限管理相关内容中,以“王婆和实习生”的例子可以更好地理解为什么 bash 需要创建子进程。

bash 命令行的作用主要有两个方面:

  1. 解释命令:将用户输入的指令解析为可执行操作。
  2. 阻止非法操作:限制用户进行不符合权限的行为。

每一条指令或可执行程序其实都对应一个独立的进程。因此,bash 命令行会先创建一个子进程来执行对应的指令,而自己则保持运行,等待处理其他命令。

这种设计的优点是:即使子进程崩溃,也不会影响 bash 命令行本身的运行,从而确保可以继续处理其他指令。

5.3 使用系统调用接口getpid、getppid

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

按照上述说法,进程每次启动对应pid都不一样,ppid通过不会发生变化,但是当我们重新启动机器时,再次执行该程序,会发现ppid发生了变化,可以说明父进程或bash命令行在打开机器时候就创建好的进程

调用监考脚本】:while :; do ps ajx | head -1 ;ps ajx | grep mycode | grep -v grep;echo "----------------------------";sleep 1;done

在这里插入图片描述

如果不想显示与 grep 指令相关的进程信息,可以使用 -v 参数来排除,会过滤掉包含 grep 本身的进程信息。

在这里插入图片描述

六、系统调用创建进程

6.1 fork函数(分叉/创建进程)

用户想创建进程,但是创建进程需要创建内核数据结构,但是用户是没有权限对内核数据结构进行任何的增删查改,用户没有权限在操作系统内新增一个test_struct,所以操作系统也需要提供相对于的系统调用fork()

在这里插入图片描述

6.2 fork函数返回值

6.2.1 fork() 的返回值

  1. 成功创建子进程时
  • 父进程中fork() 会返回子进程的 PID(正整数)。
  • 子进程中fork() 会返回数值 0
  1. 创建子进程失败时
  • fork() 会返回 -1 给父进程,表示子进程创建失败。

  • 总结:fork() 有两个返回值,分别传递给父进程和子进程,用于区分执行的上下文(父进程还是子进程),以及处理可能的错误情况。

6.2.1 为什么fork可以返回两次

由于子进程会继承父进程的代码,因此 return ret 这一段代码会被父子进程分别执行一次,从而导致两次返回结果

在这里插入图片描述

6.2.3 变量同时接收两个返回值

由于子进程在修改数据发生了写实拷贝,导致了数据存在两份内容,进程具有独立性,父进程和子进程通过if else 分流去执行共享的代码

为父进程返回子进程PID和为子进程返回数值0

  • 为父进程返回子进程的 PID:通过返回值,父进程可以知道子进程的唯一标识符(PID)。
  • 为子进程返回数值 0:子进程通过返回值知道自己是新创建的进程。

在这里插入图片描述

显示效果】:

在这里插入图片描述

6.3 fork()函数后进程关系

通过我们的实时监控,会发现PID和PPID是相同的,是存在父子关系。fork 之后,父进程和子进程会共享代码,但各自拥有独立的数据空间。

在这里插入图片描述

在这里插入图片描述

  • 【创建一个进程的本质】
  1. 系统中多了一个新的进程,对应一个新的内核 task_struct
  2. 子进程拥有自己的代码和数据空间,但默认情况下,继承了父进程的代码和数据。
  • 【数据加载机制】
    父进程的代码和数据是从磁盘加载的,子进程通过 fork 默认继承了这些内容,但有独立的内存空间。
  • 【调用关系】
    子进程会获取调用它的父进程的 id 信息(导致子进程PPID为父进程PID)。

6.4 父子进程运行顺序

当使用fork()函数创建出子进程,是先运行子进程还是父进程呢?这里先运行谁,是需要通过调度器去决定的。一般电脑只有单个CPU,调度器会根据当前进程中选择一个合适的进程放到CPU当中,进程之间会竞争CPU资源,所以调度器会遵循自己的一套原则来保证进程之间的公平性(进程优先级、时间戳)去决定。

6.5 创建子进程意义

如果父进程和子进程执行的后续代码完全相同,就没有实际意义。通常,父进程和子进程会各自执行代码的不同部分:父进程负责处理一部分逻辑,子进程负责处理另一部分逻辑。两个进程并发运行,从而提高程序的整体执行效率

问题】:问题在于fork()之后,父子进程执行后续代码完全相同,如何分开执行不同模块的功能呢?

fork 之后,父进程和子进程的代码虽然共享,但它们的执行路径可以通过 fork 的返回值来区分,从而保证父子进程执行不同的代码逻辑。

fork 的返回值:

  • 在父进程中,fork 返回子进程的 PID(正整数)。
  • 在子进程中,fork 返回 0
  • 如果 fork 失败,返回 -1 给父进程。

通过检查 fork 的返回值,可以让父进程和子进程执行不同的代码逻辑,例如:

pid_t pid = fork();

  • if (pid > 0) {// 父进程执行的逻辑}
  • ​ else if (pid == 0) { // 子进程执行的逻辑}
  • ​ else {// 错误处理逻辑}

在这里插入图片描述

这种方式确保了父子进程可以执行各自的任务,实现功能分工。

6.6 进程具有独立性

任何平台上,进程都具有独立性,这意味结束了某个进程不会影响其他进程,

在这里插入图片描述

在数据结构层面上,进程是并行的,你是你的我是我的;在逻辑上,它们是存在父子关系,但是仅仅是在数据结构指针层面上的关心。由于进程的代码和数据是从磁盘中加载进来的,所以子进程只能用父进程的代码,对于"数据"子进程也必须使用父进程的数据。

理论上,由于代码本身具有不可被修改属性,只有只读属性,所以父子支持共享代码。数据不一样,数据是可以被修改的,这样导致"子进程必须拷贝一份相同的数据"且独立出来。

问题在于父进程的数据可能非常多,但是可能子进程只是共享了其中一部分代码,不一定需要使用到父进程的所有数据。如果子进程无脑将这些数据拷贝过来,会存在大量资源浪费,效率也会大幅度减低。

6.7 Bash父进程简介

在系统启动时,操作系统被加载到内存,同时负责启动对应的bash父进程。当用户在命令行输入指令时,bash会为每条指令创建一个子进程,由子进程负责执行该指令。即使指令执行失败,也不会影响bash父进程的正常运行,这种机制有效保护了bash父进程,使其能够专注于命令行解释的核心工作。


在这里插入图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是Linux笔记,希望对你在学习Linux旅途中有所帮助!


http://www.ppmy.cn/server/170520.html

相关文章

【Python爬虫(63)】从0到1:打造图片与视频爬虫攻略

【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取&#xff…

代码随想录第三十八天| 322. 零钱兑换 279.完全平方数 139.单词拆分 动态规划:关于多重背包,你该了解这些!

322. 零钱兑换 题目描述 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 你可以认为每种硬币的数量是无限的。 力扣题目链接 322. 零钱兑换 解题思路…

跟着李沐老师学习深度学习(十四)

注意力机制(Attention) 引入 心理学角度 动物需要在复杂环境下有效关注值得注意的点心理学框架:人类根据随意线索和不随意线索选择注意力 注意力机制 之前所涉及到的卷积、全连接、池化层都只考虑不随意线索而注意力机制则显示的考虑随意…

解锁健康密码,拥抱养生生活

在快节奏的现代生活中,健康养生愈发成为人们追求美好生活的关键。步入 2025 年,让我们从日常细节入手,开启一场充满活力的健康养生之旅。 饮食是养生的基石。一日三餐,合理搭配最为重要。多吃谷物杂粮,它们富含膳食纤维…

智能合约的部署

https://blog.csdn.net/qq_40261606/article/details/123249473 编译 点击图中的 “Compile 1_Storage.sol” 存和取一个数的合约&#xff0c;remix自带 pragma solidity >0.8.2 <0.9.0; /*** title Storage* dev Store & retrieve value in a variable* custom:d…

CSS编程基础学习

1. CSS 简介 1.1. CSS概念及作用 HTML即超文本标记语言&#xff08;HyperText Markup Language&#xff09;&#xff0c;是网页制作的基础&#xff0c;通过HTML&#xff0c;开发者可以定义网页的标题、段落、链接、图像、列表、表格、表单等元素。引入CSS 可以针对 HTML 里的…

Linux驱动之tty子系统(简要分析以及驱动实现)

Linux驱动之tty子系统 深入linux源码探究&#xff01; 1. TTY 子系统概述 在 Linux 内核中&#xff0c;TTY (Teletype) 子系统是 用户空间与字符设备交互的桥梁&#xff0c;它最早用于模拟电传打字机&#xff08;Teletype&#xff09;&#xff0c;但如今主要用于 终端、串口…

MySQL 的存储引擎有哪些?它们之间有什么区别?

目录 1. 在事务安全方面 ACID 的具体含义 MySQL 中 ACID 的实现 ACID 的实际应用 总结 2. 在锁机制方面 3. 在外键支持方面 4. 在索引方面 5. 在数据存储方式上 MySQL 目前使用得比较多的版本是 MySQL5.7 和 MySQL8.0。MySQL5.7 和 MySQL 8.0 都支持多种存储引擎&#…