Linux 进程间传递文件描述符
文章目录
- 文件描述符
- 文件数据结构
- 共享文件
- UNIX域socket实现传递文件描述符
进程间传递打开的文件描述符,并不是传递文件描述符的值。先说一下文件描述符。
文件描述符
对内核来说,所有打开的文件都会通过文件描述符引用,文件描述符在进程中是一个非负整数,文件描述符在进程中是从0开始,默认0与标准输入关联、1与标准输出关联、2与标准出错关联。之后进程每打开一个文件或者创建一个新文件的时候,内核都会向进程返回一个文件描述符来表示这个文件,文件描述符是递增的。文件描述符的值与文件没有必然的联系,只是该文件在进程中的一个标识,所以同一文件在不同进程中的文件描述符可能不一样,相同值得文件描述符在不同进程中可能标识不同得文件。文件描述符的取值范围是0~OPEN_MAX。
接下来要明白文件共享涉及的数据结构。
文件数据结构
内核使用三种数据结构来表示打开的文件:
- 进程中有一个文件描述符表,每个文件描述符占用一项内容,文件描述符与指向对应文件表的指针相关联
- 内核为进程中打开的文件维护一张文件表,包含文件的状态标志、偏移量、指向v节点的指针
- 每个打开的文件都有一个v节点,包含了文件类型、对文件进行操作的函数指针、包括i节点
三者的关系如下:
共享文件
在进程间传递文件描述符是非常有用的,通过传递文件描述符,可以让其他进程拥有对文件操作的能力,在网络编程中体现比较多,比如nobody进程协助创建了数据连接,然后将socket的文件描述符传递给服务进程,由服务进程进行数据传输。
这是两个进程分别打开同一文件的情况:
这是进程共享文件描述符的状态·:
所以共享文件描述符就是将不同文件描述符指向一个文件表。这一点与fork产生的父子进程共享已打开的文件描述符是一样的。还要注意,一般文件在关闭文件描述符之后就关闭文件了,但是共享文件的情况不一样,共享文件要等到所有引用的文件描述符关闭之后才可关闭。
UNIX域socket实现传递文件描述符
可以通过UNIX域socket来传递文件描述符,实际是调用了socket中的sendmsg和recvmsg函数,利用sendmsg和recvmsg可以发送附属数据,附属数据可以是是文件描述符,两个函数的原型如下:
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; /* 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 */
};
//套接口地址成员msg_name与msg_namelen。
//I/O向量引用msg_iov与msg_iovlen。
//附属数据缓冲区成员msg_control与msg_controllen。
//接收信息标记位msg_flags。
其中,对于传递文件描述符有用的成员为:msg_control和msg_controllen,需要注意的是,如果想利用它传递辅助信息,比如文件描述符,必须携带至少一个字节的真实数据,也就是iov指针指向的缓冲区要有数据,iovlen至少是1。
- msg_control:指向附属数据缓冲区
- msg_controllen:msg_control所指向的这块缓冲的长度
传递附属数据cmsghdr的结构如下:
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 byunsigned char cmsg_data[]; */
};
首先要明白什么是附属数据,recvmsg与sendmsg函数允许程序发送或是接收附属数据,这些额外的信息受限于一定的格式规则,也就是控制信息头与管理这些信息的宏。
- cmsg_len 附属数据的字节计数,这包含结构头的尺寸。这个值是由CMSG_LEN()宏计算的。
- cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
- cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS)。
为了发送文件描述符,将cmsghdr中的成员设置如下:
- cmsg_len 设置为cmsghdr的结构长度加一个整型(文件描述符)的长度;
- cmsg_level 设置为SOL_SOCKET
- cmsg_type 设置为SCM_SCM_RIGHTS,用以表明我们在传送的内容是访问权,访问权仅能通过UNIX域socket传送
紧随cmsg_type 之后的存放内容,就是描述符。通过CMSG_DATA获取整型量的指针。
如何将文件描述符传递放在附属数据中发送呢?如下:
//附属数据的配置
p_cmsg = CMSG_FIRSTHDR(&msg); //返回附属数据部分的第一个cmsghdr
p_cmsg->cmsg_level = SOL_SOCKET;
p_cmsg->cmsg_type = SCM_RIGHTS;
p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); //返回附属数据长度
int *p_fds;
p_fds = (int *) CMSG_DATA(p_cmsg); //返回附属数据的净荷数据地址
*p_fds = fd; //设置待发送的文件描述符 将fd保存在净荷数据地址
这些宏定义的作用如下:
- CMSG_FIRSTHDR(): 返回msghdr辅助数据部分指向第一个cmsghdr的指针
- CMSG_NXTHDR(): 返回参数中cmsghdr的下一个有效cmsghdr。当msg_control buffer中没有足够剩余的空间的时候,返回NULL
- CMSG_ALIGN(): 给定一个长度,其会返回对齐后相应的长度。它是一个常量表达式,其一般实现如下:
#define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
- CMSG_SPACE(): 返回辅助数据及其所传递的净荷数据的总长度。即sizeof(cmsg_len) + sizeof(cmsg_level) + sizeof(cmsg_type) + len(cmsg_data)长度进行CMSG_ALIGN后的值.
- CMSG_DATA(): 返回cmsghdr的净荷数据部分
- CMSG_LEN(): 返回净荷数据长度进行CMSG_ALIGN后的值,一般赋值给cmsghdr.cmsg_len。
为了创建辅助数据,首先初始化msghdr.msg_controllen字段。 在msghdr上使用CMSG_FIRSTHDR()
来获取第一个控制消息,然后使用CMSG_NXTHDR()
来获取后续的控制消息。在每一个控制消息中,使用CMSG_LEN()
来初始化cmsghdr.cmsg_len,使用CMSG_DATA()
来初始化cmsghdr.cmsg_data部分
参考:https://ivanzz1001.github.io/records/post/linux/2017/11/04/linux-msghdr
所以发送文件描述符的代码如下:
void send_fd(int sock_fd, int fd) {int ret;struct msghdr msg;struct cmsghdr *p_cmsg; //附属数据struct iovec vec; msg.msg_name = NULL; //通过socketpair产生的socket通信 不需要知道ip地址msg.msg_namelen = 0;msg.msg_iov = &vec;msg.msg_iovlen = 1;msg.msg_flags = 0;char sendchar = 0;//至少携带1Byte真实数据vec.iov_base = &sendchar;vec.iov_len = sizeof(sendchar);char cmsgbuf[CMSG_SPACE(sizeof(fd))]; //附属数据缓冲区大小msg.msg_control = cmsgbuf; //指向附属数据msg.msg_controllen = sizeof(cmsgbuf);//附属数据的配置p_cmsg = CMSG_FIRSTHDR(&msg); //返回附属数据部分的第一个cmsghdrp_cmsg->cmsg_level = SOL_SOCKET;p_cmsg->cmsg_type = SCM_RIGHTS;p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); //返回附属数据长度int *p_fds;p_fds = (int *) CMSG_DATA(p_cmsg); //返回附属数据的净荷数据地址*p_fds = fd; //设置待发送的文件描述符 将fd保存在净荷数据地址ret = sendmsg(sock_fd, &msg, 0); //发送描述符if (ret != 1)ERR_EXIT("sendmsg");
}
在接收文件描述符的时候:
int recv_fd(const int sock_fd) {int ret;struct msghdr msg;struct iovec vec;int recv_fd;char recvchar;vec.iov_base = &recvchar;vec.iov_len = sizeof(recvchar);msg.msg_name = NULL;msg.msg_namelen = 0;msg.msg_iov = &vec;msg.msg_iovlen = 1;char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];msg.msg_control = cmsgbuf;msg.msg_controllen = sizeof(cmsgbuf);msg.msg_flags = 0;int *p_fd;p_fd = (int *) CMSG_DATA(CMSG_FIRSTHDR(&msg));*p_fd = -1; //先将文件描述符设置为-1 后面判断是否接收到ret = recvmsg(sock_fd, &msg, 0); //接收文件描述符if (ret != 1)ERR_EXIT("recvmsg");struct cmsghdr *p_cmsg;p_cmsg = CMSG_FIRSTHDR(&msg); //返回附属数据部分的第一个cmsghdrif (p_cmsg == NULL)ERR_EXIT("no passed fd");p_fd = (int *) CMSG_DATA(p_cmsg); //取出第一个cmsghdr中的内容 即传递的文件描述符recv_fd = *p_fd;if (recv_fd == -1)ERR_EXIT("no passed fd");return recv_fd;
}
Linux 进程间传递文件描述符相关推荐
- Linux高级进程编程———在任意两个进程间传递文件描述符:使用 sendmsg 和 recvmsg 实现
进程间传递打开的文件描述符,并不是传递文件描述符的值.那么在传递时究竟传递了什么?我们要先搞明白这个问题. 1.文件描述符 文件描述符的值与文件没有任何联系,只是该文件在进程中的一个标识,所以同一文件 ...
- Linux中进程间传递文件描述符的方法
在进行fork调用后,由于子进程会拷贝父进程的资源,所以父进程中打开的文件描述符在子进程中仍然保持着打开,我们很容易的就将父进程的描述符传递给了子进程.但是除了这种情况下,如果想将某个父进程在子进程创 ...
- android进程间传递文件描述符原理
在Linux中,进程打开一个文件,返回一个整数的文件描述符,然后就可以在这个文件描述符上对该文件进行操作.那么文件描述符和文件到底是什么关系?进程使用的是虚拟地址,不同进程间是地址隔离的,如何在两个进 ...
- 进程间传递文件描述符--sendmsg,recvmsg(可用)
UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #include <sys/socket.h> ...
- 不相干进程之间传递文件描述符
#include <sys/socket.h> #include <fcntl.h> #include <stdio.h> #include <unistd. ...
- [转] linux系统文件流、文件描述符与进程间关系详解
http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题. 包括: ...
- Linux的辅助数据和传递文件描述符
简介 首先,明确传递文件描述符的意义.一般来说,在多进程网络编程中,我们设置一个主进程用于监听新来的连接,设置一个进程池,用于处理这些连接.但是,与线程池不同,进程池各个进程之间的空间是独立的,直接共 ...
- linux c中的文件描述符与打开文件之间的关系
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通 ...
- Linux虚拟文件系统、文件描述符、管道
在Linux中,所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,目录(Windows下称为文件夹)是文件,硬件设备(键盘.监视器.硬盘.打印机等)都是文件,就连套接字(socket ...
最新文章
- 微信小程序限制当前位置和目的地的距离
- SAP Retail系统门店主数据维护思路
- 硕士论文评审记录2021年春季
- 批量部署OFFICE2013
- SVN Files 的值“ .mine”无效。路径中具有非法字符。
- ios 从assets加载图片_iOS 使用Assets.xcassets添加启动图和Icon
- 园子装修 | 打造自己的直男审美博客园(施工中,持续更新)
- POJ--1300--Door Man【推断无向图欧拉通路】
- stl sort分析
- ubuntu18.04编译Open Pose 1.7
- Struts2拦截器Filter
- 创意字体设计,创意思路哪里找
- 程序员的奋斗史(十二)——谈信念
- 信息化管理系统(制造业ERP系统)
- android手机(平板)下载文件后,在文件管理软件中可以看到,通过mtp模式连接电脑后,无法在电脑上看到
- 跑步进入元宇宙,王老吉能看到不一样的未来吗?
- php中电话号码输入框,php中固定电话号码和手机号码正则表达式验证
- Java操作wkhtmltopdf实现Html转PDF
- 电脑启动后屏幕会间歇闪烁类似刷新
- jsp未正确拼写字 mysql_jsp与mysql的汉字乱码SQL中无法使用汉字的问题
热门文章
- 基石为勤能补拙的迷宫之旅——第九天(Python函数初步)
- 基于SSH的二手物品拍卖系统
- 第11期 《收获的路上》2017年5月刊
- 【EasyExcel应用】
- 2021年度中关村颠覆性技术研发和成果转化项目申报时间及要求,补贴500万元
- 不适定问题(Ill-posed problem)
- 华硕 内存条 不同步_双11还没到,价格战就已打响,酷兽RGB灯条直接杀价至379元|内存条|内存|台式机|酷兽|rgb...
- Android之九宫格抽奖及大转盘抽奖
- html页面透明度属性,css透明度属性是什么?
- 这几个文字转语音朗读软件很好用