进程间传递文件描述符——sendmsg和recvmsg函数
先引入一个例子,该程序的目的是子进程向父进程传递文件描述符,并通过该文件描述符读取buf。
#include <func.h>
int main(){
int fds[2];
pipe(fds);
if(!fork()){
close(fds[1]);
int fd;
read(fds[0], &fd, sizeof(fd));
printf("child fd = %d\n", fd);
char buf[128] = {0};
read(fd, buf, sizeof(buf));
printf("buf = %s\n", buf);
return 0;
}
else{
close(fds[0]);
int fd;
fd = open("file", O_RDWR);
printf("parent fd = %d\n", fd);
write(fds[1], &fd, sizeof(fd));
wait(NULL);
return 0;
}
}
编译测试,发现结果不正确,通过ps aux查看到程序卡在了等待管道写数据,原因是卡在了第二个read读取buf处。我们再来看一下程序(见注释):
#include <func.h>
int main(){
int fds[2];
pipe(fds);
if(!fork()){
close(fds[1]); //子进程关闭文件描述符4,但fds[0]为3
int fd;
read(fds[0], &fd, sizeof(fd)); //通过fds[0]读出管道内容,写入fd中
printf("child fd = %d\n", fd); //输出为3
char buf[128] = {0};
read(fd, buf, sizeof(buf)); //fds[0]与fd同时为3,读阻塞
printf("buf = %s\n", buf);
return 0;
}
else{
close(fds[0]); //父进程关闭文件描述符3
int fd;
fd = open("file", O_RDWR); //打开文件的描述符为fd = 3
printf("parent fd = %d\n", fd);
write(fds[1], &fd, sizeof(fd)); //通过fds[1]写入管道内容
wait(NULL); //回收子进程
return 0;
}
}
所以我们必须借助内核传递文件描述符,sendmsg和recvmsg函数登场。
进程间传递文件描述符
步骤如下:
初始化socketpair类型描述符
sendmsg发送描述符
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
参1:sockfd指socket创建的FILENO
参2:结构体(见下)
参3:The flags argument is the bitwise OR of zero or more of the following flags.这里暂时不需要参数,先填0
使用的sockfd即sockpair初始化的描述符fds[1]
- 结构体 struct msghdr msg;
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 关键,即下面cmsghdr结构体地址 */
size_t msg_controllen; /* ancillary data buffer len cmsghdr结构体的长度*/
int msg_flags; /* flags (unused) */
};
- 结构体 struct cmsghdr
struct cmsghdr{
socklen_t cmsg_len; /* data byte count, including header */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by unsigned char cmsg_data[]; */
}
- 结构体msg_iov
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
cmsghdr结构体的初始化
int len = CMSG_LEN(sizeof(int));//通过CMSG_LEN计算cmsg_len,传递的fd的大小为整型四个字节
cmsg = (struct cmsghdr *)calloc(1, len);
cmsg->cmsg_len = len;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
int *fdptr;
fdptr = (int*)CMSG_DATA(cmsg);
*fdptr = fd;
msg_iov结构体初始化
struct iovec iov[2];
char buf1[10]="hello";
char buf2[10]="world";
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msghdr结构体初始化
/* iovec必须赋值 */
struct msghdr msg;
memset(&msg,0,sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_control = cmsg;
msg.msg_controllen = len;
最后就可以通过sendmsg来发送文件描述符,完整代码如下:
int sendFd(int sfd,int fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
struct iovec iov[2];
char buf1[10]="hello";
char buf2[10]="world";
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
struct cmsghdr *cmsg;
int len=CMSG_LEN(sizeof(int));//只传递一个文件描述符
cmsg=(struct cmsghdr *)calloc(1,len);
cmsg->cmsg_len=len;
cmsg->cmsg_level=SOL_SOCKET;
cmsg->cmsg_type=SCM_RIGHTS;
*(int*)CMSG_DATA(cmsg)=fd;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
ret=sendmsg(sfd,&msg,0);
ERROR_CHECK(ret,-1,"sendmsg");
return 0;
}
3.recvmsg接受文件描述符,接收的msghdr结构体初始化和sendmsg类似。
int recvFd(int sfd,int *fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
struct iovec iov[2];
char buf1[10];
char buf2[10];
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
struct cmsghdr *cmsg;
int len=CMSG_LEN(sizeof(int));
cmsg=(struct cmsghdr *)calloc(1,len);
cmsg->cmsg_len=len;
cmsg->cmsg_level=SOL_SOCKET;
cmsg->cmsg_type=SCM_RIGHTS;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
ret=recvmsg(sfd,&msg,0);
ERROR_CHECK(ret,-1,"sendmsg");
*fd=*(int*)CMSG_DATA(cmsg);
return 0;
}
完整代码:
#include <func.h>
int sendFd(int sfd,int fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
struct iovec iov[2];
char buf1[10]="hello";
char buf2[10]="world";
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
struct cmsghdr *cmsg;
int len=CMSG_LEN(sizeof(int));
cmsg=(struct cmsghdr *)calloc(1,len);
cmsg->cmsg_len=len;
cmsg->cmsg_level=SOL_SOCKET;
cmsg->cmsg_type=SCM_RIGHTS;
*(int*)CMSG_DATA(cmsg)=fd;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
ret=sendmsg(sfd,&msg,0);
ERROR_CHECK(ret,-1,"sendmsg");
return 0;
}
int recvFd(int sfd,int *fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
struct iovec iov[2];
char buf1[10];
char buf2[10];
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
struct cmsghdr *cmsg;
int len=CMSG_LEN(sizeof(int));
cmsg=(struct cmsghdr *)calloc(1,len);
cmsg->cmsg_len=len;
cmsg->cmsg_level=SOL_SOCKET;
cmsg->cmsg_type=SCM_RIGHTS;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
ret=recvmsg(sfd,&msg,0);
ERROR_CHECK(ret,-1,"sendmsg");
*fd=*(int*)CMSG_DATA(cmsg);
return 0;
}
int main()
{
int fds[2];
socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
if(!fork())
{
close(fds[1]);
int fd;
recvFd(fds[0],&fd);
printf("child fd=%d,fds[0]=%d\n",fd,fds[0]);
char buf[128]={0};
read(fd,buf,sizeof(buf));
printf("buf=%s\n",buf);
close(fds[0]);
return 0;
}else{
close(fds[0]);
int fd;
fd=open("file",O_RDWR);
printf("parent fd=%d\n",fd);
sendFd(fds[1],fd);
close(fds[1]);
wait(NULL);
return 0;
}
}
- writev和readv
#include <func.h>
int main(){
int fd = open("file", O_RDWR);
struct iovec iov[2];
char buf1[10] = "hello";
char buf2[10] = "world";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
writev(fd, iov, 2);//注意这里是2
close(fd);
}
进程间传递文件描述符——sendmsg和recvmsg函数的更多相关文章
- 进程间传递文件描述符fd
众所周知,子进程会继承父进程已经打开的文件描述符fd,但是fork之后的是不会被继承的,这个时候是否无能无力了?答应是NO.Linux提供了一个系统调用sendmsg,借助它,可以实现进程间传递文件描 ...
- Linux 进程间传递文件描述符
文章目录 文件描述符 文件数据结构 共享文件 UNIX域socket实现传递文件描述符 进程间传递打开的文件描述符,并不是传递文件描述符的值.先说一下文件描述符. 文件描述符 对内核来说,所有打开的文 ...
- Linux 利用进程打开的文件描述符(/proc)恢复被误删文件
Linux 利用进程打开的文件描述符(/proc)恢复被误删文件 在 windows 上删除文件时,如果文件还在使用中,会提示一个错误:但是在 linux 上删除文件时,无论文件是否在使用中,甚至是还 ...
- C/C++ 父子进程之间的文件描述符问题
在C程序中,文件由文件指针或者文件描述符表示.ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指针,UNIX的I/O ...
- LINUX中文件描述符传递
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
- [转] linux系统文件流、文件描述符与进程间关系详解
http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题. 包括: ...
- 文件描述符(File Descriptor)简介
本文转载自文件描述符(File Descriptor)简介 导语 维基百科:文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表.当程序打开一个 ...
- Linux文件描述符与打开文件之间的区别(转载)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为: ...
- linux文件描述符--转载
转自:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录 ...
随机推荐
- 消息 xxx,级别 16,状态 x,过程 sp_executesql,第 x 行 过程需要类型为 'ntext/nchar/nvarchar' 的参数 '@statement'。
消息 xxx,级别 16,状态 x,过程 sp_executesql,第 x 行 过程需要类型为 'ntext/nchar/nvarchar' 的参数 '@statement'. 原来的语句: ) s ...
- redis命令String类型(四)
String 字符串类型 命令: 1> 赋值 语法:set key value 比如:set test 123 2> 取值 语法:get key 比如:get test 3> 取值并 ...
- Python学习第五堂课
Python学习第五堂课推荐电影:华尔街之狼 被拯救的姜哥 阿甘正传 辛德勒的名单 肖申克的救赎 上帝之城 焦土之城 绝美之城 #上节内容: 变量 if else 注释 # ""& ...
- [转]How to Send Ethereum with Web3.js and Node
原文:https://davekiss.com/ethereum-web3-node-tutorial/ Ethereum took the web and cryptocurrency worl ...
- Image & Raw Image的区别
一.面板参数 1.Image类型: Source Image:图片资源(sprite) Color:颜色 Material:材质 Raycast Target :是否作为射线目标 Sprite 2D: ...
- pytorch怎么抽取中间的特征或者梯度
for i, (input, target) in enumerate(trainloader): # measure data loading time data_time.update(time. ...
- python excel 读写
python操作Excel读写--使用xlrd xlwt python中使用xlrd.xlwt操作excel表格详解
- Cannot create PoolableConnectionFactory (Communications link failure The last packet sent successfu
SQL: Cannot create JDBC driver of class '' for connect URL 使用数据库数据源的web 项目,发布后,访问数据库500报错: 浏览器端: 控制台 ...
- Android KitKat Immersive Mode使用
写了一个方法,在onCreate和onResume中调用即可,4.4以上可用. private void openImmersiveMode() { if (android.os.Build.VERS ...
- JMeter在linux服务器上使用
环境部署: 1.在Linux服务器先安装jdk:此步骤省略,可参考百度经验:https://jingyan.baidu.com/article/6b18230980c294ba59e15967.htm ...