(1)什麼是協同進程?shell
當一個過濾程序既產生某個過濾程序的輸入,又讀取該過濾程序的輸出時,它就變成了協同進程。函數
(2)spa
協同進程一般在shell的後臺運行,其標準輸入和標準輸出經過管道鏈接到另外一個程序。code
(3)popen所建立的進程與協同進程的區別。orm
popen只提供鏈接到另外一個進程的標準輸入或標準輸出的一個單向管道。接口
協同進程有鏈接到另外一個進程的兩個單向管道。進程
(1)一個簡單的過濾程序(add2爲編譯後的可執行程序)ip
#include "apue.h" int main(void) { int n, int1, int2; char line[MAXLINE]; while ((n = read(STDIN_FILENO, line, MAXLINE)) > 0) { line[n] = 0; /* null terminate */ if (sscanf(line, "%d%d", &int1, &int2) == 2) { sprintf(line, "%d\n", int1 + int2); n = strlen(line); if (write(STDOUT_FILENO, line, n) != n) err_sys("write error"); } else { if (write(STDOUT_FILENO, "invalid args\n", 13) != 13) err_sys("write error"); } } exit(0); }
(2)字符串
程序經過底層I/O(UNIX系統調用):read,write函數實現對標準輸入輸出的讀寫。(它們是不帶緩衝的I/O)<7>get
若使用標準I/O函數fgets和printf代替,則此程序不工做。由於標準I/O庫對管道默認使用全緩衝。(標準I/O函數爲那些不帶緩衝I/O函數提供了一個帶緩衝的接口)
當add2從其標準輸入讀取而發生阻塞時,驅動程序從管道讀時也發生阻塞,因而產生了死鎖。
可經過setvbuf函數將標準輸入輸出改成行緩衝使程序正常工做。
若在其餘狀況下,沒法修改過濾程序,則只能經過其餘技術來時過濾程序正常工做。好比僞終端技術。
(3)
int sscanf(const char *str, const char *format, ...);
int sprintf(char *str, const char *format, ...);
這兩個函數對字符串進行讀寫。
(1)驅動過濾程序的程序。
#include "apue.h" static void sig_pipe(int); /* our signal handler */ int main(void) { int n, fd1[2], fd2[2]; pid_t pid; char line[MAXLINE]; if (signal(SIGPIPE, sig_pipe) == SIG_ERR) err_sys("signal error"); if (pipe(fd1) < 0 || pipe(fd2) < 0) err_sys("pipe error"); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid > 0) { /* parent */ close(fd1[0]); close(fd2[1]); while (fgets(line, MAXLINE, stdin) != NULL) { n = strlen(line); if (write(fd1[1], line, n) != n) err_sys("write error to pipe"); if ((n = read(fd2[0], line, MAXLINE)) < 0) err_sys("read error from pipe"); if (n == 0) { err_msg("child closed pipe"); break; } line[n] = 0; /* null terminate */ if (fputs(line, stdout) == EOF) err_sys("fputs error"); } if (ferror(stdin)) err_sys("fgets error on stdin"); exit(0); } else { /* child */ close(fd1[1]); close(fd2[0]); if (fd1[0] != STDIN_FILENO) { if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) err_sys("dup2 error to stdin"); close(fd1[0]); } if (fd2[1] != STDOUT_FILENO) { if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) err_sys("dup2 error to stdout"); close(fd2[1]); } if (execl("./add2", "add2", (char *)0) < 0) err_sys("execl error"); } exit(0); } static void sig_pipe(int signo) { printf("SIGPIPE caught\n"); exit(1); }
(2)
1)註冊SIGPIPE的信號處理程序。
SIGPIPE信號在向沒有讀進程的管道寫時產生。
這個例子中,在等待輸入的時候殺死了協同進程,而後又輸入兩個數,則產生這個信號。
2)建立兩個管道。
3)建立子進程。
4)父子進程分別關閉不須要的管道。
5)父進程使用fgets和fputs讀寫標準輸入,用write和read讀寫管道。