Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1814742
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 系统运维

2012-03-29 12:46:07

有8种方法终止一个进程。普通终止有5种:


1、从main函数中返回;


2、调用exit;


3、调用_exit或_Exit;


4、最后线程从start函数返回;(11.5节)


5、从最后线程里调用pthread_exit(11.5节)


异常终止有3种:


6、调用abort(10.17节)


7、收到一个信号(10.2节)


8、最后线程回应一个取消请求(11.5节和12.7节)


目前,我们将忽略这三个与线程相关的终止方法,直到我们在11章和12章讨论线程。


我们在前一节提到的启动程序通常被写为:当main函数返回时,exit函数被调用。如果启动程序用C编码(它通常用汇编来编码),main函数的调用可能看起来像:


exit(main(argc, argv));


Exit 函数

三个普通终止程序的函数:_exit和_Exit从内核立即返回;eixt执行特定清理处理然后从内核返回。



  1. #include <stdlib.h>

  2. void exit(int status);

  3. void _Exit(int status);

  4. #include <unistd.h>

  5. void _exit(int status);


我们将在8.5节讨论这三个函数在其它进程,比如终止进程的子进程和父进程,上的效果。


不同头文件的原因是exit和_Exit由ISO C规定而_exit由POSIX.1规定。


历史上,exit函数问题执行一个标准I/O库的清理:fclose函数被调用来关闭所有打开的流。回想下5.5节,这会使所有缓冲的输出数据被冲洗(写入文件)。


这三个exit函数都期望一个整型参数,我们称它为退出状态。多数UNIX系统的外壳提供一种检查进程退出状态的方法。如果a、这些函数中任何一个被调用 而没有退出状态,b、main返回而没有返回值,或者c、main函数没有被声明为返回一个整型值,那么进程的退出状态是无定义的。然而,如果main的 返回类型是一个整型数而main从函数掉出来(一个隐含的return),那么进程的退出状态为0。


这是在ISO C标准的1999版本的新行为。历史上,如果main函数到达结束位置,而没有显式调用一个return语句或exit函数,退出状态是无定义的。


从main函数里返回一个整型值等价于用相同值调用exit。因而exit(0);和main函数里的return(0);相同。


看下经典的hello world程序:



  1. #include <stdio.h>

  2. main()
  3. {
  4.     printf("hello, world\n");
  5. }



当我们编译并运行这个程序时, 我们可以看到返回代码是一个随机值。如果我们在不同的系统上编译这个相同的程序,很可能会得到不同的返回代码,这取决于main函数返回时栈和寄存器的内容。

$ cc hello_world.c
$ ./a.out
hello, world
$ echo $?
13


启用1999 ISO C编译扩展:


$ cc -std=c99 hello_world.c
hello_world.c:3:1: warning: return type defaults to ‘int’
$ ./a.out
hello, world
$ echo $?
0


注意当雇用1999 ISO C扩展时编译器警告出现。这是因为main函数的返回值没有显式声明为一个整型。如果我们加上这个声明,那这个消息就不会出现。然而,如果我们启用所有推 荐的错误(用-Wall标志),那么我们将会看到类似于“control reaches end of nonvoid function.”的消息。(我在gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)上没有看到这个消息。)


main的作为整型返回的声明和使用exit而不是return从一些编译器和lint程序产生不需要的警告。问题是编译器不知道从main函数的 exit和一个return是相同的。一种处理这些警告的方式,在一段时间后会变得很烦,是在main里用return而不是exit。但这样做会阻止我 们使用UNIX系统的grep工具来定位一个程序里的所有的exit调用。另一个解决办法是把main声明为void,而不是int,然后继续调用 exit。这处理了编译器警告,但看起来不对(特别是在编程文本里),而且能产生其它的编译错误,因为main的返回值应该是一个有符号的整型。在本文, 我们让main返回一个整型,因为这是ISO C和POSIX.1共同定义的。


不同的编译器的警告有不同的信息。注意GNU C编译器通常不产生这些不重要的警告信息,除非额外的警告选项被指定。


atexit函数

在ISO C,一个进程可以最多注册32个自动被exit函数调用的函数。这些被称为exit处理器,并通过调用atexit来注册。



  1. #include <stdlib.h>

  2. int atexit(void (*func)(void));

  3. 成功返回0,错误返回非0值。


声明说我们传递一个函数地址作为atexit的参数。当这个函数被调用时,不传入任何参数也不返回任何值。exit函数以它们注册的顺序的相反顺序调用这些函数。每个函数都被调用和它被注册的一样多的次数。


这些退出处理器第一次出现在1989年的ANSI C标准里。在ANSI C之前的系统,比如SVR3和4.3BSD,没有提供这些退出处理器。


ISO C要求系统支持至少32个退出处理器。sysconf函数可能用来决定一个给定系统支持的退出处理器的最大数量。


在ISO C和POSIX.1,exit首先调用退出处理器,然后(通过fclose)关闭所有打开的文件,最后调用_exit或_Exit回到内核。 POSIX.1扩展了ISO C标准,指出如果程序调用任何一个exec家族的函数,那么任何安装的退出处理器将被清理掉。


注意内核执行一个程序的唯一方法是调用一个exec函数。进程自愿终止的唯一方法是调用_exit或_Eixt,不管是显式地还是隐式地(通过调用exit函数)。一个进程也可以被一个信号非自愿地终止。


下面的代码使用了atexit函数:



  1. #include <stdio.h>

  2. static void my_exit1(void);
  3. static void my_exit2(void);

  4. int
  5. main(void)
  6. {
  7.     if (atexit(my_exit2) != 0) {
  8.         printf("can't register my_exit2");
  9.         exit(1);
  10.     }

  11.     if (atexit(my_exit1) != 0) {
  12.         printf("can't register my_exit1");
  13.         exit(1);
  14.     }
  15.     if (atexit(my_exit1) != 0) {
  16.         printf("can't register my_exit1");
  17.         exit(1);
  18.     }

  19.     printf("main is done\n");
  20.     return(0);
  21. }

  22. static void
  23. my_exit1(void)
  24. {
  25.     printf("first exit handler\n");
  26. }

  27. static void
  28. my_exit2(void)
  29. {
  30.     printf("second exit handler\n");
  31. }


$ ./a.out
main is done
first exit handler
first exit handler
second exit handler

一个退出处理器对于每次注册都被调用一次。上面的代码里,第一个退出处理器被注册了两次,所以它也被调用了两次。注意我们没有调用exit,相反,我们从main里返回。
阅读(643) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~