✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、操作系统进程
1.1、进程背景
1.2、进程如何在CPU上运行的?
1.2、进程状态
2、Linux的进程状态
2.1、如何描述进程状态?
2.2、R 和S 运行状态
2.3、T/t 运行状态
2.4、D 运行状态
2.5、僵尸状态(Z)
2.6、死亡状态(X)
总结
1、操作系统进程
1.1、进程背景
由于Linux是一个多用户,多任务的系统,可以同时运行多个用户的多个程序,就必然会产生很多的进程,而每个进程会有不同的状态。
进程状态:一个程序被加载到内存变成进程之后,操作系统要对该进程进行管理,即为其创建对应的PCB对象,而进程状态,本质上就是PCB内部的一个整形变量,不同的整形值就对应不同的进程状态。
常见的进程状态:运行、挂起、阻塞、新建、就绪、等待、挂机、死亡。进程的不同状态本质都是用来满足不同的运行场景的。
1.2、进程如何在CPU上运行的?
CPU在内核上维护了一个运行队列,进行对进程的管理。让进程入队列,本质就是将该进程的task_struct 结构体对象放入运行队列之中。一个CPU就一个运行队列。
1.2、进程状态
1. 运行状态:
进程PCB在运行队列里就是运行状态,不是说这个进程正在运行,才是运行状态。状态是进程内部的属性,所有的属性在PCB里。
进程不只是占用CPU资源,也有可能随时要外设资源
2. 阻塞状态:
进程不在运行队列之中,进程不能直接被调度,而是在等待外设资源的状态,进程的PCB就被放在硬件的等待队列中。本质是对tack_struct对象放到不同的队列中!综上,所谓的进程不同的状态,本质是进程在不同的队列之中,等待某种资源
3. 挂起状态:
如果系统中存在许多进程,进程短期内不会被调度,代码和数据在短期内不会被执行,此时如果内存空间不足,操作系统就可以把代码和数据暂时保存到磁盘上,节省一部分空间,该进程暂时被挂起了,这就是挂起状态。对于阻塞状态和挂起状态,阻塞不一定挂起,挂起一定是阻塞。
2、Linux的进程状态
下面的状态在kernel源代码里定义:
static const char * const task_state_array[] =
{
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
2.1、如何描述进程状态?
进程状态实质是结构体task_struct内部的一个属性。通过宏定义的方式进行描述以及更改进程状态。
例如:
#define RUN 1
#define SLEEP 2 // 用数字表示进程意思
#define STOP 3
struct task_struct
{
// 内部的一个属性
int status;
}
struct task_struct process1; //创建进程
process1.status=RUN;//设置进程状态
补充:vim替换
:%s/name1/name2/ # name2替换掉name1 在命令模式中
2.2、R 和S 运行状态
R (running): 进程运行的状态 。
S (sleeping): 休眠状态,进程在等待 “资源” 就绪,可中断睡眠。
通过编写C语言代码就行验证,此处依旧使用makefile工具。
makefile 代码:
testStatus:testStatus.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f testStatus
testStatus.c 代码,测试代码:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
while (1)
{
printf("I am a process,pid: %d\n", getpid());
}
return 0;
}
输入命令./testStatus 运行可执行程序,并使用ps 查看进程信息。
查看进程信息
while :; do ps axj | head -1 && ps axj | grep testStatus | grep -v grep; sleep 1;done
通过运行结果我们可以看到,我们的进程一直在运行,但是进程状态是S+状态,按照我们正常的理解在运行的程序不就是R(运行)状态?那为什么这里确实S+状态呢???
因为:printf的本质是往显示器上打印;而程序的运行是在千里之外的云服务器上跑,最终打印出来的信息显示到我们本地的显示器上;根据冯诺依曼体系结构,显示器是一个外设,所以CPU在跑当前的程序时,把数据写入到我们当前的内存当中,打印数据的顺序:先写入到内存里,再刷新到外设里。可是我们无法保证每次打印的时候,显示器的状态都是就绪的,因为程序是CPU跑的,CPU的运算速度要比显示器本身的速度要快的多,所以进程在被调度的时候,要访问显示器的资源,因为资源要一直在显示器上打,所以大部分时间,相比较CPU来讲,大部分时间,我们对应的进程都在等待我们的设备资源是否就绪。就比如:代码执行到 printf 的时候,CPU是几纳秒,而数据刷新到显示器的时间是几毫秒,其余大部分时间都在等待中,而这等待的时间,就是(S)休眠状态。
补充:
./testStatus & # 在执行可执行程序后面加&符号,为在后台运行,跑起来后面不带+号。+号表示在前台还是后台运行,以什么为参考系后面说。后台运行后面有数字(pid),且ctrl +c 不能中断进程,需用kill -9 pid杀掉进程。
2.3、T/t 运行状态
补充命令 kill :
语法:
kill [-s <信号名称或者编号>][程序] 或 kill [-l <信息编号>]
功能:
给指定命令发信号。
常见选项:
-l <信息编号> : 若不加<信息编号>选项,则 -l 参数会列出全部的信息名称。
总共有64个信息编号。前面我们使用了-9杀死进程,下面我们会用到-18(SIGCOUT)进程继续以及-19(SIGSTOP)暂停进程。
T(stopped) :让进程暂停,等待被进一步唤醒。暂停后自动会变成后台。
下图可以看到细节:从S+状态(前台运行)变成T,再由T状态变成了S状态(后台运行)。
t (tracing stop) : 当前的进程因为被追踪而暂停了。
我们有没有让进程暂停过???
答案是当然有,在我们调试代码的时候,断点就是让进程暂停。
2.4、D 运行状态
讲解D状态之前我们先讲解一个故事。
★ 一个进程A要将1个G的数据存储到硬盘,根据冯诺依曼体系结构可以知道,本质是把数据从内存交给外设,由于速度差,进程需要等待硬盘资源把数据写入完毕,此时进程处于S状态,又因为操作系统管理进程,当系统整个的内存资源严重不足时,Linux操作系统有权利杀掉进程来释放空间,然后操作系统把A进程杀掉,但是此时B进程要给硬盘写入数据,硬盘需要去照顾B进程,导致A进程写入数据失败了,1GB的数据丢失了,如果这个数据是银行的转账记录,那么可能造成很大的影响。
★ 为了避免这种情况,如果进程在等待硬盘资源时,进程需要将自己的状态设为D状态:不可被杀深度睡眠,不可中断睡眠。
★ 我们一般不会遇到这种情况,从事系统管理、运维、存储等工作可能会遇到。
D (disk sleep): Linux系统比较特有的一种进程状态,不可被杀,深度睡眠,不可中断睡眠。
杀死D状态的方法:
1、进程自己醒来。
2、重启,重启不行则断电。
2.5、僵尸状态(Z)
僵尸状态也叫僵死状态,它是在进程在死亡状态之前的状态。当一个进程运行完毕、出现问题或者被杀掉以后,它所占用的内存资源和退出状态没有被它的父进程回收,此时这个进程的状态就称为僵尸状态。
2.6、死亡状态(X)
当一个进程执行结束或者是被操作系统杀掉,它的PCB被操作系统删除,并且对应加载到磁盘上的二进制代码也被删除,此时这个进程就处于死亡状态了。
当一个进程占有内存的所有资源被回收以后,这个进程就处于死亡状态。进程的死亡状态是看不到的,因为只有在回收完成的那一刻才会出现,PCB不存在也就搜索不到这个进程,所以也无法演示。
总结
本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!