📄 18.htm
字号:
<p>daemon_init(); </p>
<p>loop(); /* never returns */ </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.2 main 函数 </p>
<p>当使用了 -d 选项后,所有对log_XXX函数的调用(见附录B)都送到标准错</p>
<p>误。 </p>
<p>否则它们就会被syslog记录下来。 </p>
<p>函数loop是服务器程序的主循环(程序18.3)。它使用了select
函数来复</p>
<p>用 </p>
<p>不同的描述符。 </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>#include <sys/time.h> </p>
<p>#include <errno.h> </p>
<p>static void cli_done(int); </p>
<p>static void child_done(int); </p>
<p>static fd_set allset; /* one bit per client conn, plus one</p>
<p>for listenfd </p>
<p>*/ </p>
<p>/* modified by loop() and cli_do </p>
<p>e() */ </p>
<p>void </p>
<p>loop(void) </p>
<p>{ </p>
<p>int i, n, maxfd, maxi, listenfd, nread; </p>
<p>char buf[MAXLINE]; </p>
<p>Client *cliptr; </p>
<p>uid_t uid; </p>
<p>fd_set rset; </p>
<p>if (signal_intr(SIGCHLD, sig_chld) == SIG_ERR) </p>
<p>log_sys("signal error"); </p>
<p>/* obtain descriptor to listen for client reques </p>
<p>s on */ </p>
<p>if ( (listenfd = serv_listen(CS_CALL)) < 0) </p>
<p>log_sys("serv_listen error"); </p>
<p>FD_ZERO(&allset); </p>
<p>FD_SET(listenfd, &allset); </p>
<p>maxfd = listenfd; </p>
<p>maxi = -1; </p>
<p>for ( ; ; ) { </p>
<p>if (chld_flag) </p>
<p>child_done(maxi); </p>
<p>rset = allset; /* rset gets modified each time around * </p>
<p>if ( (n = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0) </p>
<p> </p>
<p>if (errno == EINTR) { </p>
<p>/* caught SIGCHLD, find entry with child </p>
<p>one set */ </p>
<p>child_done(maxi); </p>
<p>continue; /* issue the select agai </p>
<p>*/ </p>
<p>} else </p>
<p>log_sys("select error"); </p>
<p>} </p>
<p>if (FD_ISSET(listenfd, &rset)) { </p>
<p>/* accept new client request */ </p>
<p>if ( (clifd = serv_accept(listenfd, &uid)) < 0) </p>
<p>log_sys("serv_accept error: %d", clifd); </p>
<p>i = client_add(clifd, uid); </p>
<p>FD_SET(clifd, &allset); </p>
<p>if (clifd > maxfd) </p>
<p>maxfd = clifd; /* max fd for select() */ </p>
<p>if (i > maxi) </p>
<p>maxi = i; /* max index in client[] </p>
<p>array */ </p>
<p>log_msg("new connection: uid %d, fd %d", uid, clifd); </p>
<p>continue; </p>
<p>} </p>
<p>/* Go through client[] array. </p>
<p>Read any client data that has arrived. */ </p>
<p>for (cliptr = &client[0]; cliptr <= &client[maxi]; cliptr++)</p>
<p>{ </p>
<p>if ( (clifd = cliptr->fd) < 0) </p>
<p>continue; </p>
<p>if (FD_ISSET(clifd, &rset)) { </p>
<p>/* read argument buffer from cli </p>
<p>nt */ </p>
<p>if ( (nread = read(clifd, buf, MAXLINE)) < 0) </p>
<p>log_sys("read error on fd %d", clifd); </p>
<p>else if (nread == 0) { </p>
<p>/* The client has terminated or closed t </p>
<p>e stream </p>
<p>pipe. Now we can release its device </p>
<p>ock. */ </p>
<p>log_msg("closed: uid %d, fd %d", </p>
<p>liptr->uid, clifd); </p>
<p>lock_rel(cliptr->pid); </p>
<p>cli_done(clifd); </p>
<p>continue; </p>
<p>} </p>
<p>/* Data has arrived from the client. Process th </p>
<p>client's request. */ </p>
<p>if (buf[nread-1] != 0) { </p>
<p>log_quit("request from uid %d not null t </p>
<p>rminated:" </p>
<p>" %*.*s", uid, nread, n </p>
<p>ead, buf); </p>
<p>cli_done(clifd); </p>
<p>continue; </p>
<p>} </p>
<p>log_msg("starting: %s, from uid %d", buf, uid); </p>
<p>/* Parse the arguments, set opti </p>
<p>ns. Since </p>
<p>we may need to try calling ag </p>
<p>in for this </p>
<p>client, save options in clien </p>
<p>[] array. */ </p>
<p>if (buf_args(buf, cli_args) < 0) </p>
<p>log_quit("command line error: %s", buf); </p>
<p>cliptr->Debug = Debug; </p>
<p>cliptr->parity = parity; </p>
<p>strcpy(cliptr->sysname, sysname); </p>
<p>strcpy(cliptr->speed, (speed == NULL) ? "" : spe </p>
<p>d); </p>
<p>cliptr->childdone = 0; </p>
<p>cliptr->sysftell = 0; </p>
<p>cliptr->foundone = 0; </p>
<p>if (request(cliptr) < 0) { </p>
<p>/* system not found, or unable t </p>
<p>connect */ </p>
<p>if (send_err(cliptr->fd, -1, errmsg) < 0 </p>
<p>log_sys("send_err error"); </p>
<p>cli_done(clifd); </p>
<p>continue; </p>
<p>} </p>
<p>/* At this point request() has forked a child th </p>
<p>t is </p>
<p>trying to dial the remote system. We'll find </p>
<p>out the child's status when it terminates. */ </p>
<p>} </p>
<p>} </p>
<p>} </p>
<p>} </p>
<p>/* Go through the client[] array looking for clients whose</p>
<p>dialing </p>
<p>children have terminated. This function is called by loop()</p>
<p>when </p>
<p>chld_flag (the flag set by the SIGCHLD handler) is nonzero.</p>
<p>*/ </p>
<p>static void </p>
<p>child_done(int maxi) </p>
<p>{ </p>
<p>Client *cliptr; </p>
<p>again: </p>
<p>chld_flag = 0; /* to check when done with loop for more</p>
<p>SIGCHLDs */ </p>
<p>for (cliptr = &client[0]; cliptr <= &client[maxi]; cliptr++)</p>
<p>{ </p>
<p>if ( (clifd = cliptr->fd) < 0) </p>
<p>continue; </p>
<p>if (cliptr->childdone) { </p>
<p>log_msg("child done: pid %d, status %d", </p>
<p>cliptr->pid, cliptr->chi </p>
<p>ddone-1); </p>
<p>/* If the child was successful (exit(0)), just clear </p>
<p>the flag. When the client terminates, we'll read </p>
<p>the EOF on the stream pipe above and release </p>
<p>the device lock. */ </p>
<p>if (cliptr->childdone == 1) { /* child did exit(0) */ </p>
<p>cliptr->childdone = 0; </p>
<p>continue; </p>
<p>} </p>
<p>/* Unsuccessful: child did exit(1). Release the device </p>
<p>lock and try again from where we left off. */ </p>
<p>cliptr->childdone = 0; </p>
<p>lock_rel(cliptr->pid); /* unlock the device entry */ </p>
<p>if (request(cliptr) < 0) { </p>
<p>/* still unable, time to give up </p>
<p>*/ </p>
<p>if (send_err(cliptr->fd, -1, errmsg) < 0) </p>
<p>log_sys("send_err error"); </p>
<p>cli_done(clifd); </p>
<p>continue; </p>
<p>} </p>
<p>/* request() has forked another child for this client */ </p>
<p>} </p>
<p>} </p>
<p>if (chld_flag) /* additional SIGCHLDs have been caught */ </p>
<p>goto again; /* need to check all childdone flags again */ </p>
<p>} </p>
<p>/* Clean up when we're done with a client. */ </p>
<p>static void </p>
<p>cli_done(int clifd) </p>
<p>{ </p>
<p>client_del(clifd); /* delete entry in client[] array */ </p>
<p>FD_CLR(clifd, &allset); /* turn off bit in select() set */ </p>
<p>close(clifd); /* close our end of stream pipe */ </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.3 loop.c文件 </p>
<p>loop函数初始化了client数组,建立了一个对SIGCHLD信号的处理器。我们</p>
<p>不 </p>
<p>调用signal,而调用了signal_intr ,这样当有信号返回时,每一个慢速</p>
<p>的系统调 </p>
<p>用都可以被中断。loop函数然后调用serv_listen(程序15.19和程序</p>
<p>15.22)。lo </p>
<p>op函数的其他部分是一个基于select函数的无限循环,它检查两种情况:
</p>
<p>1. 如果传来一个新的客户端连接请求,我们调用serv_accept(程序15.20</p>
<p>和15.2 </p>
<p>4)。函数client_add为这个新的客户端在client数组中增加一项。 </p>
<p>2. 浏览整个client数组,看是否有客户端终止或者新的客户端请求到达。
</p>
<p>当一个客户终止拨号时(不管是否自愿),它的客户端指定的通向服务器端的</p>
<p>流管道被
关闭,我们从管道上得到一个文件终止符。这时,我们就可以释放</p>
<p>这 </p>
<p>个客户端所拥有的全部加锁,并删除client数组中的项。 </p>
<p>当从客户端收到请求时,我们就调用request函数(函数buf_args见程序</p>
<p>15.17 </p>
<p>)。如果客户端调用的远程系统的名字有效,而且找到相对应的Devices项,</p>
<p>requ </p>
<p>est函数就会创建一个子进程,然后返回。 </p>
<p>在loop函数运行中,子进程终止这一外部事件随时都可能发生。如果在</p>
<p>selec </p>
<p>t函数中阻塞,则select函数就返回一个EINTR错误。因为在loop函数的其他</p>
<p>地方也 </p>
<p>可能出现子进程终止信号,所以在这个循环中每次调用select之前都检测标</p>
<p>志chl </p>
<p>d_flag,如果有,我们就调用child_done来处理子进程终止。 </p>
<p>loop函数浏览整个client数组,检查每一项的childdone标志。如果子进程</p>
<p>成 </p>
<p>功,就不需要做其他事情。但如果子进程以状态1终止时(exit(1)),我们</p>
<p>就调用 </p>
<p>request函数来尝试使用Systems文件中的下一项。 </p>
<p>程序18.4 是cli_args,这个函数在客户端请求到达时被loop函数中的</p>
<p>buf_ar </p>
<p>gs所调用。它处理客户端送来的命令行参数。注意这个函数根据命令行参数</p>
<p>设置全 </p>
<p>局变量,然后loop函数将这些全局变量复制到client数组的相应的项中,这</p>
<p>些选项 </p>
<p>只影响一个客户端请求。 </p>
<p>程序18.5是client.c,它定义了处理client数组的函数组。程序18.5和程</p>
<p>序15.27 </p>
<p>的区别在于在这里我们一定要根据进程的ID(见函数client_sigchld)来查</p>
<p>询所需 </p>
<p>要的项。 </p>
<p>程序18.6是文件lock.c。其中的函数管理父进程中的lock数组。同上面的</p>
<p>cli </p>
<p>ent数组一样,我们调用realloc来给lock数组动态分配空间,以避免编译时</p>
<p>的限制 </p>
<p>。 </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>/* This function is called by buf_args(), which is called by</p>
<p>loop(). </p>
<p>* buf_args() has broken up the client's buffer into an</p>
<p>argv[] style </p>
<p>* array, which is now processed. */ </p>
<p>int </p>
<p>cli_args(int argc, char **argv) </p>
<p>{ </p>
<p>int c; </p>
<p>if (argc < 2 || strcmp(argv[0], CL_CALL) != 0) { </p>
<p>strcpy(errmsg, "usage: call <sysname> <options>"); </p>
<p>return(-1); </p>
<p>} </p>
<p>Debug = 0; /* option defaults */ </p>
<p>parity = NONE; </p>
<p>speed = NULL; </p>
<p>opterr = 0; /* don't want getopt() writing to stderr */ </p>
<p>optind = 1; /* since we call getopt() multiple times */ </p>
<p>while ( (c = getopt(argc, argv, "des:o")) != EOF) { </p>
<p>switch (c) { </p>
<p>case 'd': </p>
<p>Debug = 1; /* client wants DEBUG() output */ </p>
<p>break; </p>
<p>case 'e': /* even parity */ </p>
<p>parity = EVEN; </p>
<p>break; </p>
<p>case 'o': /* odd parity */ </p>
<p>parity = ODD; </p>
<p>break; </p>
<p>case 's': /* speed */ </p>
<p>speed = optarg; </p>
<p>break; </p>
<p>case '?': </p>
<p>sprintf(errmsg, "unrecognized option: -%c\n", optopt); </p>
<p>return(-1); </p>
<p>} </p>
<p>} </p>
<p>if (optind < argc) </p>
<p>sysname = argv[optind]; /* name of host to call */ </p>
<p>else { </p>
<p>sprintf(errmsg, "missing <hostname> to call\n"); </p>
<p>return(-1); </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -