• Linux网络编程——进程间传递文件描述符


    先引入一个例子,该程序的目的是子进程向父进程传递文件描述符,并通过该文件描述符读取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
    ", fd);
    		char buf[128] = {0};
    		read(fd, buf, sizeof(buf));
    		printf("buf = %s
    ", buf);
    		return 0;
    	}
    	else{
    		close(fds[0]);
    		int fd;
    		fd = open("file", O_RDWR);
    		printf("parent fd = %d
    ", 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
    ", fd); //输出为3
    		char buf[128] = {0};
    		read(fd, buf, sizeof(buf)); //fds[0]与fd同时为3,读阻塞
    		printf("buf = %s
    ", buf);
    		return 0;
    	}
    	else{
    		close(fds[0]); //父进程关闭文件描述符3
    		int fd;
    		fd = open("file", O_RDWR); //打开文件的描述符为fd = 3
    		printf("parent fd = %d
    ", 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
    ",fd,fds[0]);
    		char buf[128]={0};
    		read(fd,buf,sizeof(buf));
    		printf("buf=%s
    ",buf);
    		close(fds[0]);
    		return 0;
    	}else{
    		close(fds[0]);
    		int fd;
    		fd=open("file",O_RDWR);
    		printf("parent fd=%d
    ",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);
    }
    
  • 相关阅读:
    R语言导入scv文件乱码问题及解决
    水论文而已,科研就交给恐龙吧
    R语言实现单因素方差分析带字母的显著性标记
    记录新的学习
    如果你的校园网连不上很有可能是因为IE代理服务出错导致无法上网
    git 补充
    git
    学习linux的菜鸟 shell脚本中的循环
    学习linux的菜鸟 shell脚本中的逻辑判断
    学习linux的菜鸟 shell脚本中的dat,计算器,内置变量的用法
  • 原文地址:https://www.cnblogs.com/Mered1th/p/10771553.html
Copyright © 2020-2023  润新知