在进行fork调用后,由于子进程会拷贝父进程的资源,所以父进程中打开的文件描述符在子进程中仍然保持着打开,我们很容易的就将父进程的描述符传递给了子进程。但是除了这种情况下,如果想将某个父进程在子进程创建后才打开的描述符传递给子进程,又或者是想将子进程的描述符传递给父进程时,就遇到了问题。

在Linux中,虽然文件描述符是一个整型值,但是它的传递并非只是传递这个值而已。因为这个整型值其实是文件描述符表fd_array[]的下标

由于不同进程的文件描述符表不同,所以要传递一个文件描述符,就是要在接收进程中的文件描述符表中创建一个新的文件描述符,并且这两个文件描述符要指向内核中相同的文件表项

如何在两个进程之间传递文件描述符呢?在Linux下,我们可以利用UNIX域socket在程序间传递特殊的辅助数据,以实现文件描述符的传递。

这里演示的是具有亲缘关系的进程之间的传递(如果需要非亲缘,就将管道换成socket即可),主要借助UNIX域socket中的以下三个函数,socketpair、sendmsg、recvmsg

int socketpair(int domain, int type, int protocol, int sv[2]);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);struct msghdr
{void         *msg_name;       /* 目的IP地址 */socklen_t     msg_namelen;    /* 地址长度 */struct iovec *msg_iov;        /* 指定的内存缓冲区 */size_t        msg_iovlen;     /* 缓冲区的长度 */void         *msg_control;    /* 辅助数据 */size_t        msg_controllen; /* 指向cmsghdr结构,用于控制信息字节数 */int           msg_flags;      /* 描述接收到的消息的标志 */
};struct cmsghdr {socklen_t cmsg_len;    /* 计算cmsghdr头结构加上附属数据大小 */int       cmsg_level;  /* 发起协议 */int       cmsg_type;   /*协议特定类型 */
};//获得指向与msghadr结构关联的第一个cmsghdr结构
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);//计算 cmsghdr 头结构加上附属数据大小,并包括对其字段和可能的结尾填充字符
size_t CMSG_SPACE(size_t length);//计算 cmsghdr 头结构加上附属数据大小
size_t CMSG_LEN(size_t length);//返回一个指针和cmsghdr结构关联的数据
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

上述函数的使用在这里就不多介绍,可以通过查询man手册或者阅读相关博客来进行了解,下面直接实现功能。

//发送文件描述符
void send_fd(int sock_fd, int fd)
{ iovec iov[1]; msghdr msg;  char buff[0];   //指定缓冲区iov[0].iov_base = buff;iov[0].iov_len = 1;//通过socketpair进行通信,不需要知道ip地址msg.msg_name = nullptr;msg.msg_namelen = 0;//指定内存缓冲区msg.msg_iov = iov;msg.msg_iovlen = 1;//辅助数据cmsghdr cm;cm.cmsg_len = CMSG_LEN(sizeof(sock_fd)); //描述符的大小cm.cmsg_level = SOL_SOCKET;         //发起协议cm.cmsg_type = SCM_RIGHTS;          //协议类型*(int*)CMSG_DATA(&cm) = fd; //设置待发送描述符//设置辅助数据msg.msg_control = &cm;msg.msg_controllen = CMSG_LEN(sizeof(sock_fd));sendmsg(sock_fd, &msg, 0);  //发送描述符
}//接收并返回文件描述符
int recv_fd(int sock_fd)
{iovec iov[1];  msghdr msg;char buff[0];   //指定缓冲区iov[0].iov_base = buff;iov[0].iov_len = 1;//通过socketpair进行通信,不需要知道ip地址msg.msg_name = nullptr;msg.msg_namelen = 0;//指定内存缓冲区msg.msg_iov = iov;msg.msg_iovlen = 1;//辅助数据cmsghdr cm;//设置辅助数据msg.msg_control = &cm;msg.msg_controllen = CMSG_LEN(sizeof(sock_fd));recvmsg(sock_fd, &msg, 0);  //接收文件描述符int fd = *(int*)CMSG_DATA(&cm);return fd;
}

下面进行测试一下,子进程中打开一个文件描述符,并通过socketpair发送给父进程,来判断是否能够成功读取文件描述符中的内容

#include <sys/socket.h>
#include<sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#include <iostream>
using std::cout;
using std::endl;int main()
{int pipefd[2];          //管道int pass_fd = 0;        //待传送描述符char buff[1024] = {0};  //缓冲区//创建socketpair管道if(socketpair(PF_UNIX, SOCK_DGRAM, 0, pipefd) < 0){cout << "socketpair." << endl;return 1;}pid_t pid = fork(); //创建子进程//子进程if(pid == 0){close(pipefd[0]);   //子进程关闭多余的管道描述符pass_fd = open("test.txt", O_RDWR, 0666);if(pass_fd <= 0){cout << "open." << endl;}send_fd(pipefd[1], pass_fd);    //子进程通过管道发送文件描述符close(pass_fd);close(pipefd[1]);exit(0);}else if(pid < 0){//子进程创建失败cout << "fork." << endl;return 1;}close(pipefd[1]);   //父进程关闭多余描述符pass_fd = recv_fd(pipefd[0]);   //父进程从管道中接收文件描述符read(pass_fd, buff, 1024);  //父进程从缓冲区中读出数据,验证收发描述符是否正确cout << "fd: "<< pass_fd << " recv msg : " << buff << endl;close(pass_fd);close(pipefd[0]);return 0;
}

