LINUX中文件描述符传递

Wesley13
• 阅读 685

进程间传递文件描述符

第一步:初始化socketpair类型描述符(管道,全双工)

#include <sys/types.h> 

#include <sys/socket.h>

int socketpair(int domain, int type, int protocol, int sv\[2\]);


int fds\[2\];   //和无名管道不一样,无名管道只能用于父子进程之间,现在我们要用于网络;

socketpair(AF\_LOCAL,SOCK\_STREAM,0,fds);   //local,是因为控制信息只能在本地传

第二步sendmsg发送描述符

ssize\_t sendmsg(int sockfd, const struct msghdr \*msg, int flags);    

//内核控制信息传递接口,可以传递文件描述符

sockfd sockpair初始化的描述符 fds\[1\];



1).定义结构体  struct msghdr msg;
sendmsg 关键是初始化  msghdr结构体
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 结构体地址,告诉内核我们要传递哪一个结构体,只是一个指针,用的时候要malloc一个空间给他(CMSG\_LEN)
               size\_t        msg\_controllen;        /\* ancillary data buffer len \*/ cmsghdr结构体的长度前面是void\*,所以这里只能是字节
               int           msg\_flags;                /\* flags (unused) \*/ 没用
};
struct iovec
{
           void  \*iov\_base;           /\* Starting address \*/起始地址
           size\_t iov\_len;           /\* Number of bytes to transfer \*/要写的长度
};
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\[\]; \*/     //通过CMSG\_DATA获取这个成员的首地址,我们到时候只往这里放fd,告诉内核我们要传fd
};
unsigned char \*CMSG\_DATA(struct cmsghdr \*cmsg);    //把结构体传进去,返回要写入信息的首地址(待会要里面放fd(一个整形数)),也就是最后一块空间的首地址,最后面/\*...\*/中要放的信息
size\_t CMSG\_LEN(size\_t length);          //得出变长结构体大小16字节(这个是针对我们后面例子,传描述符,我们自己算的),待会分配空间要用到(前面三个成员指针3\*4,最后一个int型,一共16)
//这个接口的原理就是拿到我们要传递的参数的大小length,进去加上12,返回16

首先定义  struct cmsghdr \*cmsg 指针
cmsg\_len 中存取cmsghdr结构体的长度,通过CMSG\_LEN进行计算,我们传递的fd的大小为整型四个字节,所以

size\_t CMSG\_LEN(size\_t length);
int len = CMSG\_LEN(sizeof(int));(16字节)

然后为结构体申请空间:
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;
#include<sys/uio.h>
ssize\_t  readv(int fd,  const  struct  iovec  \*iov, int  iovcnt);

ssize\_t  writev(int fd,  const  struct  iovec  \*iov, int  iovcnt);
//这个函数可以传多个结构体,第二个数要传的结构体指针,第三个是要传的结构体个数

第三步:recvmsg 接收文件描述符,接收的 msghdr 结构体初始化和 sendmsg 几乎完全一致,区别如下:
ssize\_t recvmsg(int sockfd, struct msghdr \*msg, int flags);   //内核控制信息传递接口,可以接受文件描述符
fd = \*fdptr;


#include<stdio.h>         
#include<stdlib.h>           
#include<string.h>            
#include<sys/uio.h>       
#include<sys/stat.h>       
#include<sys/types.h>      
#include<fcntl.h>
LINUX中文件描述符传递
int main()
{
        int fd=open("file1",O\_RDWR);
        char buf1\[10\]="hello ";
        char buf2\[10\]="world\\n";
        struct iovec iov\[2\];
        iov\[0\].iov\_base=buf1;
        iov\[0\].iov\_len=strlen(buf1);
        iov\[1\].iov\_base=buf2;
        iov\[1\].iov\_len=strlen(buf2);
        int ret=writev(fd ,iov,2);     if(-1==ret)  { perror("writev"); return -1; }
//如果fd没有被赋值打开的文件描述符,这里传参默认0;
}

man cmsg
func.h


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>

void send\_fd(int fds,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=sendmsg(fds,&msg,0);       
        if(-1==ret)
        {
                perror("sendmsg");
                return;
        }
}
void recv\_fd(int fds,int\* pfd)
{
        struct msghdr msg;
        memset(&msg,0,sizeof(msg));
        struct iovec iov\[2\];
        char buf1\[10\]={0};
        char buf2\[10\]={0};
        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=recvmsg(fds,&msg,0);       
        if(-1==ret)
        {
                perror("recvmsg");
                return;
        }
        \*pfd=\*(int\*)CMSG\_DATA(cmsg);
}
#include "func.h"
int main()
{
        int fds\[2\];      //全双工
        //pipe(fds);   //这个管道不是前面学的无名管道
        int ret;
        ret=socketpair(AF\_LOCAL,SOCK\_STREAM,0,fds);
        if(-1==ret)
        {
                perror("socketpair");
                return -1;
        }
        if(!fork())
        {
                close(fds\[1\]);
                int fd;
                recv\_fd(fds\[0\],&fd);
                printf("child fd=%d\\n",fd);
                char buf\[10\]={0};
                read(fd,buf,sizeof(buf));
                printf("buf=%s\\n",buf);
                close(fd);
                exit(0);
        }else{
                close(fds\[0\]);
                int fd=open("file",O\_RDWR);
                printf("parent fd=%d\\n",fd);
                send\_fd(fds\[1\],fd);
                close(fd); //发过去,关闭,引用计数降为1
                wait(NULL);
                return 0;
        }
}
//fds\[0\]和fds\[1\]都具有读写属性,但是也要关闭一端,剩下的那一端都可以读或者写。

LINUX中文件描述符传递    //不用管为什么父进程是3传给子进程就变成4; 描述符是操作系统内核管控的, 传递只是把控制信息给传递过去了, 不用管具体描述符数字是多少;











点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
C++面向对象实现封装线程池
<htmlxmlns"http://www.w3.org/1999/xhtml"<head<stylebody,table{fontfamily:微软雅黑;fontsize:13.5pt}table{bordercollapse:collapse;border:solidgray;borderwidth:2px
Stella981 Stella981
3年前
AVR 嵌入式单片机芯片的中断系统介绍
<htmlxmlns"http://www.w3.org/1999/xhtml"<head<stylebody,table{fontfamily:微软雅黑;fontsize:13.5pt}table{bordercollapse:collapse;border:solidgray;borderwidth:2px
Wesley13 Wesley13
3年前
LINUX打开文件
<htmlxmlns"http://www.w3.org/1999/xhtml"<head<stylebody,table{fontfamily:微软雅黑;fontsize:10pt}table{bordercollapse:collapse;border:solidgray;borderwidth:2px0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这