【Linux】进程概念

其他篇章【Linux专栏】

其他篇章 【C语言专栏】

上期回顾 【Linux】冯诺依曼---操作系统

文章目录

  • 1.进程的概念
  • [2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct](#2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct)
  • [3. 进程管理](#3. 进程管理)
    • [3. 1组织进程](#3. 1组织进程)
    • [3.2 查看进程](#3.2 查看进程)
    • [3.3 通过系统调⽤获取进程标⽰符](#3.3 通过系统调⽤获取进程标⽰符)
  • [4. 创建进程](#4. 创建进程)
    • [4.1 fork 创建进程](#4.1 fork 创建进程)
    • [4.2 fork的底层原理](#4.2 fork的底层原理)
  • [5. 进程状态](#5. 进程状态)

1.进程的概念

  • 基本概念:程序的一个执行实例,正在执行的程序等。
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。
  • 我们来理解:进程 = 内核数据结构(task_struct) + 自己的程序代码和数据

注意:

  • 运行起来的程序,加载到内存的程序都是进程
  • 一个任务就是一个进程,一个程序可以启动多个进程,程序只有一个,但是一个程序可以有多个进程

2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct

2.1基本概念

  • 进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课本上称之为PCB(process control block), Linux 操作系统下的 PCB 是: task_struct

帮助理解:可以把进程理解为老师管理学生,要想管理好学生,老师就得了解学生的基本情况(姓名,学号...),所以OS要想管理进程,就需要一个"信息表",即 PCB (Process Control Block->进程控制块)

2.2task_struct

  • 在 Linux 中描述进程的结构体叫做 task_struct 。
  • task_struct 是 Linux 内核的⼀种数据结构类型,它会被装载到RAM(内存)⾥并且包含着进程的信息。

2.21 task_struct 内容分类

  • 标示符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。

  • 状态: 任务状态,退出代码,退出信号等。

  • 优先级: 相对于其他进程的优先级。

  • 程序计数器: 程序中即将被执⾏的下⼀条指令的__地址__。

  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

  • 上下⽂数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。

  • I/O状态信息: 包括显⽰的I/O请求,分配给进程的I/O设备和被进程使⽤的⽂件列表。

  • 记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。

3. 进程管理

3. 1组织进程

Linux内核中, 最基本的组织进程task_struct的方式是采用"双向链表组织"。

3.2 查看进程

1.进程的信息可以通过 /proc 系统文件夹查看
2.大多数进程信息同样可以使用top和ps这些用户级工具来获取

3.3 通过系统调⽤获取进程标⽰符

  • 进程id(PID)
  • ⽗进程id(PPID)
#include 复制代码
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}

4. 创建进程

4.1 fork 创建进程

fork是Linux最基础的进程创建方式(子进程)

1. 本质:复制父进程,创建子进程

  • 调用 fork() 后,原来的进程(父进程)会多出一个几乎完全相同的子进程。代码、数据、内存状态基本都复制一份(只有部分会被修改)。

2.返回值区分父子

  • 父进程: fork 返回子进程 PID(大于0的整数)
  • 子进程: fork 返回 0
  • 出错:返回 -1

3.各自独立运行

两个进程有独立的地址空间,后续修改变量互不影响。

4. 典型用途

  • 让程序同时干多件事(并发)
  • 守护进程、后台服务
  • shell 执行命令时,都是先 fork 再 exec

4.2 fork的底层原理

本质: fork 本质是"内核"帮你复制进程描述信息 + 共享物理内存(写时复制),不是真的全量拷贝内存

内核做了哪几件事?

1.创建 task_struct内核为新进程分配一个task_struct (进程控制块 PCB),把父进程的几乎所有信息复制一份:

  • 页表
  • 文件描述符表
  • 信号处理方式
  • 当前寄存器上下文
  • 内存地址空间结构

2.分配新 PID

  • 子进程有独立 PID,与父进程不同
    (即父子进程地址空间独立,但物理内存初期共享)

3.写时复制(Copy-On-Write, COW)这是 fork 高效的关键:​

  • 刚 fork 完,父子共用同一块物理内存
  • 页表标记为"只读"
  • 谁要修改数据,才触发缺页异常,内核真正复制那一页内存→ 只有修改时才拷贝,不修改就一直共享

4.设置返回值内核分别给父子进程设置不同返回值:​

  • 父进程:返回子进程 PID(大于0的整数)
  • 子进程:返回 0之后两者完全独立调度、独立运行

1.为什么fork()函数要给子进程返回0?给父进程返回子进程pid?

  • 为了后面在调用fork()函数之后,可以根据不同的if判断来让父子进程执行不同的代码片段,及为了区分不同的执行流,使其可以执行不同的代码块。
    (一般而言,fork()函数之后的代码是 父子共享 的!)

2.为什么同一个变量会返回两次呢?

  • 底层是因为当该函数出现了return,创建子进程的时候,父进程共享了return,所以相应的就会出现返回两次的情况

两个返回值的含义

  • 父进程中,fork () 的返回值 = 子进程的 PID(正数):父进程通过这个返回值,识别自己创建的子进程

  • 子进程中,fork () 的返回值 = 0(零):子进程通过返回值 0,识别自己是子进程;

  • 若 fork 返回负数(\u003C0):表示子进程创建失败(比如系统资源不足,无法分配新的 task_struct)。

5. 进程状态

5.1 操作系统(OS)层面进程的状态

调用fork()函数创建子进程之后,父子进程并非一直处于运行状态,会在不同状态之间切换;
(进程状态反映进程执行过程的变化)

  • 三态模型中:进程状态分为 运行态、就绪态、阻塞态;
  • 五态模型中:进程状态分为 新建态、终止态、运行态、就绪态、阻塞态;
  • 运行状态

    进程占有处理器正在运行或正处于运行队列中。

  • 阻塞状态

    进程不具备运行条件,正在等待某个事件的完成。

  • 挂起状态

    进程的挂起状态是指计算机系统中,一个进程因为某些原因而暂时不能继续执行,但仍然保持在进程表中,并且有可能在将来恢复执行的状态。

5.2 Linux系统层面进程的状态

R运⾏状态(running)

  • 并不意味着进程⼀定在运⾏中,它表明进程要么是在运⾏中要么在运⾏队列⾥。

S睡眠状态(sleeping)

  • 意味着进程在等待事件完成(这⾥的睡眠有时候也叫做可中断睡眠)。

  • S 状态也叫 "浅度睡眠",是 Linux 中最常见的状态(比如我们打开的浏览器、QQ,大部分时间都处于 S 状态,只有当我们操作时,才会切换到 R 态)。

D磁盘休眠状态(Disk sleep)

  • 有时候也叫 不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待I/O的结束。

  • D 状态是 "保护态"------ 比如进程正在向磁盘写入数据,如果此时强行中断进程,会导致磁盘数据损坏,所以 Linux 内核禁止中断 D 状态的进程。

T停⽌状态(stopped)

  • 可以通过发送 SIGSTOP 信号给进程来停⽌(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运⾏。

X死亡状态(dead)---很少见

  • 这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。

Z僵尸状态(Zombies)

  • 子进程已经退出,但父进程没有读取子进程的退出状态(比如父进程一直在睡眠,没有调用 wait 函数),此时子进程的 task_struct(PCB)不会被释放,处于 "僵尸" 状态。

  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程就会进入Z状态

  • 处于僵死状态的进程就被成为僵尸进程,其相关资源尤其是task_struct结构体不能被释放,这也就会导致僵尸进程会一直占用内存资源!

相关推荐
蜡台2 小时前
centos 8 安装 nginx-1.29.8 及相关配置教程
linux·nginx·centos
爱学习的小囧2 小时前
ESXi性能历史怎么监控?2种方法,图形化+命令行全覆盖
java·linux·运维·服务器·网络·esxi·esxi8.0
码界筑梦坊2 小时前
94-基于Python的商品物流数据可视化分析系统
开发语言·python·mysql·信息可视化·数据分析·毕业设计·fastapi
斯维赤2 小时前
每天学习一个小算法:快速排序
java·python·学习·算法·排序算法
xiaoshuaishuai82 小时前
C# Submodule 避坑指南
服务器·数据库·windows·c#
SteveSenna2 小时前
Pika数据采集与处理
人工智能·学习
sagima_sdu2 小时前
Codex 使用指南(技术向):App、CLI 与工作流接入
linux·运维·语言模型·json
圆山猫2 小时前
[Linux] 用 Buildroot 为 RISC-V QEMU 构建最小根文件系统
linux·运维·risc-v
小草儿7992 小时前
gbase8s之onatpe备份与恢复性能测试
linux·服务器·网络