测试结果

Linux中进程间传递文件描述符的方法相关推荐

  1. Linux高级进程编程———在任意两个进程间传递文件描述符:使用 sendmsg 和 recvmsg 实现

    进程间传递打开的文件描述符,并不是传递文件描述符的值.那么在传递时究竟传递了什么?我们要先搞明白这个问题. 1.文件描述符 文件描述符的值与文件没有任何联系,只是该文件在进程中的一个标识,所以同一文件 ...

  2. Linux 进程间传递文件描述符

    文章目录 文件描述符 文件数据结构 共享文件 UNIX域socket实现传递文件描述符 进程间传递打开的文件描述符,并不是传递文件描述符的值.先说一下文件描述符. 文件描述符 对内核来说,所有打开的文 ...

  3. 进程间传递文件描述符--sendmsg,recvmsg(可用)

    UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #include <sys/socket.h> ...

  4. android进程间传递文件描述符原理

    在Linux中,进程打开一个文件,返回一个整数的文件描述符,然后就可以在这个文件描述符上对该文件进行操作.那么文件描述符和文件到底是什么关系?进程使用的是虚拟地址,不同进程间是地址隔离的,如何在两个进 ...

  5. Linux的辅助数据和传递文件描述符

    简介 首先,明确传递文件描述符的意义.一般来说,在多进程网络编程中,我们设置一个主进程用于监听新来的连接,设置一个进程池,用于处理这些连接.但是,与线程池不同,进程池各个进程之间的空间是独立的,直接共 ...

  6. 不相干进程之间传递文件描述符

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

  7. Linux系统学习笔记:文件描述符标志

    文件描述符标志的概念 文件描述符标志(目前就只有一个close-on-exec): 它仅仅是一个标志,当进程fork一个子进程的时候,在子进程中调用了exec函数时就用到了这个标志.意义是执行exec ...

  8. 线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。 进程拥有这

    线程共享的环境: 进程代码段.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯).进程打开的文件描述符.信号的处理器.进程的当前目录和进程用户ID与进程组ID. 进程拥有这许多共性的 ...

  9. linux dup用法,Linux:dup和dup2文件描述符及函数解析,dupdup2

    Linux:dup和dup2文件描述符及函数解析,dupdup2 一.文件描述符 1.1 文件描述符概念 我们知道在Linux下一切皆文件,因此我们需要一个东西对这些文件进行管理,此时就需要文件描述符 ...

最新文章

  1. a10双核(8dm1)-android4.1.1-v2.07,台电官方论坛 - A10 双核 8DM1 2G ROM 4.0.4 固件 - 平板笔记本...
  2. 只需1秒,无人机就能平地翻跟头 | IEEE
  3. Android Camera架构分析
  4. CAN 总线 之四 BOSCH CAN2.0 Part A
  5. 【计算机系统结构】第一周 课上笔记
  6. CCF201312-3 最大的矩形(100分)
  7. 前端学习(1984)vue之电商管理系统电商系统之完成静态属性
  8. 利用VS2012自带功能,将xml文档反序列化为对象
  9. zeromq不需要消息服务器,ZeroMQ发布订阅TCP丢弃消息订阅服务器失败
  10. 剑指offer 面试题5—从尾到头打印链表
  11. 20年未解的MIT密码难题,被自学成才的程序员破解了,比预计早15年
  12. 中国无人车第一案!百度状告景驰王劲:窃取机密,不还电脑,索赔5000万
  13. hadoop安装和基本知识
  14. 基于Go的马蜂窝旅游网分布式IM系统技术实践
  15. python简单的构建爬虫ip代理池
  16. 什么是王道?什么是王道中的王道?
  17. c语言工具栏运行不见了,电脑下面的任务栏不见了怎么办 几种方法介绍
  18. CyclicBarrier栅栏
  19. 双代号网络图如何用计算机画,怎么画双代号网络图,双代号网络图的绘制规则和步骤...
  20. To install spack and your first package

热门文章

  1. log4j2.xml
  2. webclientt和httpwebrequest
  3. Shiro使用redis作为缓存(解决shiro频繁访问Redis)
  4. 大数据构建模块:选择体系结构和开源框架
  5. OkHttp如何移除User-Agent,Accept-Encoding等框架自动添加的请求头参数
  6. Java中String、StringBuffer、StringBuilder的区别
  7. 设计原则--开放-封闭原则(OCP)
  8. Linux命令行上执行操作,不退回命令行的解决方法
  9. 汇编实现大写转小写函数(to_lower)
  10. WP7 开发(九) WP7控件开发(六)-DeepZoom技术