linux 传递文件描述符,历程间传递文件描述符 - UNIX

当前位置:我的异常网» Linux/Unix » 历程间传递文件描述符 - UNIX

历程间传递文件描述符 - UNIX

www.myexceptions.net  网友分享于:2013-08-16  浏览:19次

进程间传递文件描述符 - UNIX

先来两个函数: unix_send_fd 和 unix_recv_fd

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结构,然后将该文件描述符保存到用户态地址中。

文章评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值