进程间传递文件描述符——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系统中一切皆可以看成是文件,文件又可分为:普通文件.目录 ...
随机推荐
- python实现三级菜单间的前后跳转
#录入菜单def createMenu(): Menu = {} provinces = ["陕西","四川","江苏"] sh ...
- 机器学习基础环境的安装与使用(MAC版)
使用到Matplotlib.Numpy.Pandans等库 1.创建虚拟环境 >>>> sudo pip3 install virtualenv >>>> ...
- Linux 驱动——Button驱动4(fasync)异步通知
button_drv.c驱动文件: #include <linux/module.h>#include <linux/kernel.h>#include <linux/f ...
- 牛客网PAT乙级(Basic Level)真题-组个最小数 (20)
组个最小数 (20) 时间限制 1000 ms 内存限制 32768 KB 代码长度限制 100 KB 判断程序 Standard (来自 小小) 题目描述 给定数字0-9各若干个.你可以以任意顺序排 ...
- Ubuntu系统下Anaconda使用方法总结
前言:个人认为Anaconda比pip使用起来要方便很多,因为它创建虚拟环境和下载各种库都很方便.但是有时候conda的安装方法中没有某些库,必须使用pip来进行安装.所以,主要用Anaconda,偶 ...
- Hailstone冰雹序列问题
在复习数据结构,课程中提到Hailstone冰雹序列问题,问题如下 代码如下: public List<int> Hailstone(int n) { List<int> li ...
- Go小爬虫测试
package main import ( "fmt" "io/ioutil" "log" "net/http" ) t ...
- C#实现按键计算器功能
C#实现按键计算器功能 (一次失败的编程) 界面: 代码如下: using System; using System.Collections.Generic; using System.Compone ...
- MySql最土的语法解释使用一。
create database namedb charset utf8;解释:创建一个数据库 namedb改成你的数据库名字,charset是字符集的意思 utf8代表数据库支持中文字符集.必须分号结 ...
- java web(五):java web三大组件之另外两个和八大监听器
java的三大组件指Servlet.Filter.Listener.八大监听器指八个接口.前面介绍了Servlet,现在介绍一下Filter拦截器以及拦截地址的设置, Listener监听那些事件. ...