body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

进程间传递文件描述符

第一步:初始化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>

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中文件描述符传递的更多相关文章

  1. Linux中文件描述符fd和文件指针flip的理解

    转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...

  2. [转载] linux中文件描述符fd和文件指针flip的理解

    转载自http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...

  3. linux中文件描述符

    :: # cat ping.txt PING baidu.com (() bytes of data. bytes from ttl= time=32.1 ms bytes from ttl= tim ...

  4. 对于Linux中文件描述符的疑问以及解决

    问题 ​ 每次web服务器或者是几乎所有Linux服务器都需要对文件描述符进行调整,我使用ulimit -n来查看当前用户的最多能打开的文件,默认设置的是1024个,但是系统运行起来以及开启一些简单的 ...

  5. Linux下文件描述符

    http://blog.csdn.net/kumu_linux/article/details/7877770 文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket.第一个打开的 ...

  6. linux 最大文件描述符fd

    使用四种框架分别实现百万websocket常连接的服务器 著名的 C10K 问题提出的时候, 正是 2001 年.这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务 ...

  7. Linux Shell 文件描述符 及 stdin stdout stderr 重定向

    Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已 ...

  8. linux 最大文件描述符

    Linux对应用程序能打开的的最大文件描述符数量有两个层次的限制:用户级限制和系统级限制. 用户级限制是指目标用户运行的所有进程总共能打开的文件描述符数. 系统级的限制是指所有用户总共能打开的文件描述 ...

  9. 【详解】Linux的文件描述符fd与文件指针FILE*互相转换

    使用系统调用的时候用文件描述符(file descriptor,简称fd)的时候比较多,但是操作比较原始.C库函数在I/O上提供了一些方便的包装(比如格式化I/O.重定向),但是对细节的控制不够. 如 ...

随机推荐

  1. 高可用Mysql架构_Mycat集群部署(HAProxy + 两台Mycat+Mysql双主双从)

    既然大家都知道了Mysql分布式在大型网站架构中的作用,在这里就不再阐述.本片博客文章是基于我曾经搭建过的一个Mysql集群基础上实现的,实现过双主热备.读写分离.分库分表. 博客链接:http:// ...

  2. Java-JVM调优常见配置举例

    常见配置举例 堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统 下,一般限制在1.5G~ ...

  3. Linux安全基线检查脚本

    基线检查内容: 一:共享账号检查 配置名称:用户账号分配检查,避免共享账号存在配置要求:1.系统需按照实际用户分配账号; 2.避免不同用户间共享账号,避免用户账号和服务器间通信使用的账号共享.操作指南 ...

  4. RocEDU.阅读.写作《苏菲的世界》书摘(二)

    "迷信",多么奇怪的一个名词.如果你信基督教或伊斯兰教,这就叫"信仰",但如果你相信占星术或十三号星期五不吉利,就是迷信.谁有权利说别人相信的东西就是" ...

  5. RocEDU.阅读.写作《乌合之众》(一)

    序言 作者在序言里主要论述了时代演变的内在原因,表明对群体进行研究的重要性,阐述了研究群体行为特征时的研究方法,并概述了群体的发展过程. 造成文明变革的唯一重要变化,是影响到思想.观念和信仰的变化.目 ...

  6. 20145335郝昊《Java程序设计》第2周学习总结

    20145335郝昊<Java程序设计>第2周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long整 ...

  7. cmd中如何查看环境变量

    查看所有环境变量 set 查看某一个环境变量 C:\WINDOWS\system32>set no_proxyNO_PROXY=localhost,127.0.0.1,172.31.212.14 ...

  8. storm(一) window机制

    Watermark作用 在解释storm的window之前先说明一下watermark原理. Watermark中文翻译为水位线更为恰当. 顺序的数据从源头开始发送到到操作,中间过程肯定会出现数据乱序 ...

  9. 拓扑排序 - hdu 1285(普通和优先队列优化)

    2017-09-12 19:50:58 writer:pprp 最近刚开始接触拓扑排序,拓扑排序适用于:无圈图的顶点的一种排序, 用来解决有优先级别的排序问题,比如课程先修后修,排名等. 主要实现:用 ...

  10. Linux 实用操作命令

    1. ssh远程连接服务器命令 ssh [username@]hostname 2. 查看远程服务器近期登陆记录 last 3. 用户及其主目录的创建 1.  useradd –d /home/lb ...