进程状态、排队

1. 进程排队

进程为什么要排队呢?答案就是资源不够。需要等待某个软硬件资源,就像我们常用的scanf函数就是等待键盘资源。

之前的文章我们有个结论,进程 = PCB + 可执行程序,那么进程排队是谁在排队呢?答案是PCB在排队,就像我们找工作,是简历排队。排队就是链入到一个数据结构中,且PCB可以链入到多种数据结构,linux内核的PCB——task_struct是以双向链表的形式链接起来的。但和一般的双向链表的实现不一样,指向下一个task_struct的指针指的不是起始地址。
在这里插入图片描述

获取起始地址的运算:(task_struct*)(&n - ((task_struct*)0 ->n)

那如何理解可以链入到多个数据结构中呢?答案就是一个task_struct中有多个struct listnode节点。

在这里插入图片描述

2. 进程各个状态

在这里插入图片描述

看起来很复杂,但是在task_struct中,状态就是一个整型变量

那分成这么多状态有什么用呢?因为状态决定了后续动作。

  1. 运行状态
    明确一个概念,一个CPU有一个运行队列,当task_struct被链入到这个运行队列中,就处于运行状态(R),表明随时准备好被调度。
    在这里插入图片描述
  2. 堵塞状态
    一个现实:当你写了一个死循环程序运行,其他的程序依然不受影响的在运行,这就表明了它被从运行队列中移除了。
    现在有一个场景,你写的程序中有一个scanf函数,我们知道是在等待键盘资源,这时候该程序的状态会发生什么呢?
    在这里插入图片描述
    这时候该程序会从运行队列中移除,将自己的状态改为堵塞,链接到键盘的等待队列中,如果等待成功,就会再将自己从等待队列中移除,链接到运行队列中,将自己的状态改为运行。

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

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

3. 查看一个进程的状态

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7     while(1)
  8     {
  9     printf("I am a process,pid: %d,ppid: %d\n",getpid(),getppid());
 10     //sleep(1);                                                               
 11     }
 12     return 0;
 13 }
ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep

在这里插入图片描述

在这里插入图片描述
这里可能会有疑惑,为什么该段代码一直在死循环的运行,但状态还是Sleep呢?关键在于printf这个函数大部分时间还是处于一个等待硬件资源的过程中,所以很难查到运行状态,你以为printf执行的很快,但不要以自己的想法衡量CPU的速度。
为了验证这个我们将代码中这一句注释掉

 1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7     while(1)
  8     {
  9     //printf("I am a process,pid: %d,ppid: %d\n",getpid(),getppid());
 10     //sleep(1);                                                               
 11     }
 12     return 0;
 13 }

在这里插入图片描述
果然变成的运行状态,后面的加号是什么意思呢?有加号表示这个进程是前台进程,没有加号表示是后台进程,我们在运行一个程序的时候再后面输入一个&。该程序就会以后台程序运行,只能通过kill命令终止该进程。

4. linux内核描述进程状态

/*
* 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 */
};
  1. R运行状态(running) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
    里。
  2. S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
    (interruptible sleep))。
  3. D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  4. T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  5. X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态.

S、D、T、t都可以理解成堵塞状态,它们都在等待资源。

  1. Z僵尸状态(zombie)
    当一个进程执行完成之后,该进程的代码和数据可以释放了,但是它返回的信息不能释放(存放在struct_task),因为需要被父进程获取。僵尸状态就是进程执行完毕但是没有获取该进程的返回信息
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 
  6 int main()
  7 {
  8     pid_t id = fork();
  9     if(id == 0)
 10     {
 11         printf("I am child, pid : %d, ppid : %d\n",getpid(),getppid());
 12         sleep(5);
 13         exit(0);
 14     }
 15     else
 16     {
 17         printf("I am father, pid : %d, ppid : %d\n",getpid(),getppid());
 18         sleep(10);
 19     }
 20     return 0;
 21 }

在这里插入图片描述
这会造成什么问题呢?很简单,就是内存泄漏,解决办法后面会介绍。

5. 孤儿进程

父进程比子进程先退出,那么子进程就会变成孤儿进程,并且同时变成后台程序
会造成什么影响吗?答案是不会的因为孤儿进程会被操作系统所领养

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值