当前位置: 首页 > article >正文

【Linux】认识进程以及进程的状态

目录

认识进程

基本概念

查看进程

父子进程

进程的状态

进程排队

运行状态

阻塞状态

挂起状态

僵尸进程

孤儿进程


认识进程

基本概念

有些教材上会说:正在运行的程序就是进程。这并没有错误,但是太过于笼统。现在我们深入到Linux底层来了解一下什么是进程。

我们想要执行一个程序,就必须要把代码和数据给操作系统。也就是说将其加载到内存中

但是操作系统有很多正在运行的程序,它肯定要把他们都管理起来。怎么做呢?先描述,再组织。也就是说先用结构体把各个进程的基本属性描述出来,然后再用数据结构将这些进程组织起来

这个结构体被称为进程控制块(PCB)。在Linux中PCB命名为task_struct

有了PCB之后,操作系统中一切管理进程的行为,本质都是管理对应的PCB。比如操作系统想要进程排队,只需要让PCB排队就好了。所以我们可以得出一个结论:

进程=内核PCB数据对象+可执行程序

task_ struct常见内容

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

查看进程

查看进程状态:ps ajx 或者 ps aux

但是这样会显示所有的进程,所以我们一般配合着grep使用。比如你要查找一个叫做xxx.exe的进程,你就输入ps ajx | grep xxx.exe即可查找到。

父子进程

在Linux中,每一个进程都有父进程。一切在命令行调用的进程,都是bash的子进程。

上图中PID代表进程的唯一标识符,PPID代表父进程的唯一标识符。我们可以用函数getpid()和函数getppid()去分别获取他们。其包含在<unistd.h>文件中,返回值类型是pid_t 。在Linux中,每一个进程都有父进程。

_____________________________________________________________________________

进程的状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。大家可能看见过这样的图片:

我们一般把进程分为运行、阻塞、挂起三种状态。但是它们具体是什么样的呢?下面我们来看一看。

下面的状态在kernel源代码里定义:

/*
The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 */
};

在Linux中,PCB内部有一个整型变量来表示进程的状态。比如R状态的位图就是00000000,S状态的位图就是00000001等等。

所以进程状态的本质其实就是一个整型变量,进程状态就是用一个整型来描述的。Linux中会有多个进程都要通过它们的状态来执行后续动作。

_____________________________________________________________________________

进程排队

软硬件资源是有限的,但是我们有很多进程,所以进程需要排队。

进程排队的本质就是PCB在排队。一个PCB内部有许多链表节点,因此可以把一个PCB放入到多个数据结构中进行管理。linux内核的PCB——task_struct是以双向链表的形式链接起来的。想要通过节点找回PCB,可以使用相对地址找回PCB的指针。

获取起始地址的运算:(task_struct*)(&n - (task_struct*)0 ->n)。task_struct中有很多类似于struct listnode的节点,因此它可以链入多个数据结构中。

每个软硬件都有自己的等待队列。当一个进程需要运行,就把它链接到CPU的等待队列中,当一个进程需要网络请求,就把它链接到网卡的等待队列中。对于PCB的状态改变,以及把PCB放到哪一个队列中,都是由操作系统来执行的。

____________________________________________________________________________

运行状态

操作系统给CPU维护了一个等待队列叫做运行队列,运行队列是一个双向循环链表,它容纳了系统中所有可以运行的进程。当task_struct链入到运行队列中就处入运行状态(R状态)。

_____________________________________________________________________________

阻塞状态

堵塞状态就是将task_struct从运行队列中移除再链入到其他等待队列中。

举个例子:你的程序中有一个scanf函数,该程序会从运行队列中移除,将自己的状态改为堵塞,链接到键盘的等待队列中,如果等待成功,就会再将自己从等待队列中移除,链接到运行队列中,将自己的状态改为运行。

_____________________________________________________________________________

S状态

S状态(sleep)休眠状态也可以叫做可中断睡眠状态浅度睡眠状态,属于阻塞状态,其一般处于等待资源的状态。可以用Ctrl+c中断程序。

____________________________________________________________________________

D状态

D状态(disk sleep)也叫做不可中断睡眠状态或者深度睡眠状态,属于阻塞状态。
当Linux过于繁忙的时候,内存中可能会有大量进程,此时操作系统就会选择直接杀掉某些进程,防止崩溃。

D状态,相当于有一个免死金牌,操作系统不会杀掉D状态的进程,而是继续让其等待资源。

_____________________________________________________________________________

T状态

T状态,进程处于暂停状态,属于阻塞状态。

我们可以通过给进程发送信号 kill -19,就是暂停的信号。

如果想要从T状态恢复,可以使用信号 kill -18,即继续进程。

T状态的进程是后台运行的进程,哪怕通过kill -18恢复过来依旧是一个后台进程。

_____________________________________________________________________________

t状态

t状态也是一个暂停状态,属于阻塞状态,但是其是一种被追踪的暂停状态。

比如说使用调试程序的时候,当进程到某个断点处停止了,此时进程就属于t状态。

_____________________________________________________________________________

挂起状态

当内存非常吃紧的时候,操作系统会将一些进程的代码和数据放入到磁盘的一个特定的区域,当缓和时,再将代码和数据放入内存。但注意task_struct不会放入磁盘。这种状态就称为挂起状态。挂起状态是一个特殊的阻塞状态。

____

_________________________________________________________________________

僵尸进程

进程退出后,其代码和数据会被立即释放,但是这个进程的PCB会被保留,因为我们可能需要这个进程的状态信息,此时这个状态就是僵尸状态(Z状态)。所以进程在死亡前一定会进入Z状态。父进程调用wait()或者waitpid()系统调用取得子进程的终止状态,此时进程真正死亡(X状态)。

由父进程创建一个子进程,子进程结束后,如果父进程一直不读取子进程的数据,子进程就会一直被保留,这种进程称之为僵尸进程。

僵尸进程有很大的危害:

  • 僵尸进程的 PID 还占据着,意味着海量的子进程会占据满进程表项,导致后来的进程无法 fock()
  • 僵尸进程的内核栈无法被释放掉(1K 或者 2K大小),导致内存泄漏。

_____________________________________________________________________________

孤儿进程

如果一对父子进程,父进程比子进程先退出,那么子进程的PCB就无法被父进程回收了,对于这种父进程先退出的进程,叫孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。


http://www.kler.cn/a/410561.html

相关文章:

  • Linux系统使用valgrind分析C++程序内存资源使用情况
  • Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理
  • 信息收集(1)
  • CSS clamp() 函数:构建更智能的响应式设计
  • 网络安全概论
  • 详细教程-Linux上安装单机版的Hadoop
  • 非交换几何与黎曼ζ函数:数学中的一场革命性对话
  • ubuntu24挂载硬盘记录
  • 故障排除-------K8s挂载集群外NFS异常
  • 【数据结构】树——链式存储二叉树的基础
  • 利用开源图床的技巧与实践
  • Pytorch微调深度学习模型
  • linux僵尸线程清理
  • 【Redis 缓存策略】更新、穿透、雪崩、击穿、布隆过滤
  • C语言-数学基础问题
  • ArcGIS API for Javascript学习
  • git 命令之只提交文件的部分更改
  • Python多进程与多线程详解:全面指南
  • 硬中断关闭后的堆栈抓取方法
  • HarmonyOS4+NEXT星河版入门与项目实战(19)------状态管理 @Prop@Link@Provide@Consume
  • nodejs操作selenium-webdriver
  • HashMap的寻址算法(源码分析)
  • 路由器中继与桥接
  • WPF中如何让Textbox显示为一条直线
  • Kali Linux语言设置成中文
  • 硬盘(HDD)与固态硬盘(SSD)详细解读