發(fā)表時(shí)間:2010-01-19   最后修改:2010-01-19
先來(lái)兩個(gè)函數(shù): 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
}

這兩個(gè)函數(shù)來(lái)自 postfix。其中NO_MSGHDR_MSG_CONTROL應(yīng)該是針對(duì)Tru64平臺(tái)特有的定義。我們知道UNIX的文件描述符是一個(gè)整數(shù),這個(gè)整 數(shù)是由內(nèi)核維護(hù)的一個(gè)數(shù)組的下標(biāo)。在進(jìn)程內(nèi)部,對(duì)文件描述的操作有: open,close, dup 和 dup2。dup和dup2是將文件描述符復(fù)制一份,帶SOL_SOCKET的sendmsg是不是也是這樣的,它又是如何在進(jìn)程間復(fù)制文件描述符的呢?
先看sendmsg在Linux內(nèi)核的實(shí)現(xiàn):
net/socket.c: sendmsg
net/socket.c: --> sock_sendmsg
net/socket.c: --> __sock_sendmsg
--> security_socket_sendmsg
--> sock->ops->sendmsg
security_socket_sendmsg 是一個(gè)和安全相關(guān)的函數(shù),暫時(shí)忽略。
從代碼可以看出,unix_send_fd必須使用UNIX域socket,所以sock->ops->sendmsg指向unix_stream_sendmsg 函數(shù)。
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中,內(nèi)核將要發(fā)送的文件描述符對(duì)應(yīng)的file引用計(jì)數(shù)加一,發(fā)送端關(guān)閉文件時(shí)并不會(huì)真正關(guān)閉文件描述符。當(dāng)然,此時(shí)發(fā)送端也可一直打開(kāi)該文件描述符。

從接收端看,recvmsg堆棧和sendmsg差不多,sock->ops_recvmsg指向unix_stream_recvmsg。
在unix_stream_recvmsg又調(diào)用 scm_recv --> scm_detach_fds。
在scm_detach_fds為發(fā)送時(shí)保存的文件分配一個(gè)未使用的文件描述符,并分配file結(jié)構(gòu),然后將該文件描述符保存到用戶(hù)態(tài)地址中。