📄 00000015.htm
字号:
close(fd[0]); <BR> if (fd[1] != STDIN_FILENO) { <BR> if (dup2(fd[1], STDIN_FILENO) != STDIN_FILENO) <BR> err_sys("dup2 error to stdin"); <BR> } <BR> if (fd[1] != STDOUT_FILENO) { <BR> if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) <BR> err_sys("dup2 error to stdout"); <BR> } <BR> if (execl("./opend", "opend", NULL) < 0) <BR> err_sys("execl error"); <BR> } <BR> close(fd[1]); /* parent */ <BR> } <BR> sprintf(buf, " %d", oflag); /* oflag to ascii */ <BR> iov[0].iov_base = CL_OPEN " "; <BR> iov[0].iov_len = strlen(CL_OPEN) + 1; <BR> iov[1].iov_base = name; <BR> iov[1].iov_len = strlen(name); <BR> iov[2].iov_base = buf; <BR> iov[2].iov_len = strlen(buf) + 1; /* +1 for null at end of buf */ <BR> len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; <BR> if (writev(fd[0], &iov[0], 3) != len) <BR> err_sys("writev error"); <BR> /* read descriptor, returned errors handled by write() */ <BR> return( recv_fd(fd[0], write) ); <BR>} <BR> 程序15.13 csopen函数 <BR>子进程关闭管道的一端,父进程关闭另一端。子进程也为它所执行的服务器将管道 <BR>它使用的一端复制到其标准输入和标准输出0(另一种可选择的方案是将描述符fd <BR>[1]的ASCII 表示形式作为一个参数传送给服务器。) <BR>父进程将请求发送给服务器,请求中包含路径名和打开方式。最后,父进程调用r <BR>ecv-fd以返回描述符或错误消息。如若服务器返回一错误消息则调用write,向标 <BR>准出错输出该消息。 <BR>现在,观察open服务器。其程序是opend,它由子进程执行(见程序15.13)。先观 <BR>察opend.h头文件(程序15.14),它包括了系统头文件,并且说明了全局变量和函 <BR>数原型。 <BR>#include <sys/types.h> <BR>#include <errno.h> <BR>#include "ourhdr.h" <BR>#define CL_OPEN "open" /* client's request for server */ <BR> /* declare global variables */ <BR>extern char errmsg[]; /* error message string to return to client */ <BR>extern int oflag; /* open() flag: O_xxx ... */ <BR>extern char *pathname; /* of file to open() for client */ <BR> /* function prototypes */ <BR>int cli_args(int, char **); <BR>void request(char *, int, int); <BR>程序.15.14 opend.h头文件 <BR>#include "opend.h" <BR> /* define global variables */ <BR>char errmsg[MAXLINE]; <BR>int oflag; <BR>char *pathname; <BR>int <BR>main(void) <BR>{ <BR> int nread; <BR> char buf[MAXLINE]; <BR> for ( ; ; ) { /* read arg buffer from client, process request */ <BR> if ( (nread = read(STDIN_FILENO, buf, MAXLINE)) < 0) <BR> err_sys("read error on stream pipe"); <BR> else if (nread == 0) <BR> break; /* client has closed the stream pipe */ <BR> request(buf, nread, STDIN_FILENO); <BR> } <BR> exit(0); <BR>} <BR>程序15.15 main函数 <BR>#include "opend.h" <BR>#include <fcntl.h> <BR>void <BR>request(char *buf, int nread, int fd) <BR>{ <BR> int newfd; <BR> if (buf[nread-1] != 0) { <BR> sprintf(errmsg, "request not null terminated: %*.*s\n", <BR> nread, nread, buf); <BR> send_err(STDOUT_FILENO, -1, errmsg); <BR> return; <BR> } <BR> /* parse the arguments, set options */ <BR> if (buf_args(buf, cli_args) < 0) { <BR> send_err(STDOUT_FILENO, -1, errmsg); <BR> return; <BR> } <BR> if ( (newfd = open(pathname, oflag)) < 0) { <BR> sprintf(errmsg, "can't open %s: %s\n", <BR> pathname, strerror(errno)); <BR> send_err(STDOUT_FILENO, -1, errmsg); <BR> return; <BR> } <BR> /* send the descriptor */ <BR> if (send_fd(STDOUT_FILENO, newfd) < 0) <BR> err_sys("send_fd error"); <BR> close(newfd); /* we're done with descriptor */ <BR>} <BR>程序15.16 request函数 <BR> 主函数(程序15.15)经流管道(它的标准输入)读来自客户的请求,然后调 <BR>用函数request。 <BR>程序15.16中的request 函数承担全部工作。它调用函数buf_args将客户请求分解 <BR>成标准argv型的参数表,然后调用函数cli_args以处理客户的参数。如若一切正常 <BR>,则调用open 以打开相应文件,接着调用send_fd,经由流管道(它的标准输出) <BR>将描述符回送给客户。如若出错则调用send_err回送一则出错消息,其中使用我们 <BR>在前面说明了的客户/服务器协议。 <BR>#include "ourhdr.h" <BR>#define MAXARGC 50 /* max number of arguments in buf */ <BR>#define WHITE " \t\n" /* white space for tokenizing arguments */ <BR>/* buf[] contains white-space separated arguments. We convert it <BR> * to an argv[] style array of pointers, and call the user's <BR> * function (*optfunc)() to process the argv[] array. <BR> * We return -1 to the caller if there's a problem parsing buf, <BR> * else we return whatever optfunc() returns. Note that user's <BR> * buf[] array is modified (nulls placed after each token). */ <BR>int <BR>buf_args(char *buf, int (*optfunc)(int, char **)) <BR>{ <BR> char *ptr, *argv[MAXARGC]; <BR> int argc; <BR> if (strtok(buf, WHITE) == NULL) /* an argv[0] is required */ <BR> return(-1); <BR> argv[argc = 0] = buf; <BR> while ( (ptr = strtok(NULL, WHITE)) != NULL) { <BR> if (++argc >= MAXARGC-1) /* -1 for room for NULL at end */ <BR> return(-1); <BR> argv[argc] = ptr; <BR> } <BR> argv[++argc] = NULL; <BR> return( (*optfunc)(argc, argv) ); <BR> /* Since argv[] pointers point into the user's buf[], <BR> user's function can just copy the pointers, even <BR> though argv[] array will disappear on return. */ <BR>} <BR>程序15.17 buf_args函数 <BR> buf_args调用的服务器函数是cli_args(程序15.18)。它验证客户发送的参 <BR>数数是否正确,然后将路径名和打开方式存放在全局变量中。 <BR> 这样也就完成了open服务器,它由客户执行fork和exec而调用。在fork之前创 <BR>建了一个流管道,然后客户和服务器用其进行通信。在这种安排下,每个客户都有 <BR>一服务器。 <BR>在下一节观察了客户一服务器连接后,我们将在15.6节重新实现一个open服务器, <BR>其中用一个精灵进程作为服务器,所有客户都与其进行联系。 <BR>#include "opend.h" <BR>/* This function is called by buf_args(), which is called by <BR> * request(). buf_args() has broken up the client's buffer <BR> * into an argv[] style array, which we now process. */ <BR>int <BR>cli_args(int argc, char **argv) <BR>{ <BR> if (argc != 3 || strcmp(argv[0], CL_OPEN) != 0) { <BR> strcpy(errmsg, "usage: <pathname> <oflag>\n"); <BR> return(-1); <BR> } <BR> pathname = argv[1]; /* save ptr to pathname to open */ <BR> oflag = atoi(argv[2]); <BR> return(0); <BR>} <BR>程序15.18 cli_args函数 <BR>15.5 客户-服务器连接函数 <BR> 对于相关进程(例如,父进程和子进程)之间的IPC,流管道是非常有用的。 <BR>前节所述的open服务器使用末命名的流管道能从子进程向父进程传送文件描述符。 <BR>但是当我们处理无关进程时(例如,若服务器是一精灵进程),则需要使用有名的 <BR>流管道。 <BR> 我们可以先构造一末名流管道(用s_pipe 函数),然后对每一端加上一
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -