• 传送文件描述符的简单示例


         传送进程描述符,简单的来说,就是进程A打开一个文件f,获得了一个文件描述符fd1,然后进程A将该描述符通过某些方式,传递给了B,此时B就具有了描述符fd2(注意,fd1 不一定等于fd2),从而可以通过fd2对文件f进行读写等一系列的操作。其实本质上

    相当于A,B两个进程同时打开了文件f。

         具体实现其实比较简单,例如当一个父进程要向子进程传递一个文件描述符时,首先会在fork产生子进程以前,调用socketpair建立一个套接字对,用于父子进程之间的通信。之后父进程打开文件f,获得文件描述符fd1。接着通过sendmsg将包含文件描述符fd1的消息发送出去,而在子进程通过recvmsg接收消息,从中获取出文件描述符fd2。最后,子进程就能通过操作fd2对文件f进行一系列的读写操作。

        最后,需要注意的是,当发送进程将文件描述符传送给接收进程以后,通常会关闭该描述符。不过,发送进程关闭该描述符并不会真的关闭该文件或设备,其原因是该文件描述符仍然视为由接收进程打开(即使接收进程尚未接收到该描述符,此时称该描述符在飞行中...in flight)。简单的代码示例,如下所示:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    
    #include <sys/socket.h>
    
    // CONTROLLEN 为cmsghdr加一个文件描述符的长度
    #define CONTROLLEN CMSG_LEN(sizeof(int)) 
    
    int send_fd(int fd, int fd_to_send) {
    	struct iovec	iov[1];
    	struct msghdr	msg;
    	char		buf[1];			// buf用于表示传递的描述符是否合法,合法buf[0]=0, 否则buf[0]=1
    	struct cmsghdr	*cmptr = NULL;
    
    	iov[0].iov_base = buf;		
    	iov[0].iov_len	= 1;		
    	msg.msg_iov	= iov;		// array of IO buffers
    	msg.msg_iovlen	= 1;		// number of elements in array
    	msg.msg_name	= NULL;
    	msg.msg_namelen	= 0;
    
    	if (fd_to_send < 0) {
    		msg.msg_control 	= NULL;
    		msg.msg_controllen	= 0;
    		buf[0] = 1;
    	} else {
    		// cmsghdr 包含了要传递的信息
    		if ((cmptr = malloc(CONTROLLEN)) == NULL) {
    			return -1;
    		}
    		cmptr->cmsg_level = SOL_SOCKET;
    		cmptr->cmsg_type  = SCM_RIGHTS;
    		cmptr->cmsg_len	  = CONTROLLEN;
    		msg.msg_control   = cmptr;
    		msg.msg_controllen= CONTROLLEN;
    		*(int*)CMSG_DATA(cmptr) = fd_to_send;
    		buf[0] = 0;
    	}
    	
    	if (sendmsg(fd, &msg, 0) != 1) {
    		return -1;
    	}
    	return 0;
    }
    
    int recv_fd(int fd, int *fd_to_recv) {
    	int 		nr;
    	char		buf[1];
    	struct iovec	iov[1];
    	struct msghdr	msg;
    	struct cmsghdr	*cmptr = NULL;
    	
    	iov[0].iov_base = buf;
    	iov[0].iov_len	= 1;
    	msg.msg_iov	= iov;
    	msg.msg_iovlen	= 1;
    	msg.msg_name	= NULL;
    	msg.msg_namelen	= 0;
    
    	if ((cmptr = malloc(CONTROLLEN)) == NULL) {
    		return -1;
    	}
    	msg.msg_control = cmptr;
    	msg.msg_controllen = CONTROLLEN;
    
    	if(recvmsg(fd, &msg, 0) < 0) {
    		printf("recvmsg error
    ");
    		return -1;
    	}
    
    	if(msg.msg_controllen < CONTROLLEN) {
    		printf("recv_fd get invalid fd
    ");
    		return -1;
    	}
    	
    	*fd_to_recv = *(int*)CMSG_DATA(cmptr);
    	return 0;
    }
    
    int main() {
    	int	fd;
    	pid_t	pid;
    	int	sockpair[2];
    	int	status;
    	char	fname[256];
    
    	status = socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair);
    	if (status < 0) {
    		printf("Call socketpair error, errno is %d
    ", errno);
    		return errno;
    	}
    
    	pid = fork();
    	if (pid == 0) {
    		close(sockpair[1]);
    		
    		status = recv_fd(sockpair[0], &fd);
    		if (status != 0) {
    			printf("[CHILD]: recv error, errno is %d
    ", status);
    			return status;
    		}
    
    		status = write(fd, "Yao DengDeng", strlen("Yao Dengdeng"));
    		if (status < 0) {
    			printf("[CHILD]: write error, errno is %d
    ", status);
    			return -1;
    		} else {
    			printf("[CHILD]: append logo successfully
    ");
    		}
    		close(fd);
    
    		exit(0);
    	}
    	
    	printf("[PARENT]: enter the filename:
    ");
    	scanf("%s", fname);
    
    	fd = open(fname, O_RDWR | O_APPEND);
    	if (fd < 0) {
    		printf("[PARENT]: open file error, errno is %d
    ", errno);
    		return -1;
    	}
    
    	status = send_fd(sockpair[1], fd);
    	if (status != 0) {
    		printf("[PARENT]: send_fd error, errno is %d
    ", status);
    		return -1;
    	}
    	close(fd);
    
    	wait(NULL);
    	return 0;
    }
    

      

    monster@monster-Z:~/TEST/c$ touch my.log
    monster@monster-Z:~/TEST/c$ gcc -o fdpass main.c
    monster@monster-Z:~/TEST/c$ ./fdpass 
    [PARENT]: enter the filename:
    my.log
    [CHILD]: append logo successfully
    monster@monster-Z:~/TEST/c$ cat my.log 
    Yao DengDeng
    

      

  • 相关阅读:
    Maven打包时去掉项目版本号
    maven编译的时候排除junit测试类
    Redis与Zookeeper实现分布式锁的区别
    分布式锁(基于redis和zookeeper)详解
    解读阿里巴巴集团的“大中台、小前台”组织战略
    java高并发系列
    JAVA之Unsafe学习笔记
    测试用例之正交排列法
    测试用例之因果图/判定表
    测试用例之边界值法
  • 原文地址:https://www.cnblogs.com/YaoDD/p/5919579.html
Copyright © 2020-2023  润新知