Linux下进程状态深入分析

1)进程的状态的概述:

1.1)Running(R),运行或将要运行
1.2)Interruptible(S),被阻断而等待一个事件,可能会被一个信号激活
1.3)Uninterruptible(D),被阻断而等待一个事件,不会被信号激活
1.4)Stopped(T),由于任务的控制或者外部的追踪而被终止,比如:strace
1.5)Zombie(Z),僵死,但是它的父进程尚未调用wait函数.
1.6)Deal(X),这个永远看不见

在内核源代码中的定义如下:


=====================================================
/usr/src/linux/fs/proc/array.c


static const char *task_state_array[] = {
        "R (running)",          /*  0 */
        "S (sleeping)",         /*  1 */
        "D (disk sleep)",       /*  2 */
        "T (stopped)",          /*  4 */
        "T (tracing stop)",     /*  8 */
        "Z (zombie)",           /* 16 */
        "X (dead)"              /* 32 */
};
=====================================================

在ps命令的帮助中定义如下:

PROCESS STATE CODES
       Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to
       describe the state of a process.
       D    Uninterruptible sleep (usually IO)
       R    Running or runnable (on run queue)
       S    Interruptible sleep (waiting for an event to complete)
       T    Stopped, either by a job control signal or because it is being traced.
       W    paging (not valid since the 2.6.xx kernel)
       X    dead (should never be seen)
       Z    Defunct ("zombie") process, terminated but not reaped by its parent.

       For BSD formats and when the stat keyword is used, additional characters may be displayed:
       <    high-priority (not nice to other users)
       N    low-priority (nice to other users)
       L    has pages locked into memory (for real-time and custom IO)
       s    is a session leader
       l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
       +    is in the foreground process group
======================================================

 


2)分析不可被中断的睡眠进程:


2.1)重现:

终端1)

vi test.c
#include <unistd.h>

void main() {
if (!vfork()) sleep(100);
}
gcc test.c -o test

./test

终端2)
ps aux|grep test
root     19884  0.0  0.0   3640   360 pts/0    D+   16:38   0:00 ./test
root     19885  0.0  0.0   3640   360 pts/0    S+   16:38   0:00 ./test


2.2)分析:
系统进入这种不可中断是很少发生的,即使发生也是一个短暂的状态,引起这种状态的发生一般是驱动程序.
例如:驱动程序可能正在特殊的设备上等待通过检测的响应,但又要保证自己不在可中断睡眠状态(S)被中断.所以驱动程序会把进程切换到不可中断的睡眠状态,直到硬件已返回到已知状态.


可以通过访问一个慢设备来观察不可中断的睡眠状态,比如CDROM这样的设备
例如:
dd if=/dev/cdrom f=/dev/null &


进程在一个不可中断的状态是十分危险的,你不能用kill -9杀掉它
例如:
一个有问题的驱动程序访问一个有问题的设备,设备不给驱动程序响应,驱动程序永远得不到响应,而又永远等待响应.

 

 

3)分析被跟踪或被停止的进程状态(T)


3.1)重现被跟踪时的状态:

终端1)
strace top

终端2)
ps auxf|grep top
root       980  9.4  0.0  1716  608 pts/0    S    00:31   0:12  |       \_ strace top
root       981  3.7  0.1 10084 7076 pts/0    T    00:31   0:05  |           \_ top

在用strace跟踪top执行的时候,top进程为T的状态


3.2)重现被停止的进程状态:

停止进程有三种手段:
3.2.1)发送SIGTSTP信停止进程.
-SIGTSTP的信号相当于CTRL+Z的组合键来终止正在前台运行的进程.

终端1)
vi /etc/passwd

终端2)
kill -SIGTSTP 12029

查看进程状态:
ps auxf

root     10297  0.0  1.0   5124  2696 pts/0    Ss+  Dec16   0:00      \_ -bash
root     12029  0.0  0.8   5348  2200 pts/0    T    05:15   0:00      |   \_ vi test.c


终端1)
查看放到后台的作业
jobs
[1]+  Stopped                 vi test.c

用fg将作业切换到前台
fg


3.2.2)进程自已终止自己,标准输入引起进程停止

一个终端利用常规的后台和前台进程管理进程,一个终端有且只有一个前台进程,只有这个进程可以接受键盘的输入,其它任何开始于终端的进程都被认为是后台进程,但是当后台进程试图从标准输入读取数据时,终端将发送一个SIGTTIN终端它,因为这里只有一个输入设备键盘,只能用于前台进程.
这里的前后台进程概念仅限于终端的范围.

SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN 信号. 缺省时这些进程会停止执行.


终端1)

尝试在后台运行read命令,因为后台进程不能从终端获取标准输入,所以进程将会收到信号SIGTTIN,使进程进入停止状态.
read x &
[1] 12057

[1]+  Stopped                 read x

终端2)
jobs
[1]+  Stopped                 read x
查看进程,12057这个PID就是read x&,现在看到是-bash,它的状态已经是T了
ps aux
root     12057  0.0  0.5   5124  1476 pts/0    T    05:26   0:00 -bash

用SIGCONT来唤醒
kill -SIGCONT 12057

终端1)
输入回车后,12057的进程依然会进入停止状态,也就是阻塞,只有会放到前台后,它才能完成输入.
fg
read x
hello

 

