📄 15.htm
字号:
<p>static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */ </p>
<p>#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int)) </p>
<p>/* size of control buffer to send/recv one file descriptor */ </p>
<p>/* Receive a file descriptor from another process (a server). </p>
<p>* In addition, any data received from the server is passed </p>
<p>* to (*userfunc)(STDERR_FILENO, buf, nbytes). We have a </p>
<p>* 2-byte protocol for receiving the fd from send_fd(). */ </p>
<p>int </p>
<p>recv_fd(int servfd, ssize_t (*userfunc)(int, const void *, size_t)) </p>
<p>{ </p>
<p>int newfd, nread, status; </p>
<p>char *ptr, buf[MAXLINE]; </p>
<p>struct iovec iov[1]; </p>
<p>struct msghdr msg; </p>
<p>status = -1; </p>
<p>for ( ; ; ) { </p>
<p>iov[0].iov_base = buf; </p>
<p>iov[0].iov_len = sizeof(buf); </p>
<p>msg.msg_iov = iov; </p>
<p>msg.msg_iovlen = 1; </p>
<p>msg.msg_name = NULL; </p>
<p>msg.msg_namelen = 0; </p>
<p>if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL) </p>
<p>return(-1); </p>
<p>msg.msg_control = (caddr_t) cmptr; </p>
<p>msg.msg_controllen = CONTROLLEN; </p>
<p>if ( (nread = recvmsg(servfd, &msg, 0)) < 0) </p>
<p>err_sys("recvmsg error"); </p>
<p>else if (nread == 0) { </p>
<p>err_ret("connection closed by server"); </p>
<p>return(-1); </p>
<p>} </p>
<p>/* See if this is the final data with null & status. </p>
<p>Null must be next to last byte of buffer, status </p>
<p>byte is last byte. Zero status means there must </p>
<p>be a file descriptor to receive. */ </p>
<p>for (ptr = buf; ptr < &buf[nread]; ) { </p>
<p>if (*ptr++ == 0) { </p>
<p>if (ptr != &buf[nread-1]) </p>
<p>err_dump("message format error"); </p>
<p>status = *ptr & 255; </p>
<p>if (status == 0) { </p>
<p>if (msg.msg_controllen != CONTROLLEN) </p>
<p>err_dump("status = 0 but no fd"); </p>
<p>newfd = *(int *)CMSG_DATA(cmptr); /* new descriptor */ </p>
<p>} else </p>
<p>newfd = -status; </p>
<p>nread -= 2; </p>
<p>} </p>
<p>} </p>
<p>if (nread > 0) </p>
<p>if ((*userfunc)(STDERR_FILENO, buf, nread) != nread) </p>
<p>return(-1); </p>
<p>if (status >= 0) /* final data has arrived */ </p>
<p>return(newfd); /* descriptor, or -status */ </p>
<p>} </p>
<p>} </p>
<p>程序15.10 4.3BSD之下的recv_fd函数 </p>
<p>15.4 Open服务器,版本1 </p>
<p>使用文件描述符传送技术,现在我们开发一个open服务器:它是一个可执行程
</p>
<p>序, </p>
<p>由一个进程执行以打开一个或多个文件。该服务器不是将这种文件送回调用进程,
</p>
<p>而是送回一个打开文件描述符。这使该服务器对任何类型的文件(例如调制解调器
</p>
<p>线,或一网络连接)而不单是普通文件都能起作用。这也意味着,用IPC交换最小
</p>
<p>量的信息--从客户到服务器传送文件名和打开方式,而从服务器到客户返回描述符
</p>
<p>。文件内容则不需用IPC传送。 </p>
<p>将服务器设计成一个单独的可执行程序有很多优点: </p>
<p>1.
任一客户都易于和服务器联系,这类似于客户调用一库函数。我们不需要将一
</p>
<p>特定服务编码在应用程序中,而是设计一种可供重用的设施。 </p>
<p>2.
如若需要更改服务器,那么也只影响一道程序。相反,更新一库函数可能要更
</p>
<p>改调用此库函数的所有程序(用连编程序重新连接)。共享库函数可以简化这种更
</p>
<p>新。 </p>
<p>3. 服务器可以是设置__用户__ID程序,于是使其具有客户没有的附加许可权。注
</p>
<p>意,一个库函数(或共享库函数)不能提供这种能力。 </p>
<p>客户进程创建一流管道,然后调用fork和exec以调用服务器。客户经流管道发
</p>
<p>送请求,服务器经管道回送响应。我们定义客户和服务器间的协议如下。
</p>
<p>1. 客户经流管道向服务器发送下列形式的请求: </p>
<p>open <pathname> <openmode>\o </p>
<p><openmode>是open函数的第二个参数,以十进制表示。该请求字符串以null字节结
</p>
<p>尾。 </p>
<p>2. 服务器调用send_fd 或send_err回送一打开描述符或一条出错消息。这是一个
</p>
<p>进程向其父进程发送一打开描述符的实例。在15.6节,我们将修改此实例,其中使
</p>
<p>用了一个精灵服务器,它将一个描述符发送给完全无关的进程。 </p>
<p>程序15.11是头文件open.h,它包括标准系统头文件,并且定义了各个函数原
</p>
<p>型。 </p>
<p>#include <sys/types.h> </p>
<p>#include <errno.h> </p>
<p>。文件内容则不需用IPC传送。 </p>
<p>将服务器设计成一个单独的可执行程序有很多优点: </p>
<p>1.
任一客户都易于和服务器联系,这类似于客户调用一库函数。我们不需要将一
</p>
<p>特定服务编码在应用程序中,而是设计一种可供重用的设施。 </p>
<p>2.
如若需要更改服务器,那么也只影响一道程序。相反,更新一库函数可能要更
</p>
<p>改调用此库函数的所有程序(用连编程序重新连接)。共享库函数可以简化这种更
</p>
<p>新。 </p>
<p>3. 服务器可以是设置__用户__ID程序,于是使其具有客户没有的附加许可权。注
</p>
<p>意,一个库函数(或共享库函数)不能提供这种能力。 </p>
<p>客户进程创建一流管道,然后调用fork和exec以调用服务器。客户经流管道发
</p>
<p>送请求,服务器经管道回送响应。我们定义客户和服务器间的协议如下。
</p>
<p>1. 客户经流管道向服务器发送下列形式的请求: </p>
<p>open <pathname> <openmode>\o </p>
<p><openmode>是open函数的第二个参数,以十进制表示。该请求字符串以null字节结
</p>
<p>尾。 </p>
<p>2. 服务器调用send_fd 或send_err回送一打开描述符或一条出错消息。这是一个
</p>
<p>进程向其父进程发送一打开描述符的实例。在15.6节,我们将修改此实例,其中使
</p>
<p>用了一个精灵服务器,它将一个描述符发送给完全无关的进程。 </p>
<p>程序15.11是头文件open.h,它包括标准系统头文件,并且定义了各个函数原
</p>
<p>型。 </p>
<p>#include <sys/types.h> </p>
<p>#include <errno.h> </p>
<p>#include "ourhdr.h" </p>
<p>#define CL_OPEN "open" /* client's request for server */ </p>
<p>/* our function prototypes */ </p>
<p>int csopen(char *, int); </p>
<p>程序15.11 open.h头文件 </p>
<p>程序15.12是main函数,其中包含一个循环,它先从标准输入读一个路径名,然后
</p>
<p>将该文件复制至标准输出。它调用函数csopen以与open
服务器联系,从其返回一 </p>
<p>打开描述符。 </p>
<p>#include "open.h" </p>
<p>#include <fcntl.h> </p>
<p>#define BUFFSIZE 8192 </p>
<p>int </p>
<p>main(int argc, char *argv[]) </p>
<p>{ </p>
<p>int n, fd; </p>
<p>char buf[BUFFSIZE], line[MAXLINE]; </p>
<p>/* read filename to cat from stdin */ </p>
<p>while (fgets(line, MAXLINE, stdin) != NULL) { </p>
<p>line[strlen(line) - 1] = 0; /* replace newline with null */ </p>
<p>/* open the file */ </p>
<p>if ( (fd = csopen(line, O_RDONLY)) < 0) </p>
<p>continue; /* csopen() prints error from server */ </p>
<p>/* and cat to stdout */ </p>
<p>while ( (n = read(fd, buf, BUFFSIZE)) > 0) </p>
<p>if (write(STDOUT_FILENO, buf, n) != n) </p>
<p>err_sys("write error"); </p>
<p>if (n < 0) </p>
<p>err_sys("read error"); </p>
<p>close(fd); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序15.12 main 函数 </p>
<p>程序15.13是函数csopen,它先创建一流管道,然后进行服务器的fork和exec操作
</p>
<p>。 </p>
<p>#include "open.h" </p>
<p>#include <sys/uio.h> /* struct iovec */ </p>
<p>/* Open the file by sending the "name" and "oflag" to the </p>
<p>* connection server and reading a file descriptor back. */ </p>
<p>int </p>
<p>csopen(char *name, int oflag) </p>
<p>{ </p>
<p>pid_t pid; </p>
<p>int len; </p>
<p>char buf[10]; </p>
<p>struct iovec iov[3]; </p>
<p>static int fd[2] = { -1, -1 }; </p>
<p>if (fd[0] < 0) { /* fork/exec our open server first time */ </p>
<p>if (s_pipe(fd) < 0) </p>
<p>err_sys("s_pipe error"); </p>
<p>if ( (pid = fork()) < 0) </p>
<p>err_sys("fork error"); </p>
<p>else if (pid == 0) { /* child */ </p>
<p>close(fd[0]); </p>
<p>if (fd[1] != STDIN_FILENO) { </p>
<p>if (dup2(fd[1], STDIN_FILENO) != STDIN_FILENO) </p>
<p>err_sys("dup2 error to stdin"); </p>
<p>} </p>
<p>if (fd[1] != STDOUT_FILENO) { </p>
<p>if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) </p>
<p>err_sys("dup2 error to stdout"); </p>
<p>} </p>
<p>if (execl("./opend", "opend", NULL) < 0) </p>
<p>err_sys("execl error"); </p>
<p>} </p>
<p>close(fd[1]); /* parent */ </p>
<p>} </p>
<p>sprintf(buf, " %d", oflag); /* oflag to ascii */ </p>
<p>iov[0].iov_base = CL_OPEN " "; </p>
<p>iov[0].iov_len = strlen(CL_OPEN) + 1; </p>
<p>iov[1].iov_base = name; </p>
<p>iov[1].iov_len = strlen(name); </p>
<p>iov[2].iov_base = buf; </p>
<p>iov[2].iov_len = strlen(buf) + 1; /* +1 for null at end of buf */ </p>
<p>len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; </p>
<p>if (writev(fd[0], &iov[0], 3) != len) </p>
<p>err_sys("writev error"); </p>
<p>/* read descriptor, returned errors handled by write() */ </p>
<p>return( recv_fd(fd[0], write) ); </p>
<p>} </p>
<p>程序15.13 csopen函数 </p>
<p>子进程关闭管道的一端,父进程关闭另一端。子进程也为它所执行的服务器将管道
</p>
<p>它使用的一端复制到其标准输入和标准输出0(另一种可选择的方案是将描述符fd
</p>
<p>[1]的ASCII 表示形式作为一个参数传送给服务器。) </p>
<p>父进程将请求发送给服务器,请求中包含路径名和打开方式。最后,父进程调用r
</p>
<p>ecv-fd以返回描述符或错误消息。如若服务器返回一错误消息则调用write,向标
</p>
<p>准出错输出该消息。 </p>
<p>现在,观察open服务器。其程序是opend,它由子进程执行(见程序15.13)。先观
</p>
<p>察opend.h头文件(程序15.14),它包括了系统头文件,并且说明了全局变量和函
</p>
<p>数原型。 </p>
<p>#include <sys/types.h> </p>
<p>#include <errno.h> </p>
<p>#include "ourhdr.h" </p>
<p>#define CL_OPEN "open" /* client's request for server */ </p>
<p>/* declare global variables */ </p>
<p>extern char errmsg[]; /* error message string to return to client */ </p>
<p>extern int oflag; /* open() flag: O_xxx ... */ </p>
<p>extern char *pathname; /* of file to open() for client */ </p>
<p>/* function prototypes */ </p>
<p>int cli_args(int, char **); </p>
<p>void request(char *, int, int); </p>
<p>程序.15.14 opend.h头文件 </p>
<p>#include "opend.h" </p>
<p>/* define global variables */ </p>
<p>char errmsg[MAXLINE]; </p>
<p>int oflag; </p>
<p>char *pathname; </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int nread; </p>
<p>char buf[MAXLINE]; </p>
<p>for ( ; ; ) { /* read arg buffer from client, process request */ </p>
<p>if ( (nread = read(STDIN_FILENO, buf, MAXLINE)) < 0) </p>
<p>err_sys("read error on stream pipe"); </p>
<p>else if (nread == 0) </p>
<p>break; /* client has closed the stream pipe */ </p>
<p>request(buf, nread, STDIN_FILENO); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序15.15 main函数 </p>
<p>#include "opend.h" </p>
<p>#include <fcntl.h> </p>
<p>void </p>
<p>request(char *buf, int nread, int fd) </p>
<p>{ </p>
<p>int newfd; </p>
<p>if (buf[nread-1] != 0) { </p>
<p>sprintf(errmsg, "request not null terminated: %*.*s\n", </p>
<p>nread, nread, buf); </p>
<p>send_err(STDOUT_FILENO, -1, errmsg); </p>
<p>return; </p>
<p>} </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -