进程间传递文件描述符——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系统中一切皆可以看成是文件,文件又可分为:普通文件.目录 ...
随机推荐
- centos6.5安装Mysql5.6及更改密码
(一) centos6.5安装Mysql5.6 二进制文件安装的方法分为两种: 第一种是不针对特定平台的通用安装方法,使用的二进制文件是后缀为.tar.gz的压缩文件: 第二种是使用RPM或其他包进行 ...
- 【SHELL】:定时任务删除指定目录
现有一个需求,需要定时删除积累过久的目录,并且部分目录不可删除.在此,我们分析了一下该父目录的命名结构:非连续性数字命名,部分目录中包含a.txt文件.如下图所示: 在此,我们对此可作如下操作,获取该 ...
- 【消息中间件之RabbitMQ学习】-开篇-001
写在前面的话 项目中因为要用到消息中间件,当初极力推荐RabbitMq.但因为种种原因,最终选型为java+mongodb自实现一套分布式的消.没有用RabbitMq工作过,实属遗憾.因为个人来说实在 ...
- 07_mysql常用sql语句
一.数据库相关 1.创建数据库: mysql> create database test default character set utf8 collate utf8_general_ci;Q ...
- mybatis 类创建流程
Configuration ---> XmlConfigBuilder ---> SqlSessionFactoryBuilder ---> SqlSessionFactory(Co ...
- Skynet服务热点火焰图分析
最近花了一周时间对场景服务进行热点分析,利用以前的火焰图工具做了一点微小的贡献,分享下心得(仓库地址在https://github.com/spin6lock/skynet_systemtap_set ...
- Java基于opencv实现图像数字识别(二)—基本流程
Java基于opencv实现图像数字识别(二)-基本流程 做一个项目之前呢,我们应该有一个总体把握,或者是进度条:来一步步的督促着我们来完成这个项目,在我们正式开始前呢,我们先讨论下流程. 我做的主要 ...
- HTML下标签之应用
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" ...
- maven安装本地jar包到本地仓库命令
mvn install:install-file -Dfile=C:\Users\windows\.m2\repository\com\jayway\jsonpath\json-path\2.2.0\ ...
- 《DSP using MATLAB》Problem 7.24
又到清明时节,…… 注意:带阻滤波器不能用第2类线性相位滤波器实现,我们采用第1类,长度为基数,选M=61 代码: %% +++++++++++++++++++++++++++++++++++++++ ...