通常情况下,要终止一个进程,要么在程序中调用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; }
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才返回。
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; }
另一种是用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; }