在进程间传递文件描述符

Posted tianzeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在进程间传递文件描述符相关的知识,希望对你有一定的参考价值。

  由于fork调用之后,父进程中打开的文件描述符在子进程中仍然保持打开,所以文件描述符可以很方便地从父进程传递到子进程。需要注意的是,传递一个文件描述符并不是传递一个文件描述符的值,而是要在接收进程中创建一个新的文件描述符,并且该文件描述符和发送进程中被传递的文件描述符指向内核中相同的文件表项。
  在Linux下,我们可以利用UNIX城socket在进程间传递特殊的辅助数据,以实现文件描述符的传递,它在子进程中打开一个文件描述符,然后将它传递给父进程,父进程则通过读取该文件描述符来获得文件的内容。

#include<sys/socket.h>
#include<sys/param.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
/*
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh,struct cmsghdr *cmsg);
size_t CMSG_ALIGN(size_t length);
size_t CMSG_SPACE(size_t length);
size_t CMSG_LEN(size_t length);
unsigned char* CMSG_DATA(struct cmsghdr *cmsg);
struct cmsghdr{
        socklen_t cmsg_len;
        int cmsg_level;
        int cmsg_type;
};
*/
static const int CONTROL_LEN = CMSG_LEN(sizeof(int));
/*发送文件描述符,fd参数是用来传递信息的UNIX域的socket,fd_to_send参数是待发送的文件描述符*/
 
void send_fd(int fd,int fd_to_send)
{
    struct iovec iov[1];
    struct msghdr msg;
    char buf[0];
 
    iov[0].iov_base = buf;
    iov[1].iov_len = 1;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov  = iov;
    msg.msg_iovlen = 1;
 
    cmsghdr cm;
    cm.cmsg_len = CONTROL_LEN;
    cm.cmsg_level = SOL_SOCKET;
    cm.cmsg_type = SCM_RIGHTS;
    *(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[1].iov_len = 1;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov  = iov;
    msg.msg_iovlen = 1;
 
    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]*/
 
        int ret = socketpair( PF_UNIX,SOCK_DGRAM,0,pipefd);//管道函数
        assert(ret != -1);
 
        pid_t pid = fork();
        assert(pid >= 0);
 
        if(pid == 0)//子进程
        {
                close(pipefd[0]);//?????
                fd_to_pass = open("text.txt",O_RDWR,0666);
                /*子进程通过管道将文件描述符发送到父进程。如果文件text打开失败则子进程将标准输入文件描述符发送到父进程*/
                send_fd( pipefd[1],(fd_to_pass > 0)?fd_to_pass:0);
                close(fd_to_pass);
                exit(0);
        }
 
        close(pipefd[1]);
        fd_to_pass = recv_fd(pipefd[0]);//父进程从管道接收目标文件描述符
        char buff[128];
        memset(buff,0,sizeof(buff));
        read(fd_to_pass,buff,127);//读目标文件描述符,以验证其有效性
        printf("I Got fd: %d and Data: %s\n",fd_to_pass,buff);
        close(fd_to_pass);
        return 0;
}

 

以上是关于在进程间传递文件描述符的主要内容,如果未能解决你的问题,请参考以下文章

进程间传递文件描述符——sendmsg和recvmsg函数

通过 Unix Domain Socket 传递文件描述符

实验八进程间通信

3.1 进程间通信之管道

linux 进程间通信机制(IPC机制)- 管道

在不同进程之间传递文件描述符的可移植方式