使用sendmsg/recvmsg在进程之间传递文件描述符

需要注意一点,传递一个文件描述符,并不是传递一个文件描述符的值,而是要在接收进程中创建一个新的文件描述符,并且该文件描述符和发送进程中被传递的文件描述符,指向同一个文件

例程

#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){
    struct iovec iov[1];
    struct msghdr msg;
    char buf[0];

    iov[0].iov_base =buf;
    iov[0].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[0].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;

    msg.msg_name =     NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    recvmsg(fd,&msg,0);
    int fd_to_read = *(int *) CMSG_DATA(&cm);
    return fd_to_read;
}
int main(int argc, char const *argv[])
{
    int pipefd[2];
    int fd_to_pass1 = 0;
    int fd_to_pass = 0;
    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_pass1 = open("test1.txt",O_RDWR,0666);
        send_fd(pipefd[1],(fd_to_pass1 >0)? fd_to_pass1 :0);
        printf("this is chile process pid= %d,open file test1.txt,it's fd =%d\n",getpid(),fd_to_pass1);
        

        fd_to_pass = open("test2.txt",O_RDWR,0666);
        send_fd(pipefd[1],(fd_to_pass >0)? fd_to_pass :0);
        printf("this is chile process pid= %d,open file test2.txt,it's fd =%d\n",getpid(),fd_to_pass);
        
        close(fd_to_pass1);
        close(fd_to_pass);

        exit(0);
    }
    close(pipefd[1]);

    fd_to_pass1 = recv_fd(pipefd[0]); //父进程从管道接收目标文件描述符
    char buf[1024];
    memset(buf,'\0',1024); 
    read(fd_to_pass1,buf,1024);//读目标文件描述符,以验证其有效性
    printf("this is father process pid= %d,getfile fd =%d and data:%s\n",getpid(),fd_to_pass1,buf);
    close(fd_to_pass1);

    fd_to_pass = recv_fd(pipefd[0]); //父进程从管道接收目标文件描述符
    memset(buf,'\0',1024); 
    read(fd_to_pass,buf,1024);//读目标文件描述符,以验证其有效性
    printf("this is father process pid= %d,getfile fd =%d and data:%s\n",getpid(),fd_to_pass,buf);

    close(fd_to_pass);
    return 0;
}

结果

cwd@cwd:~/code/test$ ./passfd_vis_process 
this is chile process pid= 18009,open file test1.txt,it's fd =3
this is father process pid= 18008,getfile fd =4 and data:heihei,cwd.this is test1

this is chile process pid= 18009,open file test2.txt,it's fd =5
this is father process pid= 18008,getfile fd =4 and data:heihei,cwd.this is test2

可见程序对文件描述符进行了正确的传递,但是直接输出各个进程对同一个文件的描述符的值并不相同

补充

man手册上对该程序中几个函数的讲解

recvmsg()

   The  recvmsg()  call  uses a msghdr structure to minimize the number of
   directly supplied arguments.  This structure is defined as  follows  in
   <sys/socket.h>:

       struct iovec {                    /* Scatter/gather array items */
           void  *iov_base;              /* Starting address */
           size_t iov_len;               /* Number of bytes to transfer */
       };

       struct msghdr {
           void         *msg_name;       /* optional address */
           socklen_t     msg_namelen;    /* size of address */
           struct iovec *msg_iov;        /* scatter/gather array */
           size_t        msg_iovlen;     /* # elements in msg_iov */
           void         *msg_control;    /* ancillary data, see below */
           size_t        msg_controllen; /* ancillary data buffer len */
           int           msg_flags;      /* flags on received message */
       };

   The  msg_name field points to a caller-allocated buffer that is used to
   return the source address if the socket  is  unconnected.   The  caller
   should  set  msg_namelen  to  the size of this buffer before this call;
   upon return from a successful call, msg_namelen will contain the length
   of  the returned address.  If the application does not need to know the
   source address, msg_name can be specified as NULL.

   The fields msg_iov and msg_iovlen describe scatter-gather locations, as
   discussed in readv(2).
   The  field  msg_control,  which  has length msg_controllen, points to a
   buffer for other protocol  control-related  messages  or  miscellaneous
   ancillary  data.   When recvmsg() is called, msg_controllen should con‐
   tain the length of the available buffer  in  msg_control;  upon  return
   from  a  successful call it will contain the length of the control mes‐
   sage sequence.

   The messages are of the form:

       struct cmsghdr {
           size_t cmsg_len;    /* Data byte count, including header
                                  (type is socklen_t in POSIX) */
           int    cmsg_level;  /* Originating protocol */
           int    cmsg_type;   /* Protocol-specific type */
       /* followed by
           unsigned char cmsg_data[]; */
       };

   Ancillary data should  be  accessed  only  by  the  macros  defined  in
   cmsg(3).

   As  an  example,  Linux  uses  this  ancillary  data  mechanism to pass
   extended errors, IP options, or file descriptors over UNIX domain sock‐
   ets.

sendmsg()

   The  field  msg_control,  which  has length msg_controllen, points to a
   buffer for other protocol  control-related  messages  or  miscellaneous
   ancillary  data.   When recvmsg() is called, msg_controllen should con‐
   tain the length of the available buffer  in  msg_control;  upon  return
   from  a  successful call it will contain the length of the control mes‐
   sage sequence.

   The messages are of the form:

       struct cmsghdr {
           size_t cmsg_len;    /* Data byte count, including header
                                  (type is socklen_t in POSIX) */
           int    cmsg_level;  /* Originating protocol */
           int    cmsg_type;   /* Protocol-specific type */
       /* followed by
           unsigned char cmsg_data[]; */
       };

   Ancillary data should  be  accessed  only  by  the  macros  defined  in
   cmsg(3).

   As  an  example,  Linux  uses  this  ancillary  data  mechanism to pass
   extended errors, IP options, or file descriptors over UNIX domain sock‐
   ets.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值