五种正常终止的方式:
异常终止的3种方式:
调用abort | 调用abort。它产生SIGABRT信号,所以是下一种异常终止的一种特 |
接到一个信号 | 当进程接收到某个信号时。信号可由进程本身(如调用abort函数)、其他进程和内核产生。例如,进程引用地址空间之外的存储单元,或者除以0,内核就会为该进程产生相应的信号 |
最后一个线程对取消请求做出响应 |
进程终止状态
- 对上述任意一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于exit、_exit、_Exit,实现这一点的方法是,将其退出状态作为参数传送给函数。在异常终止情况,内核(不是进程本身)产生一个指示其异常终止原因的终止状态( termination status)
- 在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态
子进程在父进程之前终止,父进程如何获取子进程的终止状态?
- 内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到有关信息。这种信息至少包括进程ID、该进程的终止状态、以反该进程使用的CPU时间总量。内核可以释放终止进程所使用的所有存储器,关闭其所有打开文件
#include
void exit(int status);
void _Exit(int status);
#include
void _exit(int status);
参数可取得值
- 0:表示进程正常结束
- 1~255:表示进程出错结束
C标准指定了两个常量也可以传递给exit作为参数使用:
- EXIT_SUCCESS:成功退出
- EXIT_FAILURE:出错退出
//stdio.h中 #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1
未定义的进程终止状态
- 如果程序符合下面的某一条件,那么就说这个进程的终止状态是未定义的:
- ①若调用这些函数时不带终止状态
- ②main执行了一个无返回值的return语句
- ③main没有声明返回类型为整型
- 案例:执行下面没有返回值的main函数,其终止码是随机的
#include
main() { printf("hello, world\n"); }
- 如果使用gcc的1999 ISO C标准,就会提示警告信息
- 如果main的返回类型为整型,并且main执行到最后一条语句时返回(隐式返回),那么该进程的终止状态是0
- main函数(仅限main函数)调用return和调用exit是等价的。例如,下面都返回0
exit(0); //等价于return(0);
概念一:
- 如果main函数返回一个整型以及用exit代替return,对某些C编译程序和UNIX lint(1)程序而言会产生不必要的警告信息,因为这些编译程序并不了解main中的exit与return语句的作用相同
概念二:
- 避开这种警告信息的一种方法是:在main中使用return语句而不是exit。但是这样做的结果是不能用UNIX 的grep实用程序来找出程序中所有的exit调用
概念三:
- 另外一个解决方法是将main说明为返回void而不是int,然后仍旧调用exit。这也避开了编译程序的警告,但从程序设计角度看却 并不正确,而且会产生其他的编译编辑警告,因为main的返回类型应当是带符号的整型
#include
int atexit(void (*func)(void));
//参数:函数指针
//返回值:成功返回0,失败返回非0
进程的开启与终止流程图
演示案例
#include
#include static void my_exit1(void); static void my_exit2(void); int main(void) { if (atexit(my_exit2) != 0) perror("can’t register my_exit2"); if (atexit(my_exit1) != 0) perror("can’t register my_exit1"); if (atexit(my_exit1) != 0) perror("can’t register my_exit1"); printf("main is done\n"); return(0); } static void my_exit1(void){ printf("first exit handler\n"); } static void my_exit2(void){ printf("second exit handler\n"); }
#include
int on_exit(void (*function)(int , void *), void *arg);
//返回值:成功返回0;否则返回非零值
参数:
- 参数1:进程终止处理函数(参数1为调用on_exit()函数进程的退出码,参数2为附加信息,为void*类型)
- 参数2:传递给on_exit()参数1所绑定的函数的第2个参数
演示案例
- 子进程绑定两个on_exit函数。父进程等待子进程结束
#include
#include #include #include void On_exit1(int status,void *arg) { printf("%s:child exit,exit code is %d\n",(char*)arg,status); } void On_exit2(int status,void *arg) { printf("%s:child exit,exit code is %d\n",(char*)arg,status); } int main() { pid_t pid; int status; if((pid=fork())==-1){ perror("fork"); exit(1); }else if(pid==0){ printf("I am child,pid=%d\n",getpid()); sleep(1); char buff1[]="On_exit1 function"; char buff2[]="On_exit2 function"; on_exit(On_exit1,buff1); on_exit(On_exit2,buff2); exit(2); }else{ printf("I am father,pid=%d\n",getpid()); } if(wait(&status)<=0){ perror("wait"); exit(2); } printf("wait success,status code :%d\n",WEXITSTATUS(status)); exit(0); }
演示案例
- 子进程先结束,但是父进程处于sleep状态,因此子进程就变为僵死进程了
#include
#include #include int main() { pid_t pid; if((pid=fork())==-1) perror("fork"); else if(pid==0) { printf("child_pid pid=%d\n",getpid()); exit(0); } sleep(3); //子进程已经结束了,但是父进程没有对子进程进行任何处理,wait、waitpid之类的 system("ps"); exit(0); }
- 更改上面的案例,在父进程调用wait之前和调用wait之后都执行一次system("ps")。可以看到父进程之后wait处理子进程之后,子进程的僵死状态消失了
#include
#include #include int main() { pid_t pid; if((pid=fork())==-1) perror("fork"); else if(pid==0) { printf("child_pid pid=%d\n",getpid()); exit(0); } sleep(3); printf("****before wait:\n"); system("ps"); wait(NULL); printf("****after wait:\n"); system("ps"); exit(0); }
#include
#include
#include
int main()
{
pid_t pid;
if((pid=fork())<0){
perror("frok");
exit(1);
}else if(pid==0){
printf("I am child,myPid=%d,ppid=%d\n",getpid(),getppid());
sleep(2);
printf("I am child,myPid=%d,Ppid=%d\n",getpid(),getppid());
exit(0);
}else{
printf("I am father,myPid=%d\n",getpid());
sleep(1);
exit(0);
}
}