【Linux】历程状态

打印 上一主题 下一主题

主题 1089|帖子 1089|积分 3267

1. 阻塞

历程由于等待某种条件就绪,而导致的一种不推进的状态
1. 举例



  • 偶然候电脑卡,是由于开启了太多软件,为什么启动太多程序会卡呢?
  • 启动了太多的程序相称于启动了太多的历程,操作体系在用你的cpu调度时,调度不外来了,当前正在调度的在运行,没有调度的相称于卡了
  • 阻塞就是历程卡住了
2. 为什么要阻塞?



  • 历程要通过等待的方式,等详细的资源被别人使用完成后,再被自己使用
  • 阻塞:历程等待某种资源就绪的过程
  • 以硬件为例,资源可以看作磁盘、网卡、显卡等外设,
    比如去银行存钱,存钱申请的单子没了,直接去存,工作职员就会说由于存钱的单子没有就绪所以不能存,再去等一会,等有单子了再来存

  • 没有继承执行存钱的行为,当前所处状态为阻塞状态
3.操作体系层面上怎样理解历程等待某种资源就绪呢?

资源

操作体系对于磁盘、网卡、显卡等 资源通过 先描述,在组织进行管理,把设备用结构体描述起来,再用链表组织起来
管理的本质详细解释点击这里

历程

存在大量的历程,操作体系要进行管理,也要先描述,在组织,就存在了大量的task_struct的结构体,每一个历程都是由task_struct定义出来的对象
历程组成的理解点击这里



  • 将dev结构体内添加一个队列指针,若历程必要在设备资源等待,则把task_struct链接到设备资源队列的尾部
4. 总结

阻塞:阻塞就是不被调度
肯定是由于当进步程等待某种资源就绪
肯定是历程task_struct结构体必要在某种操作体系管理的资源下进行排队

2.挂起




  • 当历程被CPU调度时进行下载,由于网断了,导致下载停止,从而使历程链接到网卡设备队列的尾部,造成阻塞
    从而使CPU调用其他历程





  • 若体系中内存的资源特别紧张,把占据内存的并且闲置的数据和代码,互换到磁盘当中,把内存中这部门的代码和数据释放,当该历程等待的资源就绪时,再把存入磁盘的数据和代码换入内存中,并把历程放入CPU中运行
  • 把代码和数据暂时性由操作体系互换到磁盘时,此时的历程称之为挂起状态
3.Linux历程状态

  1. static const char * const task_state_array[] = {
  2. "R (running)", /* 0 */
  3. "S (sleeping)", /* 1 */
  4. "D (disk sleep)", /* 2 */
  5. "T (stopped)", /* 4 */
  6. "t (tracing stop)", /* 8 */
  7. "X (dead)", /* 16 */
  8. "Z (zombie)", /* 32 */
  9. };
复制代码


  • Linux源码当中历程所对应的状态

  1. struct task_struct
  2. {
  3. int status;//0代表R状态  1代表s状态 4代表T状态
  4. //....
  5. }
复制代码
task_struct 是一个结构体,内部会包罗各种属性,就有状态,通过0、1、2等数字找到对应的历程状态
1. R状态

历程只要是R状态,就肯定是在CPU运行吗?

不肯定



  • 每个历程当中有自己的运行队列,只要该历程在队列中进行排队,运行的历程就会在运行的队列中排队,CPU调度历程时,从队列中挑选指定的历程运行就可以了,这种运行状态就叫做R状态
  • R状态不直接代表历程在运行,代表该历程在运行队列中排队
证明当进步程运行状态

生成程序

创建makefile并输入如下内容
  1.    1 mytest:test.c//nakefile
  2.   2   gcc -o mytest test.c
  3.   3 .PHONY:clean
  4.   4 clean:
  5.   5  rm -f mytest  
复制代码

创建test.c并输入如下内容
  1. #include<stdio.h>//test.c
  2.   2 int main()
  3.   3 {
  4.   4   while(1)
  5.   5   {
  6.   6     printf("我在运行吗\n");                                                                                                                                            
  7.   7   }                                                                                                                        
  8.   8   return 0;                                                                                                                 
  9.   9 }                                                                                                                              
复制代码

  1. [yzq@VM-8-8-centos my]$ make
  2. gcc -o mytest test.c
  3. [yzq@VM-8-8-centos my]$ ./mytest
