http://blog.csdn.net/ly21st http://ly21st.blog.chinaunix.net
分类: LINUX
2011-09-27 13:38:58
Unix域提供了两种类型的套接口:字节流套接口(与tcp类似)和数据报套接口(与udp类似)
14.2 Unix域套接口地址结构14.7 描述字传递
当考虑从一个进程向另一个进程传递所打开的描述字时,我们通常会想到:
1)在fork调用后,子进程共享父进程的所有打开的描述字;
2)在调用exec时所有描述字仍保持打开。
在两个进程间传递描述字的步骤是:
1)创建一个字节流的或数据报的Unix域套接口。如果目标是fork一个子进程,让子进程打开描述字并将它传回给父进程,那么父进程可以用socketpair创建一个流管道,用它来传递描述字。如果进程间没有亲缘关系,那么服务器必须创建一个Unix域字节流套接口,bind一个路径名,让客户connect到这个套接口,然后客户可以向服务器发送一个请求以打开某个描述字,服务器将描述字通过Unix域套接口传回。
2)进程可以用任何返回描述字的Unix函数打开一个描述字;
3)发送进程建立一个msghdr结构,其中包含要传递的描述字。发送进程调用sendmsg通过第一步得到的Unix域套接口发送描述字。这时我们说这个描述字是“飞行中”的。即使在发送进程调用sendmsg后,但在仅售进程调用recvmsg之前将描述字关闭,它仍会为接收进程保持打开状态。描述字哦的发送导致它的访问计数加1;
4)接收进程调用recvmsg在unix域套接口上接收描述字。通常接收进程收到的描述字编号和发送进程中的描述字的编号不同,但这没有问题。传递描述字不是传递描述字的编号,而是在接收进程中创建一个新的描述字,指向内核的文件表中与发送进程发送的描述字相同的项。
客户和服务器之间必须有某种应用协议,使接收方知道何时接收描述字。如果接收方调用recvmsg但没有分配接收描述字的空间,而且有一个描述字哦已被传递并正待读出,这个已传递的描述字就会关闭。在用recvmsg接收描述字时还要避免使用MSG_PEEK标志,否则后果不可预测。
通过执行另一个程序来打开文件的好处是,另一个程序可以是一个setuid到root的程序,能打开原来没有权限打开的文件。这个程序能够扩展通常的Unix权限(用户、用户组和其他用户)到它想要的任何形式的访问检查。
进程间传递打开的描述最的一个例子:
# mycat.c
#include "unp.h"
int my_open(const char *, int);
int
main(int argc, char **argv)
{
int fd, n;
char buff[BUFFSIZE];
if (argc != 2)
err_quit("usage: mycat
if ( (fd = my_open(argv[1], O_RDONLY)) < 0)
err_sys("cannot open %s", argv[1]);
while ( (n = Read(fd, buff, BUFFSIZE)) > 0)
Write(STDOUT_FILENO, buff, n);
exit(0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# myopen.c
#include "unp.h"
int
my_open(const char *pathname, int mode)
{
int fd, sockfd[2], status;
pid_t childpid;
char c, argsockfd[10], argmode[10];
Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
if ( (childpid = Fork()) == 0) { /* child process */
Close(sockfd[0]);
snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
snprintf(argmode, sizeof(argmode), "%d", mode);
execl("./openfile", "openfile", argsockfd, pathname, argmode,
(char *) NULL);
err_sys("execl error");
}
/* parent process - wait for the child to terminate */
Close(sockfd[1]); /* close the end we don't use */
Waitpid(childpid, &status, 0);
if (WIFEXITED(status) == 0)
err_quit("child did not terminate");
if ( (status = WEXITSTATUS(status)) == 0)
Read_fd(sockfd[0], &c, 1, &fd);
else {
errno = status; /* set errno value from child's status */
fd = -1;
}
Close(sockfd[0]);
return(fd);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# read_fd.c
/* include read_fd */
#include "unp.h"
ssize_t
read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
#else
int newfd;
msg.msg_accrights = (caddr_t) &newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, 0)) <= 0)
return(n);
#ifdef HAVE_MSGHDR_MSG_CONTROL
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_RIGHTS)
err_quit("control type != SCM_RIGHTS");
*recvfd = *((int *) CMSG_DATA(cmptr));
} else
*recvfd = -1; /* descriptor was not passed */
#else
/* *INDENT-OFF* */
if (msg.msg_accrightslen == sizeof(int))
*recvfd = newfd;
else
*recvfd = -1; /* descriptor was not passed */
/* *INDENT-ON* */
#endif
return(n);
}
/* end read_fd */
ssize_t
Read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
ssize_t n;
if ( (n = read_fd(fd, ptr, nbytes, recvfd)) < 0)
err_sys("read_fd error");
return(n);
}
__________________________________________________________________________________
openfile.c
#include "unp.h"
int
main(int argc, char **argv)
{
int fd;
if (argc != 4)
err_quit("openfile
if ( (fd = open(argv[2], atoi(argv[3]))) < 0)
exit( (errno > 0) ? errno : 255 );
if (write_fd(atoi(argv[1]), "", 1, fd) < 0)
exit( (errno > 0) ? errno : 255 );
exit(0);
}
write_fd.c
#include "unp.h"
int
main(int argc, char **argv)
{
int fd;
if (argc != 4)
err_quit("openfile
if ( (fd = open(argv[2], atoi(argv[3]))) < 0)
exit( (errno > 0) ? errno : 255 );
if (write_fd(atoi(argv[1]), "", 1, fd) < 0)
exit( (errno > 0) ? errno : 255 );
exit(0);
}
14.8 接收发送者的凭证