Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

软件和网站开发以及相关技术探讨
回复
LittlePenguin
帖子: 8
注册时间: 2014-08-05 21:40
系统: Ubuntu14.04

Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#1

帖子 LittlePenguin » 2014-10-07 19:04

网上搜索到一些博客有对这两个函数的解释,看了之后还是犯迷糊。
exit()函数定义在stdlib.h头文件中,_exit()定义在unistd.h头文件中,这是区别之一。
调用_exit()函数时,其会关闭调用进程的所有文件描述符,清理内存和内核数据,但不会刷新流(stdin, stdout, stderr ...)。
exit()函数是在_exit()函数之上的一个封装,其会调用_exit(),并在调用之前先刷新流,并且exit()系统调用之前要检查文件的打开情况,把文件缓冲区的内容写回文件。所以要保证数据的完整性,得调用exit()函数。
但是也查到一些解释,《Linux环境C程序设计》(第二版 徐诚等编著)一书第205页exit系统调用小节中有这样描述:“由fork()函数创建的子进程分支里,正常情况下使用函数exit()是不正确的,这是因为使用它会导致标准输入输出的缓冲区被清空两次,而且临时文件可能被意外删除。”这与上面的解释相悖了,究竟谁是对的?
基于上面的描述,我还有以下疑问:
1、刷新缓冲区是简单的删除其中的数据,还是要进行一些操作,例如保存数据再清空?
2、exit()为什么会导致标准输入输出的缓冲区被清空两次?
希望有高手不吝赐教,解释exit()和_exit()的区别及使用方法。
谢谢!
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#2

帖子 astolia » 2014-10-07 22:59

这种事你还是查manpage这种第一手资料比较好,英文原版/影印版也还好,中文书籍乱抄一通太严重了
ubuntu默认没装系统调用和库函数两章的manpage,先安装上
sudo apt-get install manpages-dev
然后就可以用man查看了
man 3 exit
man 2 _exit

首先,_exit是系统调用,exit是库函数
其次,exit函数的基本流程如下:
1、执行用同为库函数的atexit/on_exit注册过的回调函数
2、对所有打开的stdio流执行flush操作
3、删除用tmpfile创建的临时文件
4、执行系统调用_exit

所以你问题的答案:
1、看fflush对流是怎么处理的
2、fork系统调用会把父进程拥有的资源复制一份给子进程。由于缓冲区也被复制给了子进程,那么父子进程各自的缓冲区中就可能存在两份相同的数据。flush的结果就可能和预想不符。你可以自己编译运行下面的简单例子

代码: 全选

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

void bye() {
    printf ("%d exit\n", getpid());
}

int main() {
    atexit(&bye);
    printf("parent's pid is %d\n", getpid());
    printf("=== buffered data ===");
    fork();
    exit(0);
}
结果很可能是这样的:
$ ./a.out
parent's pid is 8151
=== buffered data ===8151 exit
=== buffered data ===8152 exit
$ ./a.out > a.txt
$ cat a.txt
parent's pid is 8153
=== buffered data ===8153 exit
parent's pid is 8153
=== buffered data ===8154 exit
两者的输出不同的原因在于终端作为标准输出时默认是行缓冲,文件作为标准输出时默认是全缓冲
行缓冲和全缓冲的相关文章:http://blog.csdn.net/anonymalias/articl ... ls/8011115

最后的一点区别是,如果用了vfork系统调用,子进程要退出的话,只能用_exit(),因为vfork的子进程和父进程共享资源而不是另外复制一份
LittlePenguin
帖子: 8
注册时间: 2014-08-05 21:40
系统: Ubuntu14.04

Re: Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#3

帖子 LittlePenguin » 2014-10-12 20:05

好好研究,谢楼上赐教!
LittlePenguin
帖子: 8
注册时间: 2014-08-05 21:40
系统: Ubuntu14.04

Re: Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#4

帖子 LittlePenguin » 2014-10-13 12:39

在vfork()创建的子进程中调用_exit()也会关闭与之相关的文件描述符,这不会影响到父进程吗?难道是因为父子进程的缓冲区虽然是同一个空间,但是文件描述符是分开的,所以关闭的只是子进程的文件描述符?
头像
astolia
论坛版主
帖子: 6703
注册时间: 2008-09-18 13:11

Re: Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#5

帖子 astolia » 2014-10-13 17:44

LittlePenguin 写了:在vfork()创建的子进程中调用_exit()也会关闭与之相关的文件描述符,这不会影响到父进程吗?难道是因为父子进程的缓冲区虽然是同一个空间,但是文件描述符是分开的,所以关闭的只是子进程的文件描述符?
所以我跟你说要去查manpage,里面都有说明的
man 2 vfork

代码: 全选

       A call to vfork() is equivalent to calling clone(2) with flags specified as:
            CLONE_VM | CLONE_VFORK | SIGCHLD
man 2 clone

代码: 全选

 CLONE_FILES (since Linux 2.0)
              If CLONE_FILES is set, the calling process and the child process
              share  the same file descriptor table.  Any file descriptor cre‐
              ated by the calling process or by  the  child  process  is  also
              valid  in the other process.  Similarly, if one of the processes
              closes a file descriptor, or changes its associated flags (using
              the  fcntl(2)  F_SETFD  operation),  the  other  process is also
              affected.

              If CLONE_FILES is not set, the child process inherits a copy  of
              all  file  descriptors opened in the calling process at the time
              of clone().  (The duplicated file descriptors in the child refer
              to  the  same open file descriptions (see open(2)) as the corre‐
              sponding file descriptors in the calling  process.)   Subsequent
              operations  that  open or close file descriptors, or change file
              descriptor flags, performed by either the calling process or the
              child process do not affect the other process.
LittlePenguin
帖子: 8
注册时间: 2014-08-05 21:40
系统: Ubuntu14.04

Re: Linux 系统中调用 exit() 和 _exit() 结束进程的区别是什么?

#6

帖子 LittlePenguin » 2014-10-26 16:22

现在已经知道父子进程文件描述符的继承方法。
astolia 写了:
LittlePenguin 写了:在vfork()创建的子进程中调用_exit()也会关闭与之相关的文件描述符,这不会影响到父进程吗?难道是因为父子进程的缓冲区虽然是同一个空间,但是文件描述符是分开的,所以关闭的只是子进程的文件描述符?
所以我跟你说要去查manpage,里面都有说明的
man 2 vfork

代码: 全选

       A call to vfork() is equivalent to calling clone(2) with flags specified as:
            CLONE_VM | CLONE_VFORK | SIGCHLD
man 2 clone

代码: 全选

 CLONE_FILES (since Linux 2.0)
              If CLONE_FILES is set, the calling process and the child process
              share  the same file descriptor table.  Any file descriptor cre‐
              ated by the calling process or by  the  child  process  is  also
              valid  in the other process.  Similarly, if one of the processes
              closes a file descriptor, or changes its associated flags (using
              the  fcntl(2)  F_SETFD  operation),  the  other  process is also
              affected.

              If CLONE_FILES is not set, the child process inherits a copy  of
              all  file  descriptors opened in the calling process at the time
              of clone().  (The duplicated file descriptors in the child refer
              to  the  same open file descriptions (see open(2)) as the corre‐
              sponding file descriptors in the calling  process.)   Subsequent
              operations  that  open or close file descriptors, or change file
              descriptor flags, performed by either the calling process or the
              child process do not affect the other process.
回复