先引入一个例子,该程序的目的是子进程向父进程传递文件描述符,并通过该文件描述符读取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函数登场。

进程间传递文件描述符

步骤如下:

  1. 初始化socketpair类型描述符

  2. 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函数的更多相关文章

  1. 进程间传递文件描述符fd

    众所周知,子进程会继承父进程已经打开的文件描述符fd,但是fork之后的是不会被继承的,这个时候是否无能无力了?答应是NO.Linux提供了一个系统调用sendmsg,借助它,可以实现进程间传递文件描 ...

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

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

  3. Linux 利用进程打开的文件描述符(/proc)恢复被误删文件

    Linux 利用进程打开的文件描述符(/proc)恢复被误删文件 在 windows 上删除文件时,如果文件还在使用中,会提示一个错误:但是在 linux 上删除文件时,无论文件是否在使用中,甚至是还 ...

  4. C/C++ 父子进程之间的文件描述符问题

    在C程序中,文件由文件指针或者文件描述符表示.ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指针,UNIX的I/O ...

  5. LINUX中文件描述符传递

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  6. [转] linux系统文件流、文件描述符与进程间关系详解

    http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题. 包括:     ...

  7. 文件描述符(File Descriptor)简介

    本文转载自文件描述符(File Descriptor)简介 导语 维基百科:文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表.当程序打开一个 ...

  8. Linux文件描述符与打开文件之间的区别(转载)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239   1. 概述     在Linux系统中一切皆可以看成是文件,文件又可分为: ...

  9. linux文件描述符--转载

    转自:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述     在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录 ...

随机推荐

  1. 常用oracle可重复执行的脚本模板

    为保证脚本的可重复执行以及丢失,涉及到数据库环境的移植等,就会使用可重复执行脚本,此处仅提供相关一些模板 说明下:该脚本需要在命令窗口执行,而不是在SQL窗口执行 创建序的脚本 /** * 作者:zk ...

  2. Python_文件处理

    1.Python  文件处理 打开文件---->读取内容---->获得内容 读取文件方式:  r  只读文件  w 只写模式 a 追加模式 r+b 读写模式   w+b 写读模式  a+b ...

  3. 使用flask+SQL语句实现通过前台收到的信息向数据库中插入事件+初级CSS+HTML拯救一下我的主页·····

    1.使用flask+SQL语句实现**#@&&(懒得再扣一遍题目) 上代码: @app.route('/register', methods=['POST', 'GET']) def ...

  4. Go Lang

    IDE: https://www.jetbrains.com/products.html?fromMenu#type=ide Study: http://www.runoob.com/go/go-en ...

  5. 如何通过Chrome远程调试android设备上的Web网站

    网上的帖子很多,但很多都是老版本的,试过了,根本不管用,花了一天时间,终于在本机试验通过了,特记录下来,以备用.有需要的朋友也可以参考.先上一张图,看看PC端chrome上调试的效果: 左边是手机的模 ...

  6. elasticsearch(es) 集群恢复触发配置(Local Gateway参数)

    elasticsearch(es) 集群恢复触发配置(Local Gateway) 当你集群重启时,几个配置项影响你的分片恢复的表现. 首先,我们需要明白如果什么也没配置将会发生什么. 想象一下假设你 ...

  7. 阿里云 oss 图片上传解决方案 vue (web直传)

    我们通过aliyun-oss-web这个npm去解决 该文章主要介绍如何获取 imgSignature 和 imgPolicy 这两个参数 首先下载 web直传的案例 : http://files.c ...

  8. 实验三 CC2530平台上CC2530平台上定时器组件的

    实验三 CC2530平台上CC2530平台上定时器组件的TinyOS编程 实验目的: 加深和巩固学生对于TinyOS编程方法的理解和掌握 让学生初步掌握CC2530定时器的PWM功能,及其TinyOS ...

  9. Spring mvc后台重定向页面,实际前端不跳转

    1.ajax不支持重定向 ajax是不支持重定向的,因为ajax本身就是局部刷新,不重新加载页面的. 2.若后台出现需要重定向页面,可以设置唯一错误码 前端ajax公共调用后,凡是遇到这一类错误码,则 ...

  10. ARM Cortex M0 程序映像和启动流程