FD_CLOEXEC标记

  1. FD_CLOEXEC:这是个文件描述符标记,其值为1。
#define FD_CLOEXEC	1
  1. 这个标记的含义是:如果对文件描述符设置了FD_CLOEXEC标记,则在使用fork创建子进程后,父进程中打开的文件描述符在子进程中不会默认打开,复制到子进程中的文件描述符会被关闭;同理exec系列函数在新进程映像中会关闭原进程打开的文件描述符。如果在这种情况下不设置该标志位,程序开发者需要小心文件描述符(句柄)泄露的问题。因为文件描述符资源类似于引用计数,当引用计数为1时调用close才会真正释放文件描述符资源。
  2. 示例:程序最终会报错,原因是execlp系统调用会在新的进程映像中执行指定ls命令将输出结果写入标准输出。而由于已经在原进程中为标准输出设置FD_CLOEXEC标记,在execlp执行的进程会关闭标准输出,因此以下程序会报错写入错误。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    // 为文件描述符1设置FD_CLOEXEC标记
    int flags = fcntl(STDOUT_FILENO, F_GETFD);
    if (fcntl(STDOUT_FILENO, F_SETFD, flags |= FD_CLOEXEC) == -1) {
        perror("fctnl() ERROR:");
        return -1;
    }
    
    // 执行ls -l argv[0]命令
    execlp("ls", "ls", "-l", argv[0], (char*)NULL);
    return 0;
}

// 程序输出
// ls: 写入错误: 错误的文件描述符

FD_CLOEXEC标记的设置

存在很多场景需要对文件描述符设置FD_CLOEXEC(close-on-exec)标记,这里列举一些。

  1. 可以通过fcntl系统调用为已存在的文件描述符进行设置。
  2. 可以通过open系统调用为新创建的文件描述符进行设置,指定第二参数有O_CLOEXEC这个flag
#define O_CLOEXEC	02000000
  1. 网络编程时,通过socket为新创建的文件描述符进行设置,指定第二参数有SOCK_CLOEXEC这个flag
SOCK_CLOEXEC = 02000000
  1. 网络编程时,通过accept4为新创建的文件描述符进行设置
  2. 网络编程时,通过epoll_create1为新创建的文件描述符进行设置