蜂王交替:进程的状态的概述 - js大联盟网 - js学习者的网上家园

来源:百度文库 编辑:九乡新闻网 时间:2024/04/26 21:35:43

进程的状态的概述

发布: 2010-4-02 14:11 |  作者: 选择多灵活多 |   查看: 9次

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)",         
        "S (sleeping)",        
        "D (disk sleep)",      
        "T (stopped)",         
        "T (tracing stop)",    
        "Z (zombie)",          
        "X (dead)"             
};
=====================================================

在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.
         Uninterruptible sleep (usually IO)
         Running or runnable (on run queue)
         Interruptible sleep (waiting for an event to complete)
         Stopped, either by a job control signal or because it is being traced.
         paging (not valid since the 2.6.xx kernel)
         dead (should never be seen)
         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)
         low-priority (nice to other users)
         has pages locked into memory (for real-time and custom IO)
         is a session leader
         is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
         is in the foreground process group
======================================================

 


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


2.1)重现:

终端1)
strace -o/tmp/dd.log dd if=/dev/zero f=/share/test count=65535 bs=65535

终端2)
more /tmp/dd.log
出现大量的write和read函数调用,即调用65535次,每次读写65535个字节
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65535) = 65535

终端3)
ps aux|grep dd
root       313 16.9  0.0  3588  260 pts/1      00:12   0:10 dd if /dev/zero of test count 65535 bs 65535


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      00:31   0:12       \_ strace top
root       981  3.7  0.1 10084 7076 pts/0      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      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      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
#include
#include
#include

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      02:40   0:01 ./pisqrt    
root      3801  0.0  0.0  3700  672 pts/1      02:40   0:00 grep pisqrt
root      3791  1.0  0.0  1728  564 pts/2      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
#include
#include
#include
#include

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      03:05   0:00       \_ ./defunct
root      7281  0.0  0.0       0 pts/2      03:05   0:00           \_ [defunct ]

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

 

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

#define SIGCLD          SIGCHLD
#define SIGCHLD         17     

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

修改后的程序如下:
=================================
#include
#include
#include
#include
#include

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;
}