进程终止

来源:互联网 发布:dos下运行java程序 编辑:程序博客网 时间:2024/04/29 15:20

通常情况下,要终止一个进程,要么在程序中调用exit()要么等待程序退出main函数。

还有一种异常情况,就是收到一个signal。例如:一个进程对于接收SIGBUS(总线异常), SIGSEGV(段错误), 和SIGFPE(浮点溢出)信号的默认处理都是终止该进程。

在终端按Ctrl+c 会产生一个SIGINT发送给正在运行的进程;在程序中调用abort会给自己发送一个SIGABRT信号。这些都会使接受信号的进程终止(对信号默认处理)。SIGTERM需要kill命令行发送,收到的进程默认终止。终止进程最强有力的信号是SIGKILL,只要进程收到该信号不会出现阻塞,会立刻终止。

kill命令行可以发送任何signal,只要携带不同参数便会发送指定的signal.如:kill -KILL pid
也可以在程序中调用kill函数去终止子进程,如:kill(child_pid,SIGTERM);返回值为0表示正确,非0错误。

1.等待进程终止

linux 系统是多任务的,当在程序中创建一个子进程,无法预知父进程和子进程哪个先被系统调度,有可能是同时运行。如果要得知子进程终止时返回的状态,就必须用一种方式 让子进程在父进程之前终止。
针对这个问题,linux提供了一组家族函数:wait. 这个函数可以等待子进程执行完毕,并且获取子进程的终止状态,释放子进程资源。根据wait家族函数,可以获取已经终止进程的信息,也可以用来等待某个进程终止。
wait家族中最简单的方法就是wait,调用之后程序阻塞,直到某个子进程终止。

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int spawn(char* program,char** arg_list){        pid_t child_pid;        child_pid = fork();        if(child_pid!=0)        {                printf("This is the parent process.\n");                return child_pid;        }        else        {                printf("This is the child process.\n");                execvp(program,arg_list);                fprintf(stderr,"an error occurred in execvp.\n");                abort();        }}int main(){        int child_status;        char* arg_list[]={                        "ls",                        "-l",                        "/",                        NULL};        spawn("ls",arg_list);        wait(&child_status);        if(WIFEXITED(child_status))        {                printf("the child process exited normally,with exit code %d\n",WEXITSTATUS(child_status));        }        else                printf("the child process exited abnormally\n");       return 0;}

调用wait会阻塞,直到子进程终止或者出错才会返回。WIFEXITED可以根据子进程返回的状态child_status判断子进程是正常终止还是异常终止(如:收到signal默认处理)。WEXITSTATUS根据child_status得到子进程结束时返回code。
wait家族函数介绍:
waitpid : 指定一个子进程,专门等待这个子进程而不是等待所有的子进程。
wait3 : 可以返回终止子进程之后CPU的统计数据
wait4 : 返回指定的子进程状态改变,并且会返回关于子进程使用资源的信息

2.僵尸进程
如果在程序中调用wait,当子进程终止后会将状态信息返回给wait,并且清除资源。如果在程序中没有调用wait,子进程结束之后所有的状态都会丢失,分配的资源也没有完全释放,这样的进程称为僵尸进程(Zombie Process)。
当一个进程结束后,对于资源清除工作是它父进程的责任。当子进程在wait之前结束会成为僵尸进程,执行到wait时会获取到结束的状态并清除子进程资源;如果调用wait时子进程还没终止,wait会阻塞知道子进程结束。

3.异步清除子进程

在程序中存在以下两个问题:
1)wait在程序中调用早了会出现阻塞,之后的程序不能尽快运行,如:

int main(){int count=0;    pid_t child_pid;        child_pid = fork();        if(child_pid!=0)        {            printf("This is the parent process.\n");        }        else        {                printf("This is the child process.\n");                execvp(program,arg_list);        }wait(&child_status);count++;return 0;}
如果需要在子进程结束前执行count++;是不可能的,只有等到子进程结束wait才返回。

2)wait在程序中调用晚了会出现部分短暂的僵尸进程,短暂的占用资源,如:

int main(){int count=0;    pid_t child_pid;        child_pid = fork();        if(child_pid!=0)        {            printf("This is the parent process.\n");        }        else        {                printf("This is the child process.\n");                execvp(program,arg_list);        }data_compute();wait(&child_status);return 0;}

data_compute();是一个数据处理函数,需要占用很多的资源。如果子进程在data_compute();调用前已经结束了,但成为僵尸进程资源没有被释放,会导致data_compute();对资源紧缺。

解决以上问题有两种方法:一种是调用wiat3,wait4用标志位WNOHANG实现非阻塞,可以在合适的地点调用。


另一种是用signal,当子进程终止时会发给父进程一个SIGCHLD信号,父进程可以在SIGCHLD信号处理函数中调用wait清理子进程。
以下为一个使用SIGCHLD进行wait子进程的程序:

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>#include <string.h>#include <signal.h>sig_atomic_t child_exit_status;void clean_up_child_process(int signal_number){        int status;        wait(&status);        child_exit_status = status;        printf("The singnal_number is :%d\n",signal_number);        printf("The child exit stauts:%d\n",WEXITSTATUS(status));}int spawn(char* program,char** arg_list){        pid_t child_pid;        child_pid = fork();        if(child_pid!=0)        {                printf("This is the parent process.\n");                return child_pid;        }        else        {                printf("This is the child process.\n");                execvp(program,arg_list);                fprintf(stderr,"an error occurred in execvp.\n");                abort();        }}int main(){       struct sigaction sigchld_action;        memset(&sigchld_action,0,sizeof(sigchld_action));        sigchld_action.sa_handler = &clean_up_child_process;        sigaction(SIGCHLD,&sigchld_action,NULL);        printf("SIGCHLD:%d\n",SIGCHLD);        char* arg_list[]={                        "ls",                        "-l",                        "/",                        NULL};        spawn("ls",arg_list);        sleep(10);       return 0;}

子进程的退出状态child_exit_status在信号处理函数中赋值,为了在访问时不被打断,这里用原子类型sig_atomic_t

0 0