进程间传递文件描述符——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函数登场。

进程间传递文件描述符

步骤如下:

  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;
}
  • 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);
}

原文地址:https://www.cnblogs.com/Mered1th/p/10771553.html

时间: 2024-08-01 12:21:10

进程间传递文件描述符——sendmsg和recvmsg函数的相关文章

进程间传递文件描述符

下面的实例展示了如何使用Unix域套接字在进程间传递文件描述符 参考文献:1) <Unix网络编程> 2)  http://book.51cto.com/art/200912/168560.htm 最近学习了使用Unix域套接字在进程间传递文件描述符,仿照参考资料,自己也写了简单的程序来实践这种技术. 其他不多说了,具体理论知识参见参考资料,开始我自己的程序介绍(在OpenSolaris 2009.06平台上测试): 1  程序作用说明:父进程,子进程以及另外一个进程向同一个文件的文件描述符向

在进程间传递文件描述符

由于fork调用之后,父进程中打开的文件描述符在子进程中仍然保持打开,所以文件描述符可以很方便地从父进程传递到子进程.需要注意的是,传递一个文件描述符并不是传递一个文件描述符的值,而是要在接收进程中创建一个新的文件描述符,并且该文件描述符和发送进程中被传递的文件描述符指向内核中相同的文件表项. 在Linux下,我们可以利用UNIX城socket在进程间传递特殊的辅助数据,以实现文件描述符的传递,它在子进程中打开一个文件描述符,然后将它传递给父进程,父进程则通过读取该文件描述符来获得文件的内容.

python进程间传递文件描述符扩展库

由于python本身的线程基本上比较残废,所以为了利用机器的cpu,就不得不用上多进程... 在游戏服务器的设计中,最为常见的方式是: 挂一个前端服务器,专门来维护与客户端的连接,然后将客户端的请求数据转发给后端服务器... 上面的方式是现在最为正统的... 但是自己因为环境的限制,需要做到对客户端透明,然后将后端的服务器转换成为多进程的...所以这里就只有用一点比较别扭的方法了,首先处理登录等一些常规的逻辑放在前端服务器,当进入放进进行匹配战斗之后,将客户端的socket连接直接交给后端服务器

UNIX进程之间传递文件描述符recvmsg与sendmsg

socketpair: 功能:创建一个全双工的流管道 原型 int socketpair(int domain, int type, int protocol, int sv[2]); 参数 domain: 协议家族 type: 套接字类型 protocol: 协议类型 sv: 返回套接字对 返回值:成功返回0:失败返回-1 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int

0707 父子进程之间传递文件描述符

1 /************************************************************************* 2 > File Name: pass_fd.c 3 > Author:Monica 4 > Mail:[email protected] 5 > Created Time: Mon 07 Jul 2014 09:52:49 PM CST 6 ********************************************

通过UNIX域套接字传递文件描述符

传送文件描述符是高并发网络服务编程的一种常见实现方式.Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现.本文详细说明一下传送文件描述符的应用. 1. TCP服务器程序设计范式 ??开发一个服务器程序,有较多的的程序设计范式可供选择,不同范式有其自身的特点和实用范围,明了不同范式的特性有助于我们服务器程序的开发.常见的TCP服务器程序设计范式有以下几种: 迭代服务器 并发服务器,每个客户请求fork一个子进程 预先派生子进程,每个子进程无保护地调用accept 预先

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

在C程序中,文件由文件指针或者文件描述符表示.ISO C的标准I/0库函数(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指针,UNIX的I/O函数(open, close, read, write, ioctl)使用文件描述符.下面重点来说下,文件描述符是如何工作的. 文件描述符相当于一个逻辑句柄,而open,close等函数则是将文件或者物理设备与句柄相关联.句柄是一个整数,可以理解为进程特定的文件描述符表的 索引.先介绍下面三个概念,

在父子进程间用管道传递文件描述符

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #define ERR_EXI

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

http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题. 包括: 1.linux多/单进程与多/单文件对于文件流和描述符在使用时的关联情况及一些需要注意的问题. 2.fork,vfork流缓冲等对文件操作的影响. 1.linux文件系统结构 首先补充一点基础知识,了解一下linux文件系统.如下图所示: 图1 磁盘,分区和文件系统 应该明白,上图所示结构是硬盘中文件存放