复制代码
make产生可执行程序mytest,./mytest执行可执行程序
查察历程

赋值SSH渠道生成终端2
在保证终端1中的mytest运行的情况下,在终端2中输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep

创建终端的方法及指令详细含义点击这里
  1. [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2.   PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615 30533 30533 25615 pts/0    30533 S+    1002   0:00 ./mytest
复制代码
发现此时历程状态为S+ (+号后面会说)
S称为休眠状态,不是R状态


test.c代码修改如下
  1. #include<stdio.h>//test.c
  2.   2 int main()
  3.   3 {
  4.   4   while(1)
  5.   5   {
  6.   6    // printf("我在运行吗\n");                                                                                                                                       
  7.   7   }                  
  8.   8   return 0;           
  9.   9 }                     
  10.       
复制代码

在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
  1. [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615 31695 31695 25615 pts/0    31695 R+    1002   0:10 ./mytest
复制代码


  • 发现历程状态由S+变为R+
  • printf循环打印,就代表要频繁访问显示器设备,循环中的printf本质就是向外设打印消息,当CPU执行printf代码时,频繁打印外设不肯定就绪,历程可能在外设中排队,等资源就绪时,再把历程放入CPU上把效果写入外设中
  • 分析第一次查到的S状态是阻塞状态的一种,是以休眠状态进行阻塞的
2. S休眠状态——可停止休眠

本质是一种阻塞状态
修改test.c代码如下
  1. 1 #include<stdio.h>
  2.   2 int main()
  3.   3 {
  4.   4   while(1)
  5.   5   {
  6.   6    int b=0;
  7.   7    scanf("%d",&b);
  8.   8    printf("%d\n",b);
  9.   9   }                                                                                                                                                                  
  10. 10   return 0;                                                                                                               
  11. 11 }     
复制代码


  • ./mytest运行,并输入10 10
在保证终端1中的mytest运行的情况下,在终端2中再次输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep

  1. [yzq@VM-8-8-centos my]$  ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615  5793  5793 25615 pts/0     5793 S+    1002   0:00 ./mytest
复制代码


  • 历程状态为S,历程没有被调度,等键盘资源 即在键盘中输入数据

  1. [yzq@VM-8-8-centos my]$ ./mytest
  2. 10
  3. 10
  4. ^C//可以被终止
复制代码


  • 在终端1中ctrl c,停止mytest运行,S状态被停止
3.D休眠状态 ——不可停止休眠

想要往磁盘写入100MB的数据,由于磁盘写入数据很慢,所以历程把自己设置成阻塞状态,若内存资源特别紧张,操作体系就想要把这个阻塞状态的历程干掉,可是此时磁盘依旧还在写入数据,如许做就会使磁盘写入数据失败,终极就会使100MB的数据丢失


  • 若该历程的休眠状态为D状态,使操作体系无法干掉历程,就能解决这个问题
4.T状态——暂停状态

使用kill下令,向指定的历程发信号

  1. [yzq@VM-8-8-centos my]$ kill -l
  2. 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
  3. 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
  4. 11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
  5. 16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
复制代码


  • 9号信号SIGKILL 表现干掉 历程
  • 18号信号 SIGCONT 表现让当进步程继承运行
  • 19号信号 SIGSTOP 表现暂停一个历程
暂停历程

test.c内容如下
  1. 1 #include<stdio.h>
  2. #include<unistd.h>
  3.   2 int main()
  4.   3 {
  5.   4   int count=0;
  6.   5   while(1)
  7.   6   {                                                                                                                                                                  
  8.   7    printf("还在运行吗:%d\n",count++);
  9.        sleep(1);
  10.   8   }                                 
  11.   9   return 0;
  12. 10 }         
复制代码

保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep ,并输入 kill-19+PID值,终端1自动停止运行


  1. [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615 14791 14791 25615 pts/0    14791 S+    1002   0:00 ./mytest
复制代码
再次在终端2中查询历程,发现历程状态由S+变为T
继承历程

若想让历程继承运行,在终端2中使用 kill -18 +PID值

带加号的问题

  1. yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615 14791 14791 25615 pts/0    25615 S     1002   0:00 ./mytest
复制代码


  • 在暂停历程,又继承历程后,当进步程状态 为S,不带加号了


  • 在终端1使用ctrl c,程序不停止了
  • 历程状态带+,历程是在前台运行的,可以使用ctrl c 停止
  • 历程状态不带+,历程是在背景运行的,可以正常执行shell指令,但在背景继承还会执行自己的代码
  • 此时若想停止历程,使用 kill -9 +PID值 干掉历程
5. X状态(死亡状态)&&Z状态(僵尸状态)



  • X死亡状态只是一个返回状态,你不会在任务列表里看到这个状态,所以这里这是举例时提及,但不会验证
  • 我们创建历程,是为了历程帮我们服务,同时也关心效果,而main函数的返回值是历程的退出码
查察当进步程退出码

用于判定历程效果是否正确 echo $?
修改test.c的内容如下
  1. include<stdio.h>
  2.   2 #include<unistd.h>
  3.   3 int main()
  4.   4 {
  5.   5   int count=2;
  6.   6   if(count==2)
  7.   7   {
  8.   8     return 0;
  9.   9   }
  10. 10   else
  11. 11   {
  12. 12     return 4;
  13. 13   }                                                                           
  14. 14 }
  15. ~
复制代码

使用make生成可执行程序mytest,./mytest执行可执行程序
  1. [yzq@VM-8-8-centos my]$ make
  2. gcc -o mytest test.c
  3. [yzq@VM-8-8-centos my]$ ./mytest
  4. [yzq@VM-8-8-centos my]$ echo $?0
复制代码


  • 分析mytest历程的退出码是0,历程效果正确
对于Z状态的理解

假如一个历程退出了,立马X状态,立马退出,你作为父历程,有没偶然机拿到退出效果呢?


  • linux当历程退出的时候,一般历程不会立即彻底退出,而是要维持一个状态叫做Z状态,也叫做僵尸状态
  • 方便后续父历程读取子历程退出的退出效果
怎样让我们看到僵尸状态呢?


  • 子历程退出,但是不要采取子历程
举例



  • 假设你在某一天看到路上有一个人躺着,你报了警,警员来了后,先封锁现场,再来人确认躺者是否死亡,法医确认这个人的死因,然后关照家属等一系列事情才能进行
  • 被120、法医检查时,这个人所处的状态就叫僵尸状态
  • 为了方便别人从他的残留信息中甄别他死亡的原因是什么
验证Z状态

修改test.c内容如下
  1. #include<stdio.h>
  2.     2 #include<unistd.h>
  3.     3 int main()
  4.     4 {
  5.     5   pid_t id=fork();
  6.     6   if(id==0)
  7.     7   {
  8.     8     //子进程
  9.     9     while(1)
  10.    10     {
  11.    11       printf("我是子进程,我在运行,pid:%d,ppid:%d",getpid(),getppid());
  12.    12       sleep(1);
  13.    13     }
  14.    14   }
  15.    15   else if(id>0)
  16.    16   {
  17.    17     //父进程
  18.    18     while(1)
  19.    19     {
  20.    20       printf("我是父进程,我在运行,pid:%d,ppid:%d",getpid(),getppid());
  21.    21       sleep(1);
  22.    22   }                                                                          
  23.    23   }
  24.     }
复制代码

保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
  1. [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  2. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  3. 25615 18251 18251 25615 pts/0    18251 S+    1002   0:00 ./mytest
  4. 18251 18252 18251 25615 pts/0    18251 S+    1002   0:00 ./mytest
  5. [yzq@VM-8-8-centos my]$ kill -9 18252
  6. [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
  7. PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
  8. 25615 18251 18251 25615 pts/0    18251 S+    1002   0:00 ./mytest
  9. 18251 18252 18251 25615 pts/0    18251 Z+    1002   0:00 [mytest] <defunct>
复制代码


  • 当父子历程都运行时,两者历程状态都为S+
  • 当使用 kill - 9 +PID值将子历程干掉后,再次使用指令查询历程,发现子历程为僵尸状态,父历程为S+
僵尸状态危害



  • 在父子历程中,若父历程不停不读取,子历程就会处于Z状态
  • 在父子历程中,若子历程不停处于僵尸状态(即退出子历程,但不采取子历程),就会在内存中不停生存该历程
  • 若父历程创建多个子历程,就是不采取,就会造成内存资源的浪费

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表