?? 14.htm
字號:
<p>考慮一個應用程序,它向標準輸出寫一個提示,然后從標準輸入讀1行。使用pope
</p>
<p>n,我們可以在應用程序和輸入之間插入一道程序以對輸入進行變換處理。圖14.8顯
</p>
<p>示了進程的安排。 </p>
<p>圖14.8 用popen變換輸入 </p>
<p>#include <ctype.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int c; </p>
<p>while ( (c = getchar()) != EOF) { </p>
<p>if (isupper(c)) </p>
<p>c = tolower(c); </p>
<p>if (putchar(c) == EOF) </p>
<p>err_sys("output error"); </p>
<p>if (c == '\n') </p>
<p>fflush(stdout); </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序14.6 過濾程序,將大寫字符變換成小寫字符 </p>
<p>#include <sys/wait.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>char line[MAXLINE]; </p>
<p>FILE *fpin; </p>
<p>if ( (fpin = popen("myuclc", "r")) == NULL) </p>
<p>err_sys("popen error"); </p>
<p>for ( ; ; ) { </p>
<p>fputs("prompt> ", stdout); </p>
<p>fflush(stdout); </p>
<p>if (fgets(line, MAXLINE, fpin) == NULL) /* read from pipe */ </p>
<p>break; </p>
<p>if (fputs(line, stdout) == EOF) </p>
<p>err_sys("fputs error to pipe"); </p>
<p>} </p>
<p>if (pclose(fpin) == -1) </p>
<p>err_sys("pclose error"); </p>
<p>putchar('\n'); </p>
<p>exit(0); </p>
<p>} </p>
<p>程序14.7 調用大寫/小寫過濾程序以讀取命令 </p>
<p>對輸入進行的變換可能是路徑名的擴充,或者是提供一種歷史機制(記住以前輸入
</p>
<p>的命令)。 </p>
<p>程序14.6是一個簡單的過濾程序,它只是將輸入復制到輸出,在復制時將任一大
</p>
<p>寫字符變換為小寫字符。在寫了一行之后,我們對標準輸出進行了刷清(用fflus
</p>
<p>h),其理由將在下一節介紹協同進程時討論。 </p>
<p>我們對該過濾程序進行編譯,其可執行目標代碼存放在文件myuclc中,然后在程序
</p>
<p>14.7中用popen調用它們。 </p>
<p>因為標準輸出通常是按行進行緩沖的,而提示并不包含新行符,所以在寫了提示之
</p>
<p>后,需要調用fflush。 </p>
<p>14.4 協同進程 </p>
<p>Unix過濾程序從標準輸入讀取數據,對其進行適當處理后寫到標準輸出。幾個過濾
</p>
<p>進程通常在shell管道命令中線性地連接。當同一道程序產生某個過濾程序的輸入
</p>
<p>,同時又讀取該過濾程序的輸出時,則該過濾程序就成為協同進程。
</p>
<p>KornShell提供了協同進程。Bourne shell和C shell并沒有提供將進程連接起來按
</p>
<p>協同進程方式工作的方法。協同進程通常在shell的后臺運行,其標準輸入和標準
</p>
<p>輸出通過管道連接到另一道程序。雖然要求初始化一個協同進程,并將其輸入和輸
</p>
<p>出連接到另一個進程的shell語法是十分奇特的(詳細情況見Bolsky和Korn[1989]
</p>
<p>中的pp.66-66),但是協同進程的工作方式在C程序中也是非常有用的。
</p>
<p>popen提供連到另一個進程的標準輸入或標準輸出的一個單行管道,而對于協同進
</p>
<p>程,則它有連到另一個進程的兩個單行管道--一個接到其標準輸入,另一個則來從
</p>
<p>標準輸出開始。我們先要將數據寫到其標準輸入,經其處理后,再從其標準輸出讀
</p>
<p>取數據。 </p>
<p>實例 </p>
<p>讓我們通過一個實例來觀察協同進程。進程先創建兩個管道:一個是協同進程的標
</p>
<p>準輸入,另一個是協同進程的標準輸出。圖14.9顯示了這種安排。 </p>
<p>圖14.9 驅動一個協同進程--寫其標準輸入,讀其標準輸出 </p>
<p>程序14.8是一個簡單的協同進程,它從其標準輸入讀兩個數,計算它們的和,然后
</p>
<p>將結果寫至標準輸出。 </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int n, int1, int2; </p>
<p>char line[MAXLINE]; </p>
<p>while ( (n = read(STDIN_FILENO, line, MAXLINE)) > 0) { </p>
<p>line[n] = 0; /* null terminate */ </p>
<p>if (sscanf(line, "%d%d", &int1, &int2) == 2) { </p>
<p>sprintf(line, "%d\n", int1 + int2); </p>
<p>n = strlen(line); </p>
<p>if (write(STDOUT_FILENO, line, n) != n) </p>
<p>err_sys("write error"); </p>
<p>} else { </p>
<p>if (write(STDOUT_FILENO, "invalid args\n", 13) != 13) </p>
<p>err_sys("write error"); </p>
<p>} </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序14.8 加兩個數的簡單濾波器 </p>
<p>對此程序進行編譯,將其可執行目標代碼存入名為add2的文件。 </p>
<p>程序14.9在從其標準輸入讀入兩個數之后調用add2協同進程。從協同進程送來的值
</p>
<p>則寫到其標準輸出。 </p>
<p>#include <signal.h> </p>
<p>#include "ourhdr.h" </p>
<p>static void sig_pipe(int); /* our signal handler */ </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int n, fd1[2], fd2[2]; </p>
<p>pid_t pid; </p>
<p>char line[MAXLINE]; </p>
<p>if (signal(SIGPIPE, sig_pipe) == SIG_ERR) </p>
<p>err_sys("signal error"); </p>
<p>if (pipe(fd1) < 0 || pipe(fd2) < 0) </p>
<p>err_sys("pipe error"); </p>
<p>if ( (pid = fork()) < 0) </p>
<p>err_sys("fork error"); </p>
<p>else if (pid > 0) { /* parent */ </p>
<p>close(fd1[0]); </p>
<p>close(fd2[1]); </p>
<p>while (fgets(line, MAXLINE, stdin) != NULL) { </p>
<p>n = strlen(line); </p>
<p>if (write(fd1[1], line, n) != n) </p>
<p>err_sys("write error to pipe"); </p>
<p>if ( (n = read(fd2[0], line, MAXLINE)) < 0) </p>
<p>err_sys("read error from pipe"); </p>
<p>if (n == 0) { </p>
<p>err_msg("child closed pipe"); </p>
<p>break; </p>
<p>} </p>
<p>line[n] = 0; /* null terminate */ </p>
<p>if (fputs(line, stdout) == EOF) </p>
<p>err_sys("fputs error"); </p>
<p>} </p>
<p>if (ferror(stdin)) </p>
<p>err_sys("fgets error on stdin"); </p>
<p>exit(0); </p>
<p>} else { /* child */ </p>
<p>close(fd1[1]); </p>
<p>close(fd2[0]); </p>
<p>if (fd1[0] != STDIN_FILENO) { </p>
<p>if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) </p>
<p>err_sys("dup2 error to stdin"); </p>
<p>close(fd1[0]); </p>
<p>} </p>
<p>if (fd2[1] != STDOUT_FILENO) { </p>
<p>if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) </p>
<p>err_sys("dup2 error to stdout"); </p>
<p>close(fd2[1]); </p>
<p>} </p>
<p>if (execl("./add2", "add2", (char *) 0) < 0) </p>
<p>err_sys("execl error"); </p>
<p>} </p>
<p>} </p>
<p>static void </p>
<p>sig_pipe(int signo) </p>
<p>{ </p>
<p>printf("SIGPIPE caught\n"); </p>
<p>exit(1); </p>
<p>} </p>
<p>程序14.9 驅動add2過濾程序的程序 </p>
<p>在程序中創建了兩個管道,父、子進程各自關閉它們不需使用的端口。創建兩個管
</p>
<p>道的理由是;一個用作為協同進程的標準輸入,另一個則用作為它的標準輸出。然
</p>
<p>后在調用execl之前,子進程調用dup2使管道描述符移至其標準輸入和輸出。
</p>
<p>若編譯和運行程序14.9,它如所希望的那樣進行工作。進一步考慮,在程序14.9正
</p>
<p>等待我們的輸入時,若kill add2協同進程;然后輸入兩個數;當程序14.9對管道
</p>
<p>進行寫操作時,由于該管道無讀進程,于是調用信號處理程序(見練習14.4)。
</p>
<p>在程序15.1中,我們將提供這一實例的另一個版本,它使用一個全雙工管道而不是
</p>
<p>兩個半雙工管道。 </p>
<p>實例 </p>
<p>在協同進程add2(程序14.8)中,我們使用了Unix I/O:read和write。如果我們使
</p>
<p>用標準I/O改寫該協同進程,其后果是什么呢?程序14.10就是這個新版本。
</p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int int1, int2; </p>
<p>char line[MAXLINE]; </p>
<p>while (fgets(line, MAXLINE, stdin) != NULL) { </p>
<p>if (sscanf(line, "%d%d", &int1, &int2) == 2) { </p>
<p>if (printf("%d\n", int1 + int2) == EOF) </p>
<p>err_sys("printf error"); </p>
<p>} else { </p>
<p>if (printf("invalid args\n") == EOF) </p>
<p>err_sys("printf error"); </p>
<p>} </p>
<p>} </p>
<p>exit(0); </p>
<p>} </p>
<p>程序14.10 加兩個數的濾波器,使用標準I/O </p>
<p>若程序14.9調用此新的協同進程,則它就不再工作。問題出在系統默認的標準I/O
</p>
<p>緩存機制上。當程序14.10被調用時,對標準輸入的第一個fgets引起標準I/O庫分
</p>
<p>配一個緩存,并選擇緩存的類型。因為標準輸入是個管道,所以isatty為假,于是
</p>
<p>標準I/O庫由系統默認是全緩沖的。對標準輸出也有同樣的處理。當add2從其標準
</p>
<p>輸入讀取而發生堵塞時,程序14.9在從管道讀時也發生堵塞,于是產生了死鎖。
</p>
<p>我們對將要執行(exec)的這樣一個協同進程可以加以控制。在程序14.10中的wh
</p>
<p>ile循環之前加上下面4行。 </p>
<p>if ( setvbuf(stdin ,NULL, _IOLBF, O) !=0 ) </p>
<p>err_sys("setvbuf error"); </p>
<p>if ( setvbuf(stdout,NULL,_IOLBF,O)!=0 ) </p>
<p>err_sys("setvbuf error"); </p>
<p>這使得當有一行可用時,fgets即返回,使得當輸出一新行符時,printf即執行ff
</p>
<p>lush操作。對setvbuf進行了這些顯式調用,使得程序14.10能正常工作。
</p>
<p>如果我們不能修改這樣的協同進程,則需使用其它技術。例如,如果在我們的程序
</p>
<p>中使用awk(1)代替add2作為協同進程,則下列命令行不能工作; </p>
<p>#!/bin/awk/ -f </p>
<p>{print $1+$2} </p>
<p>不能工作的原因還是標準I/O的緩沖機制問題。但是,在這種情況下我們不能改變
</p>
<p>awk的工作方式(除非我們有awk的源代碼)。 </p>
<p>對這種問題的一般解決方法是使被調用(在本例中是awk)的協同進程認為它的標
</p>
<p>準輸入和輸出是連到一個終端。這使得在協同進程中的標準I/O例程對這兩個I/O流
</p>
<p>進行行緩存,這類似于我們在前面所做的顯式setvbuf調用。在第十九章中,我們
</p>
<p>將用偽終端實現這一點。 </p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -