Linux进程
- 进程概念
- task_struct内容分类
- 标识符
- 状态
- 优先级
- 程序计数器PC/EIP
- 其他
- 进程
- 查看进程
- 通过系统调用创建进程
- Linux进程状态
- R状态
- S状态
- D状态
- T状态
- Z状态
- X状态
进程概念
课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体
系统中可以同时存在大量的程序
Windows下的进程:
Linux下的进程:
操作系统如何管理进程?——先描述(需要有PCB——描述进程的结构体),在组织
操作系统如何通过 PCB 管理进程?
若有 100 个进程,则有 100 个 PCB(一个PCB对应一个进程),操作系统通过以下方式组织和管理这些 PCB:
1、链表或表结构:
PCB 通常以 链表 或 表(如进程表) 形式组织,便于快速遍历和查找。
例如:就绪队列、阻塞队列分别链接对应状态的进程 PCB。
2、进程生命周期管理:
创建进程:分配 PCB,初始化字段(如 PID、状态、资源)。
销毁进程:回收 PCB,释放内存和资源。
3、上下文切换:
当进程切换时,操作系统通过 PCB 保存当前进程的寄存器、程序计数器等状态,并加载下一个进程的状态。
4、资源分配与权限控制:
通过 PCB 中的资源列表和权限信息,操作系统确保进程只能访问合法资源(如防止越权访问文件)。
1、进程 > 可执行程序 :因为进程 = 可执行程序 + 内核数据结构
2、为什么会存在PCB?——操作系统需要软硬件管理,所以需要有PCB
所为的创建/删除进程就是对应的添加节点或是删除节点,即对进程的管理就是对链表的增删查改
Linux描述进程是用struct task_struct{}
包含:
1、pid、ppid
2、优先级
3、能够找到对应的代码与数据
4、时间片
5、上下文信息
6、连接信息(PCB与PCB之间的联系)
7、…
task_struct内容分类
标识符
来查询看看进程情况:
myproc就是一个正在运行的进程
ps aux查找所有的进程
杀掉进程:
之后运行程序:
可以发现pid会一直变化,但是ppid(父进程id)是没有任何变化的
干掉该进程后,会直接掉线
登录成功的时候,就会自动创建一个bash
状态
S:休眠状态 -> 这里是显示的一瞬间状态
依然是sleep——printf是往外设打印,外设很慢,os调用的时候,大部分时间都在休眠
这次取消外设printf试验:
此时就变成了R状态
R与R+表示这里程序是在前台跑还是后台跑
如何删后台进程——kill命令
优先级
什么是优先级?——获得资源的优先级
vs权限 : 权限——能或不能 优先级——能的前提下谁先谁后
为什么要有优先级?——资源有限
CPU资源是有限的,但是进程是可以有多个的,因此进程需要优先级设置
进程排队——PCB结构体进行排队
程序计数器PC/EIP
程序中即将被执行的下一条指令的地址,进程在切换的时候,需要保护数据之后恢复
在 Linux 系统中,PCB的具体实现就是 task_struct 结构体。它是操作系统内核中用于描述和管理进程的核心数据结构,每个进程(或线程)在内核中都有一个对应的 task_struct,记录了进程的所有运行时信息。
用户双击 app.exe:
操作系统从硬盘加载app.exe到内存,创建task_struct,初始化进程。进程加入就绪队列:
调度器选择该进程,分配CPU时间片。CPU 执行指令:
取指令 → 解码 → 执行,PC逐步更新。进程结束:
执行exit()系统调用,操作系统回收内存和PCB。
其他
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
在操作系统中,上下文数据是指进程执行时处理器的寄存器中保存的当前运行状态信息。这些数据是进程在CPU上运行时的实时快照,用于在进程切换时保存和恢复进程的执行环境。
上下文被保存至进程控制块里面(task_struct)
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制记账号等。
进程
查看进程
在根目录下有proc 这里也可以查进程
ls /proc/PID
打开当前进程信息
cwd:
通过系统调用创建进程
fork()创建子进程
其中的22883(fork的父是bash)
以 .bash 开头的文件是 Bash Shell 的配置文件,位于用户的主目录(~)下,用于自定义 Shell 环境和行为。
fork创建子进程,fork之前的代码被父进程执行,fork之后的代码,默认情况被父子都可以执行
原程序进程的父进程:
当用户在终端中运行 ./myproc 时,启动该程序的父进程是 Shell(如 bash 或 zsh)。此时原程序的 PPID(父进程 ID)是 Shell 的 PID。
子进程的父进程:
通过 fork() 创建的子进程,其父进程是 原程序进程(即调用 fork() 的进程)。子进程的 PPID 是原程序进程的 PID,而非 Shell 的 PID。
假设原程序进程的 PID 是 1000,Shell 的 PID 是 500,则输出为:
hello
I am a process->pid: 1000, ppid: 500 ← 父进程(原程序)
I am a process->pid: 1001, ppid: 1000 ← 子进程
fork创建的子进程,与父进程链接起来(双链表)-> 此时系统内多了一个进程
子进程执行的代码和数据来源于父进程
fork() 前的代码:
仅由父进程执行一次,子进程不会重复执行,例如 printf(“hello \n”) 只输出一次。
fork() 后的代码:
父子进程各自独立执行,互不干扰。
例如 while(1) 循环会在两个进程中持续运行。
父子进程关系:
父进程(原程序)的父进程是 Shell(如 bash)。
子进程的父进程是原程序进程,而非 Shell。
fork之后,父子进程代码共享
fork之后,就有了2个进程,这两个进程调度先后是不确定的,取决于调度算法
fork函数会有2次返回值(为什么?——后续提及),给父进程返回pid,给子进程返回0
可以使用fork进行分流:
Linux进程状态
R状态
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列(所有R状态全在一个队列上)里。
进程是R状态 -> 不代表正在运行,而是代表可以被调度
因此 一个CPU可以同时存在多个R状态的进程
S状态
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。 S 在Linux是浅度睡眠->通常是进程等待某一个事件发生
D状态
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。 在Linux中D是深度睡眠
D状态:表示该进程不会被杀掉,即便你是操作系统;除非是自己主动醒来,才可以恢复
T状态
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
列出信号:执行 kill -l 会显示当前系统支持的信号列表。这些信号用于进程间通信,例如终止进程、重新加载配置等。
杀死后会变成T状态:暂停,暂时不跑,让其先暂停
让其继续运行:SIGCONT
Z状态
Z(zombie)-僵尸进程
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
通俗来说,进程退出的时候,在系统层面,曾经申请的资源并不是被立即释放的,而是要暂存一段时间,供OS(父进程)进行读取 -> 僵尸状态
进程被创建的目的是为了完成某种工作、任务;在任务完成的时候,调用方也应该需要知道任务完成情况(除非不关心完成情况)
echo $? 是最近一次进程退出时候的退出码 return
为什么需要僵尸进程?——需要知道一个任务的完成情况,进程退出的信息(退出码)是会被暂时保存起来(task_struct)而非立即释放,如果没有人读取,此时task_struct相关数据是不应该被释放 此时这种状态就是僵尸。
以上代码结果是:子进程提前结束,但是父进程一直执行,子进程没有被访问,一直保持僵尸状态
创建僵尸进程的例子:
监视脚本
while :; do ps aux | head -1 && ps aux | grep myproc | grep -v grep; echo"##################"; sleep 1; done
此时可以看到僵尸进程
X状态
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
在 Linux 进程管理中,X(死亡状态,Dead) 是进程生命周期中的一个最终状态,表示进程已完全终止且其资源(如内存、文件句柄等)已被系统回收。
总结:
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲),没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态