本次内容:
1.理解进程概念,了解PCB
2.学习进程状态,学会创建进程,掌握僵尸进程和孤儿进程,及其形成原因和危害
3.了解进程调度,Linux进程优先级,理解竞争性与独立性,理解并行与并发
4.理解C内存空间分配规律,了解进程内存映像和应用程序区别,认识地址空间
一 进程
1.什么是进程
定义:运行中的程序(程序是静态的代码,进程是动态的执行实例)
程序就像食谱,进程像厨师按照食谱做饭的过程
核心特点:
独立性:每个进程有独立的地址空间
并发性:多个进程可以同时运行(通过CPU时间片轮换)
2. 为什么需要进程?
1.资源分配:操作系统以进程为单位分配CPU,内存等资源
2. 多任务管理:通过进程隔离,避免程序间互相干扰(例如浏览器崩了不影响音乐软件)
3.PCB
1.什么是PCB
定义:操作系统为每个进程维护的一个数据结构,记录进程的关键信息
作用:操作系统用来记录进程所有状态信息的数据结构,(管理进程)
PCB与进程的关系:(类似档案)
1.进程与PCB同时存在(PCB是进程存在的唯一标准)
2.PCB随进程的创建而存在,进程终止二销毁
3.根据PCB里的状态和优先级决定进程的优先级
进程生命周期:
创建 -> 运行 -> 阻塞 / 就绪 -> 终止
2.PCB包含内容:
| 信息 | 内容 | 类比 |
| 进程标识符 | 进程ID(PID),父进程ID(PID) | 学生的学号,老师的工号 |
| 状态 | 运行态,就绪态,阻塞态(等待I/O) | 学生状态:上课,吃饭 |
| 程序计数器 | 下一条要执行的指令地址 | 书签 |
| 寄存器值 | CPU寄存器快照(进程切换时恢复) | 暂停游戏时保存的进度 |
| 内存分配 | 页表 堆栈指针等 | 分配表 |
| 文件描述符 | 进程打开的文件列表 | 学生借阅的图书清单 |
4.task_struct(Linux中的PCB)
1.Linux如何实现PCB
内核源码中的task_struct**:Linux用这个结构体表示PCB(定义在include/linux/sched.h)
2.如何查看进程的task_struct
ps -aux//查看进程列表(用户态视角的简化信息)
5.进程的声生命周期与PCB关系
1.进程状态切换
PCB的作⽤:状态变化时,内核更新PCB中的state字段,并保存/恢复寄存器值。
| 生命周期 | 发生什么 | PCB内容 | 关键字段改变 |
| 创建 | 程序代码加载进内存 | PCB产生,内核分配内存,初始化数据 | 分配PID |
| 就绪 | 资源准备就绪,等待CPU | PCB被移入就绪队列 | 就绪 |
| 运行 | 获得CPU指令执行 | PCB移出就绪队列,CPU寄存器上下文被载入 | 运行 (记录CPU时间) |
| 阻塞 | 等待I/O | PCB移出CPU,移入阻塞队列,保存现场 | 阻塞 (保存上下文信息) |
| 唤醒 | I/O完成,事件发生 | PCB从阻塞队列移入就绪队列,等待上传CPU | 就绪 |
| 终止 | 进程结束 | 若父进程未收回,变成僵尸 父进程wait回收后PCB销毁 | 终止,回收PID |
2.进程切换
CPU快速切换,我们以为在同时进行(但是频繁切换会降低性能)
本质:
1.保存当前进程的PCB
2.加载下一个进程的PCB到CPU
6.组织进程
内核中,所有的task _struct通过tasks字段(struct list_head)连接成一个双向循环链表
优势:
1.高遍历所有进程
2.动态增删(只需要修改指针)
7.查看进程
通过 /proc系统文件夹查看
例如获取PID为1的进程信息,需要查看/proc/1这个文件夹
⼤多数进程信息同样可以使⽤top和ps这些⽤⼾级⼯具来获取
ps -aux 文件名
8.通过系统调用获取进程标识符
进程: 进程id(PID) 父进程id(PPID)
系统调用:getpid() getppid()
关键点
•PID是唯⼀的:操作系统通过PID区分不同进程。
•⽗进程:⼤多数进程由shell或其他进程创建(类似“⽗⼦关系”)。
9.通过系统调用创建进程-fork
fork():复制当前进程,创建⼀个⼦进程。(调⽤⼀次,返回两次;⽗进程⼏乎原封不动地"克隆"出⼀个⼦进程。)
核心特性:
fork()让⼦进程继承家产(内存、⽂件描述符),
但不继承⾝份(PID/PPID)、不继承历史(CPU时间、挂起信号)、不继承约束(⽂件锁、定时器)。⼦进程是⼀个全新的个体,
⼦进程获得⽗进程数据段、堆栈段的完整副本,⽗进程和⼦进程拥有独⽴的地址空间,任⼀进程的修改不会影响另⼀个进程
进程在CPU时间统计
10.程序替换-exec家族
fork可以创建⼀个全新⼦进程,但⼦进程默认执⾏和⽗进程⼀模⼀样的代码。很多场景我们希望⼦进程去运⾏⼀个完全不同的外部程序
1.execve系统调用
功能:
替换当前进程的代码段、数据段、堆、栈,执⾏⼀个新程序。
execve是唯⼀真正的系统调⽤;execl/execlp/execv/execvp/execle均为glibc库函数
特点:
•成功:不返回(当前进程被替换)
•失败:返回-1
注意:
①exec不改变进程PID
②⽂件描述符继承规则,exec执⾏时,默认已经打开的⽂件描述符会继承到新程序;只有设置了FD_CLOEXEC标志位的fd,执⾏exec时⾃动关闭。
③信号处理继承规则,
exec替换程序之后:
1.⽤⼾⾃定义的信号处理函数全部重置为系统默认⾏为
2.信号阻塞掩码会保留不变
2.execl/execlp/execv/execvp
对⽐四个常⽤库函数的区别
命名规则:
•l:list,参数以可变参数列表逐个传⼊,末尾必须填NULL
•v:vector,参数放到字符串数组中统⼀传⼊,数组末尾必须是NULL
•p:path,会⾃动读取PATH环境变量搜索可执⾏⽂件,只需写程序⽂件名,不⽤写完整路径
注意:1.列表形式l,参数⼀个个写,最后强制NULL结尾;
2.数组形式 v适合参数数量动态变化的场景;
3.带p不⽤写绝对路径,不带p必须提供可执⾏⽂件完整路径。
3.exec函数对比总结
l列表 v数组 p找路径 e环境
二 进程状态
•CPU数量有限(通常只有⼏个),但进程数量可能成百上千
•操作系统需要⼀种⽅式来跟踪每个进程当前在做什么
•根据进程状态,操作系统决定如何分配CPU资源
1.进程状态机
就绪等CPU,运⾏占CPU,阻塞等I/O。调度算法决定谁上谁下。
就绪与阻塞:
就绪态是可运⾏但未分配CPU,阻塞态是因资源不可⽤⽆法运⾏,⼆者区分提⾼了调度效率。
进程状态查看:
1. ps axu (查看所有进程状态 STAT列)
2. ps ef
3. top (动态查看进程状态和内存)
4.ps aux | grep 文件
STAT列含义:
◦R:运⾏或就绪
◦S:可中断休眠(等待事件)
◦D:不可中断休眠(通常等待I/O)
◦Z:僵⼫进程
◦T:暂停状态(如Ctrl+Z挂起)
◦I:空闲状态
2.僵尸进程
定义:
⼦进程终⽌后,⽗进程未调⽤wait()或waitpid()回收其资源,导致内核保留⼦进程的退出状态(PCB残留)
关键特点:
◦进程已死亡,但仍占⽤**进程ID**(PID)和少量内核资源。
◦⽆法⽤kill命令杀死(因其已终⽌)。
查看⽅式
危害:
1.⽗进程不断创建⼦进程,⼦进程退出后变成僵⼫,⽗进程不回收,僵⼫越积越多
2.PID号被⼤量僵⼫进程占⽤有限(通常最⼤值32768或更⼤,如/proc/sys/kernel/pid_max)
3.系统⽆法分配新PID→新进程创建失败
4.PCB(进程控制块)占⽤内核内存
解决:
◦⽗进程调⽤wait()或waitpid()主动回收。
◦若⽗进程不处理,可终⽌⽗进程,僵⼫进程会被init接管并清理。
3.进程等待 -wait/waitpid
用处:
1.回收⼦进程资源,避免僵⼫进程
2.获取⼦进程的退出状态(正常退出码or被信号终⽌)
wait函数
功能:阻塞等待任意⼀个⼦进程结束,回收其资源。
参数status:传出参数,⽤于获取⼦进程的退出状态。如果不需要,可传NULL。
waitpid函数
功能:等待指定⼦进程结束,可设置⾮阻塞模式。
参数:
| 返回值 | 含义 |
| >0 | 退出的子进程PID |
| 0 | ⾮阻塞模式下,没有⼦进程退出 |
| -1 | 出错 |
status获取退出状态
wait与waitpid对比
wait和waitpid是回收⼦进程、避免僵⼫进程的核⼼函数。
wait简单但功能有限,waitpid更灵活,⽀持⾮阻塞和指定⼦进程。
pid_t说明
pid_t是Linux系统专⻔为进程ID定义的类型别名(typedef),本质底层就是int
凡是和进程ID相关的返回值、变量,统⼀使⽤pid_t类型
简单总结:
pid_t返回值告诉你“哪个⼦进程死了”,status变量告诉你“⼦进程怎么死的、退出码是多少”
4.孤儿进程
定义:⽗进程先于⼦进程终⽌,⼦进程被init/systemd进程⾃动接管。
•关键特点:
◦孤⼉进程仍在正常运⾏,⽆危害
◦init进程会定期调⽤wait()清理其终⽌后的资源
孤儿进程与僵尸进程
三 进程优先级
在多任务系统中,CPU资源有限,操作系统需要决定哪个进程优先获得CPU时间。优先级⾼的进程会被调度器更频繁地执⾏。
例如:后台下载任务(低优先级)vs ⽤⼾交互程序(⾼优先级)。
1.查看系统进程
用ps -AI命令
我们收到信息之后,可以看到
UID:代表执⾏者的⾝份
PID:这个进程的代号
PPID:⽗进程的代号
PRI:代表这个进程可被执⾏的优先级,其值越⼩越早被执⾏
NI:代表这个进程的nice值,其表⽰进程可被执⾏的优先级的修正数值,表⽰⼀个进程愿意让出CPU的程度,nice值越大越友善(-20⾄19)
调整进程优先级,在Linux下,就是调整进程nice值
查看nice值:
ps -eo pid,ni,comm,args | head -10
PRI与NI