3.2.3)进程自已终止自己,标准输出引起进程停止

终端有一个tostop(终端输出终止)设置,在大多数系统里默认是关闭.
当是关闭的状态时,后台进程可以随时在终端写内容,如果是开启状态时,后台进程向标准输出写数据时就会被终止.


开启tostop
stty tostop

向标准输出写数据,被停止了
echo hello world &
[1] 12125

[1]+  Stopped                 echo hello world

jobs
[1]+  Stopped                 echo hello world

关闭tostop
stty -tostop

jobs
[1]+  Stopped                 echo hello world

向标准输出写数据恢复正常了
fg
echo hello world
hello world

 

 

4)分析进程的可中断睡眠态与运行态


 
编写一个小程序测试睡眠态与运行态之后的转换 :
=====================================================
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>

void
run_status(void)
{
    double pi = M_PI;
    double pisqrt;
    long i;
  
    for (i=0; i<100000000; ++i){
    pisqrt = sqrt(pi);
    }
}

int
main (void)
{
    run_status();   
    sleep(10);
    run_status();

    exit(EXIT_SUCCESS);
}
======================================================
编译链接后:
gcc -Wall -o pisqrt a.c -lm


终端1)
监控pisqrt进程
watch -n 1 "ps aux|grep pisqrt|grep -v ps|awk '{print $2}'|sort -k 8"


终端2)
strace ./pisqrt
显示如下:
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200X\1"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1572440, ...}) = 0
old_mmap(NULL, 1279916, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x49e000
old_mmap(0x5d1000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x132000) = 0x5d1000
old_mmap(0x5d4000, 10156, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x5d4000
close(3)                                = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e3a60, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xb75e4000, 75579)               = 0

此时切换到终端1看pisqrt进程状态,此时为R状态:
root      3792 99.9  0.0  1516  268 pts/2    R    02:40   0:01 ./pisqrt    
root      3801  0.0  0.0  3700  672 pts/1    S    02:40   0:00 grep pisqrt
root      3791  1.0  0.0  1728  564 pts/2    S    02:40   0:00 strace ./pisqr

之后pisqrt进程进入S状态,因为执行了sleep(10)函数,10秒之后pisqrt再次进入R状态.最终退出.

分析:
pisqrt占用CPU时间片时状态为R,而在调用sleep函数后为S,系统大多数进程状态都为S,比如APACHE和ORACLE,
而处于S状态不一定是调用了sleep函数,因为IO也会让进程处于睡眠态.
而我们可以启动多个pisqrt程序,这时在系统中会有多个R状态的进程.也就是说CPU个各数与R进程是没有直接关联的.

 


5)分析进程的僵死态(Z)

当一个进程退出时,它并不是完全消失,而是等到它的父进程发出wait系统调用才会完全消失,除非父进程发出wait系统调用终止进程,
否则该进程将一直处于所谓的僵死状态,等待它的父进程终止它.如果父进程终止了运行而没有撤销子进程,那么这些进程将被进程init收养.
init进程定期调用wait来收养这些未被撤消的进程.

先制造一段僵尸程序,如下:
=============================
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int
main (){
    if(!fork()){
    printf("child pid=%d\n", getpid());
    exit(5);
}
    sleep(20);
    printf("parent pid=%d\n", getpid());
    exit(EXIT_SUCCESS);
}
===========================================
编译
gcc -Wall defunct.c -o defunct

终端1:
watch -n 1 "ps auxf|grep defunct|grep -v ps|grep -v grep|awk '{print $2}'|sort -k 8"


终端2:
执行./defunct

查看终端1:
root      7280  0.0  0.0  1380  240 pts/2    S    03:05   0:00  |       \_ ./defunct
root      7281  0.0  0.0     0    0 pts/2    Z    03:05   0:00  |           \_ [defunct <defunct>]

20秒后查看终端2:
child pid=7281
parent pid=7280

 

关于信号集的描述:/usr/include/bits/signum.h

#define SIGCLD          SIGCHLD /* Same as SIGCHLD (System V).  */
#define SIGCHLD         17      /* Child status has changed (POSIX).  */

在上面程序的基础上加入wait函数即可将SIGCHLD信号回收

修改后的程序如下:
=================================
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int
main (){
    int status,i;
    if(!fork()){
    printf("child pid=%d\n", getpid());
    exit(5);
}
    wait(&status);
    i = WEXITSTATUS(status);
    sleep(20);
    printf("parent pid=%d,child process exit/status=%d\n", getpid(),i);
    exit(EXIT_SUCCESS);
}
==========================================


6)最后的进程X (dead)
指死掉的进程

 

最后4种附加的状态....
W状态:不驻留内存
<状态:nice小于0
N状态:nice大于0
L状态:有锁住的页面

这部分在ps的源代码(output.c)有描述:
===================================
static int
pr_stat(void)
{
        int end = 0;
        outbuf[end++] = pp->state;
        if (pp->rss == 0 && pp->state != 'Z')
                outbuf[end++] = 'W';
        if (pp->nice < 0)
                outbuf[end++] = '<';
        if (pp->nice > 0)
                outbuf[end++] = 'N';
        if (pp->vm_lock)
                outbuf[end++] = 'L';
        outbuf[end] = '\0';
        return end;
}
=====================================

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值