⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 18.htm

📁 UNIX环境下C编程的详细详细介绍
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<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) &gt;= 0) { </p>

<p>if (strcmp(sysptr-&gt;type, devptr-&gt;type) == 0 &amp;&amp; </p>

<p>strcmp(sysptr-&gt;class, devptr-&gt;class) == 0) </p>

<p>return(0); /* found a device match */ </p>

<p>} </p>

<p>sprintf(errmsg, &quot;device `%s'/`%s' not found\n&quot;, </p>

<p>sysptr-&gt;type, sysptr-&gt;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 &quot;calld.h&quot; </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, &quot;r&quot;)) == NULL) </p>

<p>log_sys(&quot;can't open %s&quot;, 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-&gt;dialer = strtok(dialline, WHITE)) == NULL) { </p>

<p>if (dialline[0] == '\n') </p>

<p>goto again; /* ignore empty line */ </p>

<p>log_quit(&quot;missing `dialer' in Dialers file, line %d&quot;,</p>

<p>diallineno); </p>

<p>} </p>

<p>if (dialptr-&gt;dialer[0] == '#') </p>

<p>goto again; /* ignore comment line */ </p>

<p>if ( (dialptr-&gt;sub = strtok(NULL, WHITE)) == NULL) </p>

<p>log_quit(&quot;missing `sub' in Dialers file, line %d&quot;,</p>

<p>diallineno); </p>

<p>if ( (dialptr-&gt;expsend = strtok(NULL, &quot;\n&quot;)) == NULL) </p>

<p>log_quit(&quot;missing `expsend' in Dialers file, line %d&quot;,</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) &gt;= 0) { </p>

<p>if (strcmp(dialptr-&gt;dialer, devptr-&gt;dialer) == 0) </p>

<p>return(0); /* found a dialer match */ </p>

<p>} </p>

<p>sprintf(errmsg, &quot;dialer `%s' not found\n&quot;, dialptr-&gt;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 &quot;calld.h&quot; </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-&gt;sysftell); </p>

<p>while ( (cliptr-&gt;sysftell = sys_next(&amp;systems)) &gt;= 0) { </p>

<p>if (strcmp(cliptr-&gt;sysname, systems.name) == 0) { </p>

<p>/* system match */ </p>

<p>/* if client specified a speed, it must match too */ </p>

<p>if (cliptr-&gt;speed[0] != 0 &amp;&amp; </p>

<p>strcmp(cliptr-&gt;speed, systems.class) != 0) </p>

<p>continue; /* speeds don't match */ </p>

<p>DEBUG(&quot;trying sys: %s, %s, %s, %s&quot;, systems.name, </p>

<p>systems.type, systems.class, systems.phone); </p>

<p>cliptr-&gt;foundone++; </p>

<p>if (dev_find(&amp;devices, &amp;systems) &lt; 0) </p>

<p>break; </p>

<p>DEBUG(&quot;trying dev: %s, %s, %s, %s&quot;, devices.type, </p>

<p>devices.line, devices.class, devices.dialer); </p>

<p>if ( (pid = is_locked(devices.line)) != 0) { </p>

<p>sprintf(errmsg, &quot;device `%s' already locked by pid %d\n&quot;, </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-&gt;pid = fork()) &lt; 0) </p>

<p>log_sys(&quot;fork error&quot;); </p>

<p>else if (cliptr-&gt;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-&gt;pid); </p>

<p>/* let child resume, now that lock is set */ </p>

<p>TELL_CHILD(cliptr-&gt;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-&gt;foundone == 0) </p>

<p>sprintf(errmsg, &quot;system `%s' not found\n&quot;, cliptr-&gt;sysname);</p>

<p>else if (errmsg[0] == 0) </p>

<p>sprintf(errmsg, &quot;unable to connect to system `%s'\n&quot;, </p>

<p>cliptr-&gt;sysname); </p>

<p>return(-1); /* also, cliptr-&gt;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 &quot;calld.h&quot; </p>

<p>#include &lt;sys/wait.h&gt; </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, &amp;stat, 0)) &lt;= 0) </p>

<p>log_sys(&quot;waitpid error&quot;); </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(&quot;child %d terminated abnormally: %04x&quot;, 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 &quot;calld.h&quot; </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-&gt;fd (to send DEBUG() output and fd back to client),</p>

<p>* cliptr-&gt;Debug (for all DEBUG() output), childptr-&gt;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-&gt;Debug; </p>

<p>DEBUG(&quot;child, pid %d&quot;, getpid()); </p>

<p>if (strcmp(devices.dialer, &quot;direct&quot;) == 0) { /* direct tty</p>

<p>line */ </p>

<p>fd = tty_open(systems.class, devices.line, cliptr-&gt;parity,</p>

<p>0); </p>

<p>if (fd &lt; 0) </p>

<p>goto die; </p>

<p>} else { /* else assume dialing is needed */ </p>

<p>if (dial_find(&amp;dialers, &amp;devices) &lt; 0) </p>

<p>goto die; </p>

<p>fd = tty_open(systems.class, devices.line, cliptr-&gt;parity,</p>

<p>1); </p>

<p>if (fd &lt; 0) </p>

<p>goto die; </p>

<p>if (tty_dial(fd, systems.phone, dialers.dialer, </p>

<p>dialers.sub, dialers.expsend) &lt; 0) </p>

<p>goto die; </p>

<p>} </p>

<p>DEBUG(&quot;done&quot;); </p>

<p>/* send the open descriptor to client */ </p>

<p>if (send_fd(cliptr-&gt;fd, fd) &lt; 0) </p>

<p>log_sys(&quot;send_fd error&quot;); </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-&gt;fd, errmsg, n) != n) /* send error to</p>

<p>client */ </p>

<p>log_sys(&quot;send_err error&quot;); </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 + -