【Linux操作系统】简学深悟启示录:进程初步

文章目录

在操作系统中,进程(Process) 是程序的一次动态执行过程,是系统进行资源分配和调度的基本单位。简单来说,当你运行一个程序(如打开浏览器、启动一个 Python 脚本),操作系统就会创建一个进程,并为其分配内存、CPU 时间等资源,使其能够执行

1.进程先前知识铺垫

1.1 冯诺依曼体系

如图所示,计算机底层的硬件都要遵守该体系,大致由四部分组成:

  • 输入设备: 鼠标、键盘、摄像头、话筒、硬盘、网卡...
  • 存储器: 简单来说就是内存
  • 输出设备: 显示器、播放器硬件、磁盘、网卡...
  • 中央处理器:CPU,包括运算器和控制器,对我们的数据进行计算任务(算数逻辑),以及流程进行控制

有的设备是纯输入、输出设备,有的既是输出也是输入设备

一个程序要运行,必须先从输入设备进入存储器,到 CPU 处理之后,再回到存储器,由输出设备让我们看到处理结果。该体系规定不能跳过存储器直接与 CPU 交互,简单来说就是所有设备都只能直接和内存打交道

🤔为什么必须按照这种流程?

CPU 的运算速度(纳秒级)远超外部存储设备(如硬盘、U 盘,毫秒级甚至秒级)。存储器的运算速度是适中的,如果没有存储器(尤其是内存)作为 "高速缓冲中转站",CPU 会被外部设备的慢速拖垮 ------ 每次运算都要等外部设备慢吞吞地传输数据,效率会下降几万甚至几十万倍

1.2 操作系统

操作系统是一款进行软硬件管理的软件,对下通过驱动对硬件进行控制计算的管理,对上提供统一的接口供代码调用

🤔为什么操作系统只提供接口用于调用呢?

操作系统里有各式各样的数据,为了保护数据安全,也为了能给用户提供安全稳定的服务,让所有访问操作系统的行为,通通转化为系统调用完成

🤯操作系统是如何进行管理的?

先说结论,总结起来就是 "先描述,再组织",以学校管理学生的入学档案数据为例,每个学生的个人信息就是一份档案,即 struct 结构体节点,将每个节点通过双向链表链接起来,即可以通过增删查改对数据进行管理,这就是操作系统的做法

2.进程

2.1 概念

既然了解了以上知识,那么进程应该是不难理解了,简单来说,进程就是一个已经加载到内存里的程序,叫做进程,也叫任务

每当有一个进程创建的时候,就会对应创建一个 PCBprocess control block)对象,在 LinuxPCB 的具体实现是一个个的 task_structPCB 是描述对象的属性的集合

🚩task_ struct内容分类:

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

进程 = 内核 PCB 数据结构对象 + 你自己的代码和数据

2.2 查看进程

我们将以 process.cpp 这个循环输出的文件为例子查看进程状态(已配置 makefile 文件),下面提到的 PID 表示该进程的 ID 值,PPID 表示该进程的父进程(进程中创建进程后,实行创建的那个进程就叫父进程,被创建的那个就是子进程)

2.2.1 ps用户级工具获取

psLinux 系统中用于查看进程状态的命令,全称是 Process Status。它可以显示当前系统中运行的进程信息,包括进程 ID(PID)、运行状态、CPU 占用率、内存使用等

  • a:显示所有用户的进程(包括其他用户的进程)
  • j:以作业控制格式显示(包含 PPID 父进程 IDPGID 进程组 IDSID 会话 ID 等字段)
  • x:显示不关联终端的进程(如后台运行的进程、服务进程等)

ps ajx | head -1 && ps ajx | grep ./process 表示查看进程,显示出第一行的信息,并截取 ./process 这一条进程,grep --color=auto ./process 由于 grep 需要高亮显示而创建的进程

可以看到该进程的 PID20662,父进程为 18495

再去查看对应的父进程,发现是一个名为 bash 的进程。实际上,bash 是当前登录的终端交互系统的进程,大多数命令其直接父进程通常都是当前的 bash

2.2.2 系统文件查看

根据进程的 PID 同样可以直接在系统目录 proc 下查找到对应进程

2.3 函数获取进程ID

getpid():用于获取当前调用进程的进程 ID(PID)。这个 PID 常被用于生成唯一的临时文件名等场景
getppid():用于获取当前调用进程的父进程的进程 ID(PPID)

🔥值得注意的是: 使用这两个函数需要包含 <sys/types.h><unistd.h> 这两个头文件,这两个函数总是成功的,不会返回错误

2.4 创建进程

fork 是用于创建新进程的指令,相当于创建该进程的子进程

当一个进程(称为父进程)调用 fork 时,系统会创建一个新的进程(称为子进程),我们知道 进程 = 内核 PCB 数据结构对象 + 你自己的代码和数据,子进程PCB还是会正常创建一个,但是自定义的数据代码就不一样了。举个例子,子进程和父进程相当于两个厨师,代码相当于菜谱,运行中的代码是不能被修改的,对于父子进程来说是共享的,就不需要再多一份浪费空间;数据相当于食材,虽然做的是同一道菜,但是肯定是两份食材,因为数据可能被修改,不可能共享同一份数据,子进程复制父进程的数据进行的是写时拷贝,只有需要修改的数据才会拿下来拷贝,避免多余的拷贝占用空间

返回值区分: 为什么要创建子进程?是为了让多个进程同时执行不同的事情,因此需要执行不同的代码块,通过 if-else条件句实现。fork 调用会在父进程和子进程中都返回,但返回值不同。在父进程中,fork 返回子进程的 PID;在子进程中,fork 返回 0 。通过判断 fork 的返回值,程序可以区分当前是在父进程还是子进程中执行

运行结果:

可以看出确实是有两个进程运行着,根据不同的分支的结果就能看出,那么我将提出三个细节上的问题

  1. 为什么fork要给子进程返回0,给父进程返回子进程PID?

子进程被创建后,其自身的 PID 对区分身份来说并非必需 ------ 子进程若需要知道自己的 PID,可通过 getpid() 系统调用获取。更重要的是,子进程需要一个明确的信号来识别自己是子进程,而 0 恰好是一个理想的特殊值,因此 0 只作为标识,而不是有效的 PID

父进程创建子进程后,通常需要对其进行后续管理(如等待子进程结束、发送信号、监控状态等),而 PID 是进程的唯一标识,父进程必须知道子进程的 PID 才能执行这些操作

  1. fork函数是如何做到返回两次的?

看到 fork 内部的函数结构,中间部分进行相应的子进程创建操作,在 return 操作前子进程就已经创建完毕了,既然 return 也是代码,那么就是共享的,父子进程就都会执行,即 fork 函数会有两次返回

  1. 父子进程谁先运行?

这个是无法确定的,由调度器的策略决定,调度器简单来说就是一个分配进程占据CPU运行效率的组件


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

相关推荐
三不原则7 分钟前
日志管理工具 ——ELK Stack
运维·elk
qinyia10 分钟前
云原生运维与混合云运维:如何选择及 Wisdom SSH 的应用
运维·云原生·ssh
字节高级特工35 分钟前
线程互斥锁:守护临界区的关键
linux·运维·服务器·c语言
打点计时器1 小时前
onnxruntime配置开启ACL加速Arm上的模型推理
linux·c++
木子欢儿2 小时前
在 Debian 12 上安装 Xfce 桌面
java·linux·运维·服务器·debian
Vdeilae2 小时前
debian 时间同步 设置ntp服务端 客户端
java·服务器·debian
coder_lorraine2 小时前
【Linux系列】Linux Snap 安装与使用指南:高效管理应用的神器
linux·运维
LLLLYYYRRRRRTT2 小时前
9. Linux 交换空间管理
linux·数据库·redis
zhuyan1083 小时前
【ROS2】常用命令
linux·运维·服务器
涛思数据(TDengine)3 小时前
可信数据库大会现场,TDengine 时序数据库展示核电场景下的高性能与 AI 创新
大数据·运维·数据库·人工智能·时序数据库·tdengine·涛思数据