进程间通信之管道通信
兩個程序之間傳遞數(shù)據(jù)的一種簡單方法是使用popen和pclose。
#include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream); popen函數(shù)允許一個程序將另一個程序作為新進程來啟動,并可以傳遞數(shù)據(jù)給它或者通過它接收數(shù)據(jù)。command字符串是要運行的程序名和相應的參數(shù)。type必須是"r"或"w"。如果type是"r",被調程序的輸出就可以被調用程序使用,調用程序利用popen函數(shù)返回的FILE *文件流指針,可以讀取被調程序的輸出;如果type是"w",調用程序就可以向被調程序發(fā)送數(shù)據(jù),而被調程序可以在自己的標準輸入上讀取這些數(shù)據(jù)。pclose函數(shù)只在popen啟動的進程結束后才返回。如果調用pclose時它仍在運行,pclose將等待該進程的結束。 #include <stdio.h>#define SIZE 1024*100int main() {FILE *fp = popen("ps -ef", "r");if (fp == NULL){perror ("popen");return -1;}char buf[SIZE] = {0};int ret = fread(buf, sizeof(char), SIZE-1, fp);// printf ("讀到的數(shù)據(jù):\n %s\n", buf);FILE *fp2 = popen("grep a.out", "w");if (fp2 == NULL){perror ("popen");return -1;}fwrite (buf, sizeof(char), ret, fp2);printf ("寫入完成\n");pclose (fp);pclose (fp2);return 0; }管道是單向的、先進先出的,它把一個進程的輸出和另一個進程的輸入連接在一起。一個進程(寫進程)在管道尾部寫入數(shù)據(jù),另一個進程(讀進程)從管道的頭部讀出數(shù)據(jù)。管道包括無名管道和有名管道兩種,前者用于父進程和子進程間的通信,后者可用于運行于同一系統(tǒng)中的任意兩個進程間的通信。
無名管道由pipe( )函數(shù)創(chuàng)建:
當一個管道建立時,它會創(chuàng)建兩個文件描述符:filedis[0]fi用于讀管道,ledis[1]
用于寫管道。
管道用于不同進程間通信。通常先創(chuàng)建一個管道,在通過fork函數(shù)創(chuàng)建一個子進程,該子進程會繼承父進程所創(chuàng)建的管道描述符。必須在系統(tǒng)調用fork()前調用pipe(),否則子進程將不會繼承文件描述符。
1、單個進程中的管道
#include <stdio.h> #include <unistd.h>#define SIZE 1024*100int main() {int fd[2];int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}ret = write (fd[1], "hello", 5);printf ("寫入 %d 個字節(jié)\n", ret);char ch;while (1){// 如果管道里面沒有數(shù)據(jù)可讀,read會阻塞ret = read (fd[0], &ch, 1);if (ret == -1){perror ("read");break;}printf ("讀到 %d 字節(jié): %c\n", ret, ch);}close (fd[0]);close (fd[1]);return 0; }2、父子進程通過管道進行通信
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <string.h>#define SIZE 1024// 子進程通過管道從父進程接收數(shù)據(jù) void child_do(int *fd) {// 將管道的寫端關閉close (fd[1]);char buf [SIZE];while (1){// 從父進程讀取數(shù)據(jù)int ret = read (fd[0], buf, SIZE-1);if (ret == -1){perror ("read");break;}buf[ret] = '\0';printf ("子進程讀到 %d 字節(jié)數(shù)據(jù): %s\n", ret, buf);}// 關閉讀端close (fd[0]); }// 父進程通過管道向子進程發(fā)送數(shù)據(jù) void father_do(int *fd) {// 將管道讀端關閉close (fd[0]);char buf[SIZE];while (1){fgets (buf, SIZE, stdin);// 向子進程發(fā)送數(shù)據(jù)int ret = write (fd[1], buf, strlen(buf));printf ("父進程發(fā)送了 %d 字節(jié)數(shù)據(jù)\n", ret);}// 關閉寫端close (fd[1]); }int main() {int fd[2];// 創(chuàng)建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 創(chuàng)建子進程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0: // 子進程child_do(fd);break;default:father_do(fd);break;}return 0; }3、父子進程通過管道實現(xiàn)文件復制
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h>#define SIZE 1024// 子進程通過管道從父進程接收數(shù)據(jù) void child_do(int *fd) {// 將管道的寫端關閉close (fd[1]);int fd_write = open ("2.mmap", O_WRONLY|O_CREAT, 0777);if (fd_write == -1){perror ("open");return;}int ret;char buf [SIZE];// read 從管道讀數(shù)據(jù),如果管道沒有數(shù)據(jù)可讀,read 會阻塞// 如果 管道的寫端 被關閉, read 返回 0while (ret = read (fd[0], buf, SIZE)){if (ret == -1){perror ("read");break;}// 把從父進程接收的數(shù)據(jù)寫入到新文件中write (fd_write, buf, ret);}printf ("文件復制完成\n");// 關閉讀端close (fd[0]);close (fd_write); }// 父進程通過管道向子進程發(fā)送數(shù)據(jù) void father_do(int *fd) {// 將管道讀端關閉close (fd[0]);int fd_read = open ("1.mmap", O_RDONLY);if (fd_read == -1){perror ("open");return;}int ret;char buf[SIZE];while (ret = read (fd_read, buf, SIZE)){if (ret == -1){perror ("read");break;}// 把讀到的內容發(fā)送給子進程write (fd[1], buf, ret);}// 關閉寫端close (fd[1]);close (fd_read); }int main() {int fd[2];// 創(chuàng)建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 創(chuàng)建子進程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0: // 子進程child_do(fd);break;default:father_do(fd);break;}return 0; }4、管道讀端關閉,寫端繼續(xù)寫數(shù)據(jù)
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <string.h>#define SIZE 1024// 子進程通過管道從父進程接收數(shù)據(jù) void child_do(int *fd) {close (fd[1]);close (fd[0]); }void father_do(int *fd) {// 將管道讀端關閉close (fd[0]);printf ("等待子進程關閉讀端\n");sleep(2);// 所有讀端都關閉了,寫端繼續(xù)往管道寫入數(shù)據(jù)// 如果管道所有的讀端都被關閉,繼續(xù)寫數(shù)據(jù)系統(tǒng)默認的操作是使程序退出write (fd[1], "hello", 5);printf ("11111111111111111111111111111111\n");// 關閉寫端close (fd[1]); }int main() {int fd[2];// 創(chuàng)建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 創(chuàng)建子進程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0: // 子進程child_do(fd);break;default:father_do(fd);break;}return 0; }以上是無名管道常用的一些操作。
命名管道(FIFO)和無名管道基本相同,但也有不同點:無名管道只能由父子進程使用;但是通過命名管道,不相關的進程也能交換數(shù)據(jù)。
命名管道具有很好的使用靈活性,表現(xiàn)在:
1) 既可用于本地,又可用于網(wǎng)絡。
2) 可以通過它的名稱而被引用。
3) 支持多客戶機連接。
4) 支持雙向通信。
5) 支持異步重疊I/O操作。
1、創(chuàng)建命名管道
#include <sys/types.h> #include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);pathname: FIFO文件名
mode:屬性(同文件操作)
一旦創(chuàng)建了一個FIFO,就可用open打開它,一般的文件訪問函數(shù)(close、read、write等)都可用于FIFO。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h>int main() {int ret = mkfifo("/home/mkfifo", 0777);if (ret == -1){perror ("mkfifo");return -1;}return 0; }2、命名管道的傳輸
當打開FIFO時,非阻塞標識(O_NONBLOCK)將對以后的讀寫產(chǎn)生影響:
1、沒有使用O_NONBLOCK:訪問要求無法滿足時進程將阻塞。如果試圖讀取空的FIFO,將導致進程阻塞。
2、使用O_NONBLOCK:訪問要求無法滿足時不阻塞,立刻出錯返回。errno是ENXIO。
寫入:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h>#define SIZE 1024int main() {int fd = open("/home/mkfifo", O_WRONLY);if (fd== -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){fgets (buf, SIZE, stdin);write (fd, buf, strlen(buf));}return 0; }讀取:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #define SIZE 1024int main() {int fd = open("/home/mkfifo", O_RDWR);if (fd == -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){int ret = read (fd, buf, SIZE);buf[ret] = '\0';printf ("讀到 %d 字節(jié): %s\n", ret, buf);}return 0; }管道作為進程間通信的4種方式之一,他并不會保存數(shù)據(jù),區(qū)別與共享內存。
總結
以上是生活随笔為你收集整理的进程间通信之管道通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux进程号转换成16进制,Shel
- 下一篇: linux postfix 日志,lin