📄 18.htm
字号:
<p>{ </p>
<p>if (fpdev != NULL) </p>
<p>rewind(fpdev); </p>
<p>devlineno = 0; </p>
<p>} </p>
<p>/* Find a match of type and class */ </p>
<p>int </p>
<p>dev_find(Devices *devptr, const Systems *sysptr) </p>
<p>{ </p>
<p>dev_rew(); </p>
<p>while (dev_next(devptr) >= 0) { </p>
<p>if (strcmp(sysptr->type, devptr->type) == 0 && </p>
<p>strcmp(sysptr->class, devptr->class) == 0) </p>
<p>return(0); /* found a device match */ </p>
<p>} </p>
<p>sprintf(errmsg, "device `%s'/`%s' not found\n", </p>
<p>sysptr->type, sysptr->class); </p>
<p>return(-1); </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.8 读取Devices文件的函数 </p>
<p>我们看到,request函数调用dev_find函数来确定type域、class域与</p>
<p>Systems </p>
<p>文件中对应域相同的项。 </p>
<p>程序18.9 是读取Dialers文件的函数。 </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>static FILE *fpdial = NULL; </p>
<p>static int diallineno; /* for error messages */ </p>
<p>static char dialline[MAXLINE]; </p>
<p>/* can't be automatic; dial_next() returns pointers into</p>
<p>here */ </p>
<p>/* Read and break apart a line in the Dialers file. */ </p>
<p>int </p>
<p>dial_next(Dialers *dialptr) /* pointers in structure are</p>
<p>filled in */ </p>
<p>{ </p>
<p>if (fpdial == NULL) { </p>
<p>if ( (fpdial = fopen(DIALERS, "r")) == NULL) </p>
<p>log_sys("can't open %s", DIALERS); </p>
<p>diallineno = 0; </p>
<p>} </p>
<p>again: </p>
<p>if (fgets(dialline, MAXLINE, fpdial) == NULL) </p>
<p>return(-1); /* EOF */ </p>
<p>diallineno++; </p>
<p>if ( (dialptr->dialer = strtok(dialline, WHITE)) == NULL) { </p>
<p>if (dialline[0] == '\n') </p>
<p>goto again; /* ignore empty line */ </p>
<p>log_quit("missing `dialer' in Dialers file, line %d",</p>
<p>diallineno); </p>
<p>} </p>
<p>if (dialptr->dialer[0] == '#') </p>
<p>goto again; /* ignore comment line */ </p>
<p>if ( (dialptr->sub = strtok(NULL, WHITE)) == NULL) </p>
<p>log_quit("missing `sub' in Dialers file, line %d",</p>
<p>diallineno); </p>
<p>if ( (dialptr->expsend = strtok(NULL, "\n")) == NULL) </p>
<p>log_quit("missing `expsend' in Dialers file, line %d",</p>
<p>diallineno); </p>
<p>return(0); </p>
<p>} </p>
<p>void </p>
<p>dial_rew(void) </p>
<p>{ </p>
<p>if (fpdial != NULL) </p>
<p>rewind(fpdial); </p>
<p>diallineno = 0; </p>
<p>} </p>
<p>/* Find a dialer match */ </p>
<p>int </p>
<p>dial_find(Dialers *dialptr, const Devices *devptr) </p>
<p>{ </p>
<p>dial_rew(); </p>
<p>while (dial_next(dialptr) >= 0) { </p>
<p>if (strcmp(dialptr->dialer, devptr->dialer) == 0) </p>
<p>return(0); /* found a dialer match */ </p>
<p>} </p>
<p>sprintf(errmsg, "dialer `%s' not found\n", dialptr->dialer);</p>
<p>return(-1); </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.9 读取Dialers文件的函数 </p>
<p>可以看到函数child_dial调用dial_find函数来查找dialer域与一个特定</p>
<p>设备 </p>
<p>匹配的项。 </p>
<p>从图18.6可以发现Systems和Devices文件由父进程处理,而子进程处理</p>
<p>Dialers文 </p>
<p>件。这也是整个设计的目的-父进程发现一个匹配的未被加锁的设备,然后创</p>
<p>建一 </p>
<p>个子进程进行实际的拨号工作。 </p>
<p>现在看一下程序18.10中的request函数。loop函数调用此函数,来确定对应</p>
<p>一 </p>
<p>个特定的远程主机的而且未被加锁的拨号设备。为了达到这个目的,loop函</p>
<p>数查找 </p>
<p>Systems和Devices文件。如果发现匹配项,它就创建一个子进程。除了远程</p>
<p>主机名 </p>
<p>字,我们还允许客户端指定拨号速度。例如,对于图18.2中的Systems文</p>
<p>件,客户 </p>
<p>端的拨号请求可能是这样的: </p>
<p>call -s 9600 host1 </p>
<p>这样,我们就忽略了图18.2中速率不是9600的host1项。 </p>
<p>需要注意的是,除非知道了子进程的ID,否则我们不能使用lock_set函数来</p>
<p>记录设 </p>
<p>备锁,但是在创建子进程之前又必须检查设备是否被加锁。因为我们总是要先</p>
<p>加锁 </p>
<p>,然后才启动子进程拨号,因此使用TELL_WAIT函数(程序18.17)来同步父</p>
<p>进程和 </p>
<p>子进程。同样需要注意:虽然检查函数is_locked和实际的加锁函数</p>
<p>set_lock是两 </p>
<p>个分立的操作(也就是说,不是一个原子操作),但是这并没有竞争的问题,</p>
<p>这是 </p>
<p>因为request函数只能被一个服务器端精灵进程所调用-它不能被多个进程调</p>
<p>用, </p>
<p>如果request返回0,就创建一个子进程来开始拨号,否则当request返回1</p>
<p>时, </p>
<p>说明或者远程系统的名称无效,或者所有可能的设备都已经被加锁。
</p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>int /* return 0 if OK, -1 on error */ </p>
<p>request(Client *cliptr) </p>
<p>{ </p>
<p>pid_t pid; </p>
<p>errmsg[0] = 0; </p>
<p>/* position where this client left off last (or rewind) */ </p>
<p>sys_posn(cliptr->sysftell); </p>
<p>while ( (cliptr->sysftell = sys_next(&systems)) >= 0) { </p>
<p>if (strcmp(cliptr->sysname, systems.name) == 0) { </p>
<p>/* system match */ </p>
<p>/* if client specified a speed, it must match too */ </p>
<p>if (cliptr->speed[0] != 0 && </p>
<p>strcmp(cliptr->speed, systems.class) != 0) </p>
<p>continue; /* speeds don't match */ </p>
<p>DEBUG("trying sys: %s, %s, %s, %s", systems.name, </p>
<p>systems.type, systems.class, systems.phone); </p>
<p>cliptr->foundone++; </p>
<p>if (dev_find(&devices, &systems) < 0) </p>
<p>break; </p>
<p>DEBUG("trying dev: %s, %s, %s, %s", devices.type, </p>
<p>devices.line, devices.class, devices.dialer); </p>
<p>if ( (pid = is_locked(devices.line)) != 0) { </p>
<p>sprintf(errmsg, "device `%s' already locked by pid %d\n", </p>
<p>devices.line, pid); </p>
<p>continue; /* look for another entry in Systems file */ </p>
<p>} </p>
<p>/* We've found a device that's not locked. </p>
<p>fork() a child to to the actual dialing. */ </p>
<p>TELL_WAIT(); </p>
<p>if ( (cliptr->pid = fork()) < 0) </p>
<p>log_sys("fork error"); </p>
<p>else if (cliptr->pid == 0) { /* child */ </p>
<p>WAIT_PARENT(); /* let parent set lock */ </p>
<p>child_dial(cliptr); /* never returns */ </p>
<p>} </p>
<p>/* parent */ </p>
<p>lock_set(devices.line, cliptr->pid); </p>
<p>/* let child resume, now that lock is set */ </p>
<p>TELL_CHILD(cliptr->pid); </p>
<p>return(0); /* we've started a child */ </p>
<p>} </p>
<p>} </p>
<p>/* reached EOF on Systems file */ </p>
<p>if (cliptr->foundone == 0) </p>
<p>sprintf(errmsg, "system `%s' not found\n", cliptr->sysname);</p>
<p>else if (errmsg[0] == 0) </p>
<p>sprintf(errmsg, "unable to connect to system `%s'\n", </p>
<p>cliptr->sysname); </p>
<p>return(-1); /* also, cliptr->sysftell is -1 */ </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.10 request函数 </p>
<p>父进程专用函数的最后一个是sig_chld,它是SIGCHLD信号的处理程序。这</p>
<p>个 </p>
<p>函数在程序18.11中。 </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>#include <sys/wait.h> </p>
<p>/* SIGCHLD handler, invoked when a child terminates. */ </p>
<p>void </p>
<p>sig_chld(int signo) </p>
<p>{ </p>
<p>int stat, errno_save; </p>
<p>pid_t pid; </p>
<p>errno_save = errno; /* log_msg() might change errno */ </p>
<p>chld_flag = 1; </p>
<p>if ( (pid = waitpid(-1, &stat, 0)) <= 0) </p>
<p>log_sys("waitpid error"); </p>
<p>if (WIFEXITED(stat) != 0) </p>
<p>/* set client's childdone status for loop() */ </p>
<p>client_sigchld(pid, WEXITSTATUS(stat)+1); </p>
<p>else </p>
<p>log_msg("child %d terminated abnormally: %04x", pid, stat); </p>
<p>errno = errno_save; </p>
<p>return; /* probably interrupts accept() in serv_accept() */ </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.11 sig_chld信号处理程序 </p>
<p>当一个子进程终止,我们必须在client数组的适当位置上记录下它的终止状</p>
<p>态 </p>
<p>和进程ID。我们调用函数client_sigchld(程序18.5)来完成这个工作。 </p>
<p>需要注意,我们这里违反了先前在第10章中的原则-一个信号处理程序只处理</p>
<p>一个全局变量,而不做其他。这里我们调用waitpid和函数client_sigchld</p>
<p>(程序 </p>
<p>18.5),后面的这个函数对信号而言是安全的,它所做的只是在client数组</p>
<p>的对应 </p>
<p>项中做记录。它并不创建或删除项(那样的话将是不可重入的),它也不调用</p>
<p>任何 </p>
<p>系统函数。 </p>
<p>POSIX.1定义的waitpid对信号而言是安全的(见图10.3)。如果我们不从</p>
<p>信号 </p>
<p>处理程序中调用waitpid,那么当chld_flag标志非0时父进程必须调用</p>
<p>waitpid。但 </p>
<p>是因为在主循环查询chld_flag之前子进程有可能终止,我们就要或者在每</p>
<p>个子进 </p>
<p>程终止时对chld_flag加1(这样主循环就知道要调用多少次waitpid),或</p>
<p>者在循 </p>
<p>环内使用WNOHANG标志,并调用waitpid(图8.3)。最简单的方法还是从信</p>
<p>号量处 </p>
<p>理程序中调用waitpid,在client数组中记录下信息。 </p>
<p>现在讨论客户端在拨号远程系统时需调用的一些函数。在request调用</p>
<p>child_ </p>
<p>dial来创建子进程(程序18.12)的时候,这些函数会被使用。 </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>#include "calld.h" </p>
<p>/* The child does the actual dialing and sends the fd back</p>
<p>to </p>
<p>* the client. This function can't return to caller, must</p>
<p>exit. </p>
<p>* If successful, exit(0), else exit(1). </p>
<p>* The child uses the following global variables, which are</p>
<p>just </p>
<p>* in the copy of the data space from the parent: </p>
<p>* cliptr->fd (to send DEBUG() output and fd back to client),</p>
<p>* cliptr->Debug (for all DEBUG() output), childptr->parity, </p>
<p>* systems, devices, dialers. */ </p>
<p>void </p>
<p>child_dial(Client *cliptr) </p>
<p>{ </p>
<p>int fd, n; </p>
<p>Debug = cliptr->Debug; </p>
<p>DEBUG("child, pid %d", getpid()); </p>
<p>if (strcmp(devices.dialer, "direct") == 0) { /* direct tty</p>
<p>line */ </p>
<p>fd = tty_open(systems.class, devices.line, cliptr->parity,</p>
<p>0); </p>
<p>if (fd < 0) </p>
<p>goto die; </p>
<p>} else { /* else assume dialing is needed */ </p>
<p>if (dial_find(&dialers, &devices) < 0) </p>
<p>goto die; </p>
<p>fd = tty_open(systems.class, devices.line, cliptr->parity,</p>
<p>1); </p>
<p>if (fd < 0) </p>
<p>goto die; </p>
<p>if (tty_dial(fd, systems.phone, dialers.dialer, </p>
<p>dialers.sub, dialers.expsend) < 0) </p>
<p>goto die; </p>
<p>} </p>
<p>DEBUG("done"); </p>
<p>/* send the open descriptor to client */ </p>
<p>if (send_fd(cliptr->fd, fd) < 0) </p>
<p>log_sys("send_fd error"); </p>
<p>exit(0); /* parent will see this */ </p>
<p>die: </p>
<p>/* The child can't call send_err() as that would send the</p>
<p>final </p>
<p>2-byte protocol to the client. We just send our error</p>
<p>message </p>
<p>back to the client. If the parent finally gives up, it'll </p>
<p>call send_err(). */ </p>
<p>n = strlen(errmsg); </p>
<p>if (writen(cliptr->fd, errmsg, n) != n) /* send error to</p>
<p>client */ </p>
<p>log_sys("send_err error"); </p>
<p>exit(1); /* parent will see this, release lock, and try</p>
<p>again */ </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>_______ </p>
<p>程序18.12 child_dial 函数 </p>
<p>如果所使用的设备是直接
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -