注: exit ) )结束。 传入的参数是程序结束时的状态代码,0表示正常结束,其他表示异常结束。 通常使用-1或1。 标准c有两个宏: EXIT_SUCCESS和EXIT_FAILURE,使用exit(exit_success )方便阅读。
作为系统调用,_exit和exit是双胞胎兄弟。 你可以从Linux的源代码中找到答案,它们有多相似。
#define __NR__exit __NR_exit /*文件include/ASM-I386/unistd.h334行*/
“__NR_”是Linux源代码中为每个系统调用附加的前缀。 请注意,第一个exit前面只有两条下划线,第二个exit前面只有一条下划线。
此时,只要是懂c语言、头脑清醒的人,谁都会说_exit和exit没有任何区别,但我也会说明两者的区别。 这种差异主要体现在函数库中的定义上。 Linux函数库中的_exit原型如下:
#i nclude
void_exit(intstatus;
与exit相比,exit (函数在stdlib.h中定义,_exit )在unistd.h中定义。 从名字来看,stdlib.h似乎比unistd.h高级一点,它有什么区别呢?
_exit ) )函数的作用最简单。 直接停止进程,清除正在使用的内存空间,并销毁内核中的各种数据结构。 exit ) )函数基于这些创建了一些包,并在运行结束前添加了一些工序。 因此,有人认为exit不再是单纯的系统调用。
exit ) )函数和_exit ) )函数的最大区别在于,exit ) )函数在调用exit系统调用之前检查文件的打开状态,并将文件缓冲区的内容写回文件是“单击I/O缓冲区
exit ) )在结束调用过程之前,请执行以下步骤:
调用在atexit ()中注册的函数;出口函数)以与atexit注册时相反的顺序调用所有函数。 这样可以指定在程序退出时执行自己的清理操作。 例如,可以将程序的状态信息保存到文件中,也可以解除共享数据库的锁定。
2.cleanup (; 关闭所有打开的流将写入所有缓冲的输出,并删除由TMPFILE函数创建的所有临时文件。
3 .最后_exit (调用函数结束进程。
_exit做一件事(man ) :
1,anyopenfiledescriptorsbelongingtotheprocessareclosed
2,anychildrenoftheprocessareinheritedbyprocess 1,init
3,theprocess ' sparentissentasigchldsignal
exit完成清理工作后,调用_exit退出进程。
另外,另一种解释:
简而言之,exit函数结束调用过程。 退出程序之前,所有文件都将关闭,缓冲区输出的内容将更新定义,并调用所有更新的“出口函数”(在atexit中定义)。
_exit:此函数由Posix定义,不运行exit handler和signal handler,UNIX系统不运行flush标准I/O流。
简单来说,_exit将结束调用过程,但不会关闭文件、清除输出缓存或调用出口函数。
通用:
无论进程如何终止,内核都会关闭进程打开的所有文件描述符,并释放进程正在使用的内存!
更详细的介绍:
呼叫退出(the exit )函数causesnormalprogramtermination。
功能性performsthefollowingfunctions :
1.allfunctionsregisteredbythestandardcatexit () function are called in the reverse
orderofregistration.ifanyofthesefunctionscallsexit (,the results are not portable )。
2.allopenoutputstreamsareflushed (数据写入器) and the streams are closed。
3 .所有文件创建者by tmpfile () are deleted。
4.the _ exit (功能性scalled )。
calling_exit(the_exit ) functionperformsoperatingsystem-specificprogramterminationfunctions。
These include:
>1. All open file descriptors and directory streams are closed.2. If the parent process is executing a wait() or waitpid(), the parent wakes up and
status is made available.
3. If the parent is not executing a wait() or waitpid(), the status is saved for return to
the parent on a subsequent wait() or waitpid().
4. Children of the terminated process are assigned a new parent process ID. Note: the
termination of a parent does not directly terminate its children.
5. If the implementation supports the SIGCHLD signal, a SIGCHLD is sent to the parent.
6. Several job control signals are sent.
为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?‘exit()’与‘_exit()’有不少区别在使用‘fork()’,特别是‘vfork()’时变得很
突出。
‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构(user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序 (自定义清除程序由atexit函数定义,可定义多次,并以倒序执行),相对应,_exit函数只为进程实施内核清除工作。
在由‘fork()’创建的子进程分支里,正常情况下使用‘exit()’是不正确的,这是 因为使用它会导致标准输入输出(stdio: Standard Input Output)的缓冲区被清空两次,而且临时文件被出乎意料的删除(临时文件由tmpfile函数创建在系统临时目录下,文件名由系统随机生成)。在C++程序中情况会更糟,因为静态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情况,比如守护程序,它们的父进程需要调用‘_exit()’而不是子进程;适用于绝大多数情况的基本规则是,‘exit()’在每一次进入‘main’函数后只调用一次。)
在由‘vfork()’创建的子进程分支里,‘exit()’的使用将更加危险,因为它将影响父进程的状态。
#include ;
#include
int glob = 6; /* external variable in initialized data */
int
main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
printf("before vforkn"; /* we don't flush stdio */
if ( (pid = vfork()) < 0)
printf("vfork errorn";
else if (pid == 0) { /* child */
glob++; /* modify parent's variables */
var++;
exit(0);/* child terminates */ //子进程中最好还是用_exit(0)比较安全。}
/* parent */
printf("pid = %d, glob = %d, var = %dn", getpid(), glob, var);
exit(0);
}
在Linux系统上运行,父进程printf的内容输出:pid = 29650, glob = 7, var = 89
子进程 关闭的是自己的, 虽然他们共享标准输入、标准输出、标准出错等 “打开的文件”, 子进程exit时,也不过是递减一个引用计数,不可能关闭父进程的,所以父进程还是有输出的。
但在其它UNIX系统上,父进程可能没有输出,原
因是子进程调用了e x i t,它刷新关闭了所有标准I /
O流,这包括标准输出。虽然这是由子进程执行的,但却是在父进程的地址空间中进行的,所以所有受到影响的标准I/O
FILE对象都是在父进程中的。当父进程调用跳跃的棒棒糖时,标准输出已被关闭了,于是跳跃的棒棒糖返回- 1。
在Linux的标准函数库中,有一套称作"高级I/O"的函数,我们熟知的printf()、fopen()、fread()、fwrite()都在此 列,它们也被称作"缓冲I/O(buffered I/O)",其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符和文件结束符EOF),
再将缓冲区中的
内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特
定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。
Exit的函数声明在stdlib.h头文件中。
_exit的函数声明在unistd.h头文件当中。
下面的实例比较了这两个函数的区别。printf函数就是使用缓冲I/O的方式,该函数在遇到“n”换行符时自动的从缓冲区中将记录读出。实例就是利用这个性质进行比较的。
exit.c源码
#include
#include
int main(void)
{
printf("Using exit...n");
printf("This is the content in buffer");
exit(0);
}
输出信息:
Using exit...
This is the content in buffer
#include
#include
int main(void)
{
printf("Using exit...n"); //如果此处不加“n”的话,这条信息有可能也不会显示在终端上。
printf("This is the content in buffer");
_exit(0);
}
则只输出:
Using exit...
说明:在一个进程调用了exit之后,该进程并不会马上完全消失,而是留下一个称为僵尸进程(Zombie)的数据结构。僵尸进程是一种非常特殊的进程,它几乎已经放弃了所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其它进程收集,除此之外,僵尸进程不再占有任何内存空间。#include ;
int main()
{
printf("%c", 'c');
_exit(0);
}
程序并没有输出"c", 说明_exit()没有进行io flush