Linux 进程状态简述

简介:

B_0018.gif大家都知道Linux是一个多用户,多任务的操作系统,可以同时运行多个程序,必然就会产生多个进程,而每个进程会有不同的状态。


下面是我对进程的个人理解,剖析的比较浅显,而且难免有错误,请指正


    那这些状态都有哪些呢??? 

wKioL1eivYGilY22AABzE1jj8dI679.png

  1. Linux进程状态:R (TASK_RUNNING),可执行状态&运行状态(在run_queue队列里的状态)

  2. Linux进程状态:S (TASK_INTERRUPTIBLE),可中断的睡眠状态, 可处理signal

  3. Linux进程状态:D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态, 可处理signal, 有延迟

  4. Linux进程状态:T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态, 不可处理signal, 因为根本没有时间片运行代码

  5. Linux进程状态:Z (TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。不可被kill, 即不响应任务信号, 无法用SIGKILL杀死


下面分别作介绍

1. R(Running)    可执行状态

  只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。

    很多操作系统教科书将正在CPU上执行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为 TASK_RUNNING状态。

2.   S(Sleeping)   浅度睡眠态(即可中断的睡眠状态)

      处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。

      通过ps命令我们会看到,一般情况下,进程列表中的绝大多数进程都处于TASK_INTERRUPTIBLE状态(除非机器的负载很高)。毕竟CPU就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来。

3.  D(TASK_UNINTERRUPTIBLE) 深度睡眠态(即不可中断的睡眠状态)
        与TASK_INTERRUPTIBLE状态类似,进程也处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。 绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。否则你将惊奇的发现,kill -9竟然杀不死一个正在睡眠的进程了!于是我们也很好理解,为什么ps命令看到的进程几乎不会出现TASK_UNINTERRUPTIBLE状态,而总是 TASK_INTERRUPTIBLE状态。

4. T(Stopped)    暂停状态

        进程暂停执行接受某种处理。如正在接受调试的进程处于这种状态,Linux 使用TASK_STOPPED 宏表示此状态。

        当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状 态。可向其发送SIGCONT信号让进程转换到可运行状态。进程在调试期间接收到任何信号均会进入该状态。

wKioL1eivdLxlfMJAAEz2EoSylo574.png

当按Ctrl+z时,系统内核会向前台进程发送SIGSTOP信号,相似的,命令bg/fg使bash发送SIGCONT信号给进程。

wKiom1eiwOfSJljlAAC6HW29-3M811.png

事实上,直接在终端发送信号会使进程有相同的反应

1
2
kill -STOP PCB_NUM
kill -CONT PCB_NUM

wKioL1eiwGyjH4eXAAEA7SMk3uk274.png

5. Z (TASK_DEAD - EXIT_ZOMBIE)僵尸 状态

    孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

危害:

    系统提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。


测试孤儿进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
 
int  main()
{
     pid_t pid;
     //创建一个进程
     pid = fork();
     //创建失败
     if  (pid < 0)
     {
         perror ( "fork error...\n" );
         exit (1);
     }
     //子进程
     if  (pid == 0)
     {
         printf ( "i am the child process...\n" );
         //输出进程ID和父进程ID
         printf ( "child process pid: %d\tppid:%d\n" ,getpid(),getppid());
         //睡眠5s,保证父进程先退出
         sleep(5);
         printf ( "child process pid: %d\tppid:%d\n" ,getpid(),getppid());
         printf ( "child process exit...\n" );
     }
     //父进程
     else
     {
         printf ( "I am the father process!...\n" );
         //父进程睡眠1s,保证子进程输出进程id
         sleep(1);
         printf ( "father process is  exited.\n" );
     }
     return  0;
}

测试结果:

wKiom1ejMQWyHTxHAABx3JhGxjM100.png

测试僵尸进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
 
int  main()
{
     pid_t pid;
     pid = fork();
     if  (pid < 0)
     {
         perror ( "fork error!...\n" );
         exit (1);
     }
     else  if  (pid == 0)
     {
         printf ( "I am the child process,I am exitting...\n" );
         exit (0);
     }
     printf ( "I am the father process,I will sleep 2 seconds...\n" );
     //等待子进程先退出
     sleep(2);
     //输出进程信息
     system ( "ps -o pid,ppid,state,tty,command" );
     printf ( "father process is exiting...\n" );
     return  0;
}

测试结果:

wKioL1elmKLAstJOAACBD4RCF9Q066.png

处理僵尸进程:

(1)通过信号机制

  子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。测试程序如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
 
static  void  sig_child( int  signo);
 
int  main()
{
     pid_t pid;
     //创建捕捉子进程退出信号
     signal (SIGCHLD,sig_child);
     pid = fork();
     if  (pid < 0)
     {
         perror ( "fork error:" );
         exit (1);
     }
     else  if  (pid == 0)
     {
         printf ( "I am child process,pid id %d.I am exiting.\n" ,getpid());
         exit (0);
     }
     printf ( "I am father process.I will sleep two seconds\n" );
     //等待子进程先退出
     sleep(2);
     //输出进程信息
     system ( "ps -o pid,ppid,state,tty,command" );
     printf ( "father process is exiting.\n" );
     return  0;
}
 
static  void  sig_child( int  signo)
{
      pid_t        pid;
      int         stat;
      //处理僵尸进程
      while  ((pid = waitpid(-1, &stat, WNOHANG)) >0)
             printf ( "child %d terminated.\n" , pid);
}

测试结果:

wKioL1ejUNCD2sJMAACeByrxf8g148.png

(2)fork两次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
 
int  main()
{
     pid_t  pid;
     //创建第一个子进程
     pid = fork();
     if  (pid < 0)
     {
         perror ( "fork error:" );
         exit (1);
     }
     //第一个子进程
     else  if  (pid == 0)
     {
         //子进程再创建子进程
         printf ( "I am the first child process.pid:%d\tppid:%d\n" ,getpid(),getppid());
         pid = fork();
         if  (pid < 0)
         {
             perror ( "fork error:" );
             exit (1);
         }
         //第一个子进程退出
         else  if  (pid >0)
         {
             printf ( "first procee is exited.\n" );
             exit (0);
         }
         //第二个子进程
         //睡眠3s保证第一个子进程退出,这样第二个子进程的父亲就是init进程里
         sleep(3);
         printf ( "I am the second child process.pid: %d\tppid:%d\n" ,getpid(),getppid());
         exit (0);
     }
     //父进程处理第一个子进程退出
     if  (waitpid(pid, NULL, 0) != pid)
     {
         perror ( "waitepid error:" );
         exit (1);
     }
     exit (0);
     return  0;
}

测试结果:

wKioL1ejWTGwXKGiAABMiL7OPlg145.png

详情可以查看:

https://idea.popcount.org/2012-12-11-linux-process-states/

http://www.cnblogs.com/Anker/p/3271773.html



本文转自 七十七快 51CTO博客,原文链接:http://blog.51cto.com/10324228/1834277

相关文章
|
29天前
|
Shell Linux 调度
【Shell 命令集合 系统管理 】Linux 调整进程优先级 renice命令 使用指南
【Shell 命令集合 系统管理 】Linux 调整进程优先级 renice命令 使用指南
36 0
|
26天前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
65 0
|
3天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
4天前
|
监控 Linux
linux监控指定进程
请注意,以上步骤提供了一种基本的方式来监控指定进程。根据你的需求,你可以选择使用不同的工具和参数来获取更详细的进程信息。
10 0
|
5天前
|
消息中间件 监控 Linux
Linux进程和计划任务管理
通过这些命令和工具,你可以有效地管理Linux系统中的进程和计划任务,监控系统的运行状态并保持系统的稳定和可靠性。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
98 2
|
8天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
18 3
|
12天前
|
监控 Java Linux
linux下监控java进程 实现自动重启服务
linux下监控java进程 实现自动重启服务
|
13天前
|
监控 Linux Shell
初识Linux下进程2
初识Linux下进程2
|
13天前
|
Linux 编译器 Windows
【Linux】10. 进程地址空间
【Linux】10. 进程地址空间
19 4
|
17天前
|
算法 Linux Shell
linux系统的进程管理
linux系统的进程管理
19 2