浏览 4110 次
锁定老帖子 主题:进程间传递文件描述符 - UNIX
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (8)
|
|
---|---|
作者 | 正文 |
发表时间:2010-01-19
最后修改:2010-01-19
int unix_send_fd(int fd, int sendfd) { struct msghdr msg; struct iovec iov[1]; /* * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE; the * latter breaks on LP64 systems. */ #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) union { struct cmsghdr just_for_alignment; char control[CMSG_SPACE(sizeof(sendfd))]; } control_un; struct cmsghdr *cmptr; memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ msg.msg_control = control_un.control; msg.msg_controllen = CMSG_LEN(sizeof(sendfd)); /* Fix 200506 */ cmptr = CMSG_FIRSTHDR(&msg); cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd)); cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; *(int *) CMSG_DATA(cmptr) = sendfd; #else msg.msg_accrights = (char *) &sendfd; msg.msg_accrightslen = sizeof(sendfd); #endif msg.msg_name = 0; msg.msg_namelen = 0; /* * XXX We don't want to pass any data, just a file descriptor. However, * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble. See the * comments in the unix_recv_fd() routine. */ iov->iov_base = (void *)""; iov->iov_len = 1; msg.msg_iov = iov; msg.msg_iovlen = 1; return (sendmsg(fd, &msg, 0)); } /* unix_recv_fd - receive file descriptor */ int unix_recv_fd(int fd) { const char *myname = "unix_recv_fd"; struct msghdr msg; int newfd; struct iovec iov[1]; char buf[1]; /* * Adapted from: W. Richard Stevens, UNIX Network Programming, Volume 1, * Second edition. Except that we use CMSG_LEN instead of CMSG_SPACE, for * portability to LP64 environments. */ #if defined(CMSG_SPACE) && !defined(NO_MSGHDR_MSG_CONTROL) union { struct cmsghdr just_for_alignment; char control[CMSG_SPACE(sizeof(newfd))]; } control_un; struct cmsghdr *cmptr; memset((char *) &msg, 0, sizeof(msg)); /* Fix 200512 */ msg.msg_control = control_un.control; msg.msg_controllen = CMSG_LEN(sizeof(newfd)); /* Fix 200506 */ #else msg.msg_accrights = (char *) &newfd; msg.msg_accrightslen = sizeof(newfd); #endif msg.msg_name = 0; msg.msg_namelen = 0; /* * XXX We don't want to pass any data, just a file descriptor. However, * setting msg.msg_iov = 0 and msg.msg_iovlen = 0 causes trouble: we need * to read_wait() before we can receive the descriptor, and the code * fails after the first descriptor when we attempt to receive a sequence * of descriptors. */ iov->iov_base = buf; iov->iov_len = sizeof(buf); msg.msg_iov = iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, 0) cmsg_len == CMSG_LEN(sizeof(newfd))) { if (cmptr->cmsg_level != SOL_SOCKET) printf("%s: control level %d != SOL_SOCKET", myname, cmptr->cmsg_level); if (cmptr->cmsg_type != SCM_RIGHTS) printf("%s: control type %d != SCM_RIGHTS", myname, cmptr->cmsg_type); return (*(int *) CMSG_DATA(cmptr)); } else return (-1); #else if (msg.msg_accrightslen == sizeof(newfd)) return (newfd); else return (-1); #endif } 这两个函数来自 postfix。其中NO_MSGHDR_MSG_CONTROL应该是针对Tru64平台特有的定义。我们知道UNIX的文件描述符是一个整数,这个整数是由内核维护的一个数组的下标。在进程内部,对文件描述的操作有: open,close, dup 和 dup2。dup和dup2是将文件描述符复制一份,带SOL_SOCKET的sendmsg是不是也是这样的,它又是如何在进程间复制文件描述符的呢? 先看sendmsg在Linux内核的实现: net/socket.c: sendmsg net/socket.c: --> sock_sendmsg net/socket.c: --> __sock_sendmsg --> security_socket_sendmsg --> sock->ops->sendmsg security_socket_sendmsg 是一个和安全相关的函数,暂时忽略。 从代码可以看出,unix_send_fd必须使用UNIX域socket,所以sock->ops->sendmsg指向unix_stream_sendmsg 函数。 net/unix/af_unix.c: unix_stream_sendmsg include/net/scm.h: --> scm_send net/core/scm.c: --> __scm_send net/core/scm.c: --> scm_fp_copy 在scm_fp_copy中,内核将要发送的文件描述符对应的file引用计数加一,发送端关闭文件时并不会真正关闭文件描述符。当然,此时发送端也可一直打开该文件描述符。 从接收端看,recvmsg堆栈和sendmsg差不多,sock->ops_recvmsg指向unix_stream_recvmsg。 在unix_stream_recvmsg又调用 scm_recv --> scm_detach_fds。 在scm_detach_fds为发送时保存的文件分配一个未使用的文件描述符,并分配file结构,然后将该文件描述符保存到用户态地址中。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |