首页 > 代码库 > 传送文件描述符的简单示例

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

     传送进程描述符,简单的来说,就是进程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\n");		return -1;	}	if(msg.msg_controllen < CONTROLLEN) {		printf("recv_fd get invalid fd\n");		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\n", 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\n", status);			return status;		}		status = write(fd, "Yao DengDeng", strlen("Yao Dengdeng"));		if (status < 0) {			printf("[CHILD]: write error, errno is %d\n", status);			return -1;		} else {			printf("[CHILD]: append logo successfully\n");		}		close(fd);		exit(0);	}		printf("[PARENT]: enter the filename:\n");	scanf("%s", fname);	fd = open(fname, O_RDWR | O_APPEND);	if (fd < 0) {		printf("[PARENT]: open file error, errno is %d\n", errno);		return -1;	}	status = send_fd(sockpair[1], fd);	if (status != 0) {		printf("[PARENT]: send_fd error, errno is %d\n", status);		return -1;	}	close(fd);	wait(NULL);	return 0;}

  

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

  

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