在进程间传递文件描述符

8 篇文章 0 订阅

《Linux高性能服务器编程》学习笔记

由于fork调用后,父进程中打开的fd,在子进程中仍然保持打开,所以fd可以很方便的从父进程传递到子进程。

那么如何把子进程中打开的fd传给父进程呢?我们可以利用UNIX域socket在进程间传递特殊的辅助数据,以实现fd的传递。

如下代码passfdbyps.cpp在子进程中打开一个fd,然后将它传递给父进程,父进程则通过读取该fd获得文件内容

个人认为代码中一些 msghdr结构体用到了会查就行

// 功能:在进程间传递文件描述符
// 2023年02月22日 20:11:19
#include <sys/socket.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

// 返回对齐后的长度
static const  int CONTROL_LEN = CMSG_LEN(sizeof(int));
// 发送文件描述符,fd参数是用来传递信息的UNIX域socket
// fd_to_send参数是待发送的文件描述符
void send_fd(int fd, int fd_to_send)
{
    // iovec 定义了一个向量元素,这个结构用作一个多元素数组
    struct iovec iov[1];
    struct msghdr msg;  // 用于接收和发送数据
    char buf[0];

    // iov_base指向一个缓冲区,这个缓冲区是存放的是readv所接收的数据或是writev将要发送的数据。
    iov[0].iov_base = buf;
    iov[0].iov_len = 1; // 接收的最大长度以及实际写入长度
    msg.msg_name = NULL; // 表示消息的目标地址,通常是一个指向 struct sockaddr 结构体的指针
    msg.msg_iov = iov;  // 数据缓冲区
    msg.msg_iovlen = 1; // 数据缓冲区中元素个数

    // 表示控制信息头部的结构体,通常为msghdr中的msg_control字段数据
    // 通常用于发送或接收一些与协议相关的控制信息,例如 IP 的 TTL 值、TCP 的选项等
    cmsghdr cm;
    // 表示辅助数据的长度,包括 struct cmsghdr 结构体的长度和后面跟随的辅助数据的长度。
    cm.cmsg_len = CONTROL_LEN;
    // 表示辅助数据所属协议的层级
    cm.cmsg_level = SOL_SOCKET;
    // 表示辅助数据的类型
    cm.cmsg_type = SCM_RIGHTS; // SCM_RIGHTS表面上传递的是文件描述符,但实际上并不是简单地传递描述符的数字,而是传递描述符背后的 file 文件
    // CMSG_DATA()返回一个指向cmsghdr的数据部分的指针
    *(int *)CMSG_DATA(&cm) = fd_to_send;
    msg.msg_control = &cm; // 设置辅助数据
    msg.msg_controllen = CONTROL_LEN;
    
    sendmsg(fd, &msg, 0);
}

// 接收目标文件描述符
int recv_fd(int fd)
{
    struct iovec iov[1];
    struct msghdr msg;  // 用于接收和发送数据
    char buf[0];

    iov[0].iov_base = buf;
    iov[0].iov_len = 1; // 接收的最大长度以及实际写入长度
    msg.msg_name = NULL; // 表示消息的目标地址,通常是一个指向 struct sockaddr 结构体的指针
    msg.msg_iov = iov;  // 数据缓冲区
    msg.msg_iovlen = 1; // 数据缓冲区中元素个数

    // 表示控制信息头部的结构体,通常为msghdr中的msg_control字段数据
    // 通常用于发送或接收一些与协议相关的控制信息,例如 IP 的 TTL 值、TCP 的选项等
    cmsghdr cm;
    msg.msg_control = &cm;
    msg.msg_controllen = CONTROL_LEN;

    recvmsg(fd, &msg, 0);

    int fd_to_read = *(int *)CMSG_DATA(&cm);
    return fd_to_read;
}

int main()
{
    int pipefd[2];
    int fd_to_pass = 0;
    // 创建父子进程间的管道,文件描述符pipefd[0]和pipefd[1]都是UNIX域socket
    int ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, pipefd);
    assert(ret != -1);

    pid_t pid = fork();
    assert(pid >= 0);

    if(pid == 0) // child
    {
        close(pipefd[0]);
        fd_to_pass = open("test.txt", O_RDWR, 0666);
        // 子进程通过管道将文件描述符发送到父进程
        // 若test.txt 打开失败,则子进程将标准输入文件描述符发送到父进程
        send_fd(pipefd[1], (fd_to_pass) > 0 ? fd_to_pass : 0); //!!!
        close(fd_to_pass);
        exit(0);
    }
    // parent
    close(pipefd[1]);
    fd_to_pass = recv_fd(pipefd[0]); // 父进程从管道接收目标文件描述符
    char buf[1024];
    memset(buf, '\0', 1024);
    read(fd_to_pass, buf, 1024);
    printf("I got fd %d and data %s\n", fd_to_pass, buf);
    close(fd_to_pass);
}

测试

当前目录下有一个test.txt文件

this is a test txt
编译调试
jyhlinux@ubuntu:~/share/Linux高性能服务器编程/ch13$ g++ -o passfdbyps passfdbyps.cpp 
jyhlinux@ubuntu:~/share/Linux高性能服务器编程/ch13$ ./passfdbyps 
I got fd 4 and data this is a test txt
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值