sharkie's inn

Back

Linux 中如何在进程间传递文件描述符#

有些场景,我们需要让父进程的某个文件描述符(记作 fd1)指向子进程对应的文件描述符(记作 fd2) 指向的打开文件表项,也就是 fd1 和 fd2 指向同一个处于内核中的打开文件表,又或者是让两个不相关的进程共享同一个打开文件表项。在 Linux 下,我们可以用 UNIX 套接字来在进程之间传递特殊的辅助数据,以实现打开文件表项的共享。

下面的例子是子进程打开一个名为 “text.txt” 的文件,得到一个文件描述符 fdToPass,然后将 fdToPass 对应的打开文件表项共享给父进程。

#include <stdio.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define BUFFER_SIZE 1024

int sendFd(int fd, int fdToSend) {
    struct iovec iov[1];
    char dummy = 0;
    iov[0].iov_base = &dummy;
    iov[0].iov_len = 1;

    struct msghdr msg;
    char controlBuf[CMSG_SPACE(sizeof(int))];
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_control = controlBuf;
    msg.msg_controllen = sizeof(controlBuf);

    struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
    if (cm == NULL) {
        return -1;
    }
    cm->cmsg_len = CMSG_LEN(sizeof(int));
    cm->cmsg_level = SOL_SOCKET;
    cm->cmsg_type = SCM_RIGHTS;
    *((int *) CMSG_DATA(cm)) = fdToSend;

    if (sendmsg(fd, &msg, 0) < 0) {
        return -1;
    }

    return 0;
}

int recvFd(int fd) {
    struct iovec iov[1];
    char dummy = 0;
    iov[0].iov_base = &dummy;
    iov[0].iov_len = 1;

    struct msghdr msg;
    char controlBuf[CMSG_SPACE(sizeof(int))];
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_control = controlBuf;
    msg.msg_controllen = sizeof(controlBuf);

    if (recvmsg(fd, &msg, 0) < 0) {
        return -1;
    }

    struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
    if (cm == NULL || cm->cmsg_type != SCM_RIGHTS) {
        return -1;
    }

    int fdRecvd = *((int *) CMSG_DATA(cm));

    return fdRecvd;
}

int main() {
    int pipeFd[2];
    int fdToPass = 0;
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pipeFd)) {
        fprintf(stderr, "socketpair() failed: %s\n", strerror(errno));
        return 1;
    }

    pid_t pid = fork();
    if (pid < 0) {
        fprintf(stderr, "fork() failed: %s\n", strerror(errno));
        return 1;
    }

    if (pid == 0) {
        close(pipeFd[0]);
        fdToPass = open("test.txt", O_RDWR, 0666);
        if (fdToPass < 0) {
            fprintf(stderr, "open \"test.txt\" failed: %s\n", strerror(errno));
            return 1;
        }
        if (sendFd(pipeFd[1], fdToPass) < 0) {
            fprintf(stderr, "sendFd() failed: %s\n", strerror(errno));
            return 1;
        }
        close(fdToPass);

        return 0;
    }

    close(pipeFd[1]);

    if ((fdToPass = recvFd(pipeFd[0])) < 0) {
        fprintf(stderr, "recvFd() failed: %s\n", strerror(errno));
        return 1;
    }

    char buf[BUFFER_SIZE] = {0};
    read(fdToPass, buf, BUFFER_SIZE);

    printf("I got data from fd %d: %s\n", fdToPass, buf);

    close(fdToPass);

    return 0;
}
c
Linux 中如何在进程间传递文件描述符
https://sharkie.cn/blog/linux-%E4%B8%AD%E5%A6%82%E4%BD%95%E5%9C%A8%E8%BF%9B%E7%A8%8B%E9%97%B4%E4%BC%A0%E9%80%92%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6
Author sharkie
Published at October 20, 2025
Comment seems to stuck. Try to refresh?✨