fork出来的子进程最好总是用_exit退出

摘要

fork家族函数(fork, vfork)所创建的子进程最好用_exit()退出,不管是通过 man vfork还是通过看相关网站都能得到这一结论。本文主要介绍一些案例来说明使用 exit()函数退出进程会引起弊病。

正文

linux下man vfork可以看到这样一句话:

The child must not return from the current function or call exit(), but may call _exit().

下面举几个因使用exit从fork出来的子进程退出而导致各种蛋疼的例子

atexit()掉包返回值

    #include <stdio.h>
    #include <stdlib.h>
    void end(void)
    {
        exit(-1);
    }
    int main()
    {
        int pid = 0;
        atexit(end);
        pid = fork();
        if (pid == 0) {
            exit(0);
        } else if (pid > 0) {
            int status = 0;
            wait(&status);
            printf("%d\n", status);
        }

        return 0;
    }

大家猜它输出什么?有的同学可能一下子就看出应该输出0,headool曾经也这么相信爱卿。实际上它输出为65280((unsigned char)-1 * 256)。现在headool带你走进科学: 

  1. main函数进来不久,程序用atexit将end设置成收尾函数(headool自创名词, 它在main函数return或exit()之后被调用);
  2. 接着fork,进程一分为二;
  3. 父进程 等待的子进程退出。子进程exit()退出后调用收尾函数end(),再次调用exit(-1)( 不会再递归调用end())。

退出码本来是妥妥的0,现在被调包成-1。

这样的exit,能靠谱吗?把它换成_exit(头文件unistd.h)或_Exit(头文件 stdlib.h)试试。

不要以为只有C语言有这问题,python有这问题,perl估计也有(这个headool没试过)。 python中解决此问题有对应的os._exit,perl中有对应的POSIX::_exit

伤不起的printf

下面的程序输出什么?

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int pid = 0;
    printf("headool");
    pid = fork();
    if (pid == 0) {
        exit(0);
    } else if (pid > 0) {
        int status = 0;
        wait(&status);
    }
    return 0;
}

和直觉相反,它输出为: headoolheadool(没有回车)

理解上一结果需要知道以下几个unix的事实:

  • fork是父子进程间的全拷贝(写时才复制),包括io缓存数据。
  • printf所打印的数据会经过行缓冲,只有在遇到\n时才会真正刷到文件(stdout)。
  • exit时进程所有文件将关闭,所有缓存数据会因此刷到文件。

明白上一例子到底怎么回事了吧。怎么办?将exit换成_exit!


最后说一个看上去更离奇的例子,它stdout的结果和重定向到文件里的不一样,但exit 换成_exit后天下又重新太平。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int pid = 0;
    printf("headool\n");//较上个示例程序仅仅加了一个\n
    pid = fork();
    if (pid == 0) {
        exit(0);
    } else if (pid > 0) {
        int status = 0;
        wait(&status);
    }
    return 0;
}

其中的原因以后再细说,查查setvbuf函数吧。 headool提醒你:用fork,记得_exit

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值