进程的disk sleep状态与僵尸进程

先说一下进程的状态

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),这个永远看不见

睡眠状态

Linux进程有两种睡眠状态,一种interruptible sleep,处在这种睡眠状态的进程是可以通过给它发信号来唤醒的,也是可以kill的,进程状态如下

[root@lmxe:/home]#cat /proc/949/status
Name:   sysmgt
State:  S (sleeping)

另外一种睡眠状态是uninterruptible sleep,处在这种状态的进程不接受外来的任何信号,这也是为什么之前我无法用kill杀掉这些处于D状态的进程,无论是”kill”, “kill -9″还是”kill -15″,因为它们压根儿就不受这些信号的支配。

[root@lmxe:/home]#cat /proc/949/status
Name:   sysmgt
State:  D (disk sleeping)

进程为什么会被置于uninterruptible sleep状态呢?处于uninterruptible sleep状态的进程通常是在等待IO,比如磁盘IO,网络IO,其他外设IO,如果进程正在等待的IO在较长的时间内都没有响应,那么就很会不幸地被 ps看到了,同时也就意味着很有可能有IO出了问题,可能是外设本身出了故障,也可能是比如挂载的远程文件系统已经不可访问了,我这里遇到的问题就是由互斥锁引起的,比如说我开了8个进程同时访问一个io,访问的时候势必会加锁来保护资源,那么,当一个进程正在访问的时候,其他进程如果在等待锁,那么就会进入disk sleep,当你执行kill,它不会立即响应,当锁满足条件的时候才可能响应信号。

僵尸进程

当我杀死一个子进程,而父进程又处于disk sleep时,那么子进程就会变为无人收尸的僵尸进程。子进程从运行直至其终止,从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

根据上面的描述,我们很容易去写一个程序来产生僵尸进程,如下代码:

#include 
#include 

int main()
{
    //fork a child process
    pid_t pid = fork();

    if (pid > 0)   //parent process
    {
        printf("in parent process, sleep for one miniute...zZ...\n");
        sleep(60);
        printf("after sleeping, and exit!\n");
    }
    else if (pid == 0)  
    {
        //child process exit, and to be a zombie process
        printf("in child process, and exit!\n");
        exit(0);
    }

    return 0;
}
系统中多了一个僵尸进程。但如果等父进程睡眠醒来退出之后,我们再次查看系统进程信息,发现刚才的僵尸进程不见了。

怎样避免僵尸进程的产生

不能使用 kill 后接 SIGKILL 信号这样的命令像杀死普通进程一样杀死僵尸进程,因为僵尸进程是已经死掉的进程,它不能再接收任何信号。事实上,如果系统中僵尸进程并不多的话,我们也无需去消除它们,少数的僵尸进程并不会对系统的性能有什么影响。

那么在编程时,如果能避免系统中大量产生僵尸进程呢?根据上面描述的,子进程在终止时会向父进程发 SIGCHLD 信号,Linux 默认是忽略该信号的,我们可以显示安装该信号,在信号处理函数中调用 wait 等函数来为其收尸,这样就能避免僵尸进程长期存在于系统中了。示例代码如下:

#include 
#include 
#include 
#include 
#include 

sig_atomic_t child_exit_status;

void clean_up_child_process(int signal_num)
{
    /* clean up child process */
    int status;
    wait (&status);

    /* store its exit status in a global variable */
    child_exit_status = status;
}

int main()
{
    /* handle SIGCHLD by calling clean_up_child_process  */
    struct sigaction sigchild_action;
    memset(&sigchild_action, 0, sizeof(sigchild_action));
    sigchild_action.sa_handler = &clean_up_child_process;
    sigaction(SIGCHLD, &sigchild_action, NULL);

    /* fork a child, and let the child process dies before parent */
    pid_t c_pid;
    c_pid = fork();
    if (c_pid > 0)
    {
        printf("in parent process, and sleep for on mininute...zZ...\n");
        sleep(60);
    }
    else if(c_pid == 0)
    {
        printf("in child process, and exit now\n");
        exit(0);
    }
    else
    {
        printf("fork failed!\n");
    }

    return 0;
}   





你可能感兴趣的:(Linux,System,Program)