日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX再学习 -- 进程间通信之管道

發(fā)布時間:2025/3/15 编程问答 9 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX再学习 -- 进程间通信之管道 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、進程間通信概念

首先,需要了解一下什么是進程間通信。 進程之間的相互通信的技術,稱為進程間通信(InterProcess Communication,IPC)。
下圖列出 4 種實現(xiàn)所支持的不同形式的 IPC。

之前進程間交換信息的方法只能是由 fork 或 exec 傳送文件。 進程間通信 (IPC)方式有: (1)管道 (2)消息隊列 (3)信號量 (4)共享存儲 (5)套接字 其中消息隊列、信號量、共享存儲統(tǒng)稱為 XSI IPC通信方式
下面我們開始一一詳細講解:

二、管道

管道是 UNIX 系統(tǒng) IPC 的最古老形式。所有 UNIX 系統(tǒng)都提供此種通信機制。管道有以下兩種局限性。 (1)歷史上,它們是半雙工的(即數(shù)據(jù)只能在一個方向上流動)。現(xiàn)在,某些系統(tǒng)提供全雙工管道,但是為了最佳的可移植性,我們決不應預先假定系統(tǒng)支持全雙工管道。 (2)管道只能在具有公共祖先的兩個進程之間使用。通常,一個管道由一個進程創(chuàng)建,在進程調用 fork 之后,這個管道就能在父進程和子進程之間使用了。 盡管有這兩種局限性,半雙工管道仍是最常用的 IPC 形式。
其中管道又分為,有名管道 無名管道。

1、無名管道

無名管道是一個與文件系統(tǒng)無關的內核對象,主要用于父子進程之間的通信,需要用專門的系統(tǒng)調用函數(shù)創(chuàng)建。 #include <unistd.h> int pipe(int pipefd[2]); 返回值:若成功,返回 0;若出錯,返回 -1.

(1)函數(shù)功能

主要用于創(chuàng)建管道文件,利用參數(shù)返回兩個文件描述符。 其中 pipefd[0] 用于從所創(chuàng)建的無名管道中讀取數(shù)據(jù),pipefd[1] 用于向該管道寫入數(shù)據(jù)pipefd[1] 的輸出是?pipefd[0] 的輸入。

(2)基于無名管道實現(xiàn)進程間通信的編程模型

《1》父進程調用 pipe 函數(shù)在系統(tǒng)內核中創(chuàng)建無名管道對象,并通過該函數(shù)的輸出參數(shù) pipefd,獲得分別用于讀寫該管道的兩個文件描述符 pipefd[0] 和 pipefd[1]?
《2》父進程調用 fork 函數(shù),創(chuàng)建子進程。子進程復制父進程的文件描述符表,因此子進程同樣持有分別用于讀寫該管道的兩個文件描述符 pipefd[0] 和 pipefd[1]
《3》負責寫數(shù)據(jù)的進程關閉無名管道對象的讀端文件描述符 pipefd[0],而負責讀數(shù)據(jù)的進程則關閉管道的寫端文件描述符 pipefd[1]

《4》父子進程通過無名管道對象以半雙工的方式傳輸數(shù)據(jù)。如果需要在父子進程間實現(xiàn)雙向通信,較一般化的做法是創(chuàng)建兩個管道,一個從父流向子,一個從子流向父
《5》父子進程分別關閉自己所持有的寫端或讀端文件描述符。在與一個無名管道對象相關聯(lián)的所有文件描述符都被關閉以后,該無名管道對象即從系統(tǒng)內核中被銷毀

(3)示例說明

//示例一 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h>int main() {int pipefd[2];if (pipe (pipefd) == -1){perror ("pipe");exit (EXIT_FAILURE);}pid_t pid;if((pid=fork())<0){perror("fork");}else if(pid==0){printf("這是子進程,pid=%d,",getpid());printf("父進程的pid=%d\n",getppid());if (close (pipefd[1]) == -1){perror ("close");exit (EXIT_FAILURE);}char text[20];ssize_t readed = read (pipefd[0], text, 20);if (readed == -1){perror ("read");exit (EXIT_FAILURE);}printf("%s\n", text);if (close (pipefd[0]) == -1){perror ("close");exit (EXIT_FAILURE);}}else{sleep (1);printf("這是父進程,pid=%d\n",getpid());if (close (pipefd[0]) == -1){perror ("close");exit (EXIT_FAILURE);}ssize_t written = write (pipefd[1], "hello world", 12);if (written == -1){perror ("write");exit (EXIT_FAILURE);}if (close (pipefd[1]) == -1){perror ("close");exit (EXIT_FAILURE);}}return 0; } 輸出結果: 這是子進程,pid=2799,父進程的pid=2798 這是父進程,pid=2798 hello world //示例二 #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main(void){ int result,n; int fd[2]; pid_t pid; char line[256]; if(pipe(fd) < 0){ perror("pipe"); return -1; } if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid > 0){ //parent close(fd[0]); if(fd[1] != STDOUT_FILENO){ dup2(fd[1],STDOUT_FILENO); } execl("/bin/ls","ls",(char*)0); }else{ //child close(fd[1]); while((n =read(fd[0],line,256)) > 0){ if(write(STDOUT_FILENO,line,n) != n){ perror("write"); exit(-1); } } close(fd[0]); } return 0; } 輸出結果: a.out test.c test.c~

(4)示例解析

創(chuàng)建了一個從父進程到子進程的管道,將父進程的讀關閉,子進程的寫關閉。使得父進程經(jīng)由該管道想子進程傳送數(shù)據(jù)。管道方向如下:
當管道的一端被關閉后,下列兩條規(guī)則其作用:
(1)當讀(read)一個寫端已經(jīng)被關閉的管道時,在所有數(shù)據(jù)都被讀取后,read 返回 0,表示文件結束。
(2)如果寫(write)一個讀端已經(jīng)被關閉的管道,則產(chǎn)生信號 SIGPIPE。如果忽略該信號或者捕獲該信號并從其處理程序返回,則 write 返回 -1,errno 設置為 EPIPE。
在寫管道(或FIFO)時,常量 PIPE_BUF 規(guī)定了內核中管道緩沖區(qū)的大小,如果對管道調用 write,而且要求寫的字節(jié)數(shù)小于等于 PIPE_BUF,則此操作不會與其他進程對同一管道(或FIFO)的 write 操作交叉進行。但是,若有多個進程同時寫一個管道(或FIFO),而且我們要求寫的字節(jié)數(shù)超過 PIPE_BUF 字節(jié)數(shù)時,那么我們所寫的數(shù)據(jù)可能會與其他進程所寫的數(shù)據(jù)相互交叉。用 pathconf 或 fpathconf 函數(shù)可以確定 PIPE_BUF 的值

(5)函數(shù) popen 和 pclose

#include<stdio.h> FILE *popen(const char* cmdstring, const char *type); //若成功則返回文件指針,出錯則返回NULL。 int pclose(FILE *fp); //返回cmdstring的終止狀態(tài),若出錯則返回-1。

《1》函數(shù)解析

常見的操作是創(chuàng)建一個連接到另一個進程的管道,然后讀其輸出或向其輸入端發(fā)送數(shù)據(jù),為此,標準 I/O 庫提供了兩個函數(shù) popen 和 pclose。這兩個函數(shù)實現(xiàn)的操作是:創(chuàng)建一個管道,fork 一個子進程,關閉未使用的管道端,執(zhí)行一個 shell 運行命令,然后等待命令終止。

《2》函數(shù)使用

函數(shù) popen 先執(zhí)行 fork,然后調用 exec 執(zhí)行 cmdstring,并且返回一個標準 I/O 文件指針。如果 type 是“r”,則文件指針連接到 cmdstring 的標準輸出。如果 type 是“w”,則文件指針連接到 cmdstring 的標準輸入。

pclose 函數(shù)關閉標準 I/O 流,等待命令終止,然后返回 shell 的終止狀態(tài)。如果 shell 不能被執(zhí)行,則 pclose 返回的終止狀態(tài)與 shell 已執(zhí)行 exit (127) 一樣。 cmdstring 由 Bourbe shell 以下列方式執(zhí)行: sh -c cmdstring 這表示 shell 將擴展 cmdstring 中的任何特殊字符。例如,可以使用:
fp = popen ("ls *.c", "r");
或者 fp = popen ("cmd 2>$1", "r");

《3》示例說明

#include<stdio.h> int main(void) { char line[256]; FILE* fpin; int n; if((fpin = popen("/bin/ls","r")) == NULL){ perror("popen"); return -1; } while(fgets(line, 256, fpin) != NULL){ if(fputs(line,stdout) == EOF){ perror("fputs"); return -1; } } if(pclose (fpin) == -1){perror ("pclose");return -1;}return 0; } 輸出結果: a.out test.c test.c~

2、有名管道?

(1)有名管道簡介

有名管道亦稱 FIFO,是一種特殊的文件,它的路徑名存在于文件系統(tǒng)中。通過 mkfifo 命令可以創(chuàng)建管道文件 //創(chuàng)建管道文件 # mkfifo myfifo//在文件系統(tǒng)中,管道文件被顯示成這樣子 # ls -la myfifo prw-r--r-- 1 root root 0 Jun 3 13:49 myfifo 查看?mkfifo --help# mkfifo --help 用法:mkfifo [選項]... 名稱... 以指定的名稱創(chuàng)建先進先出文件(FIFO)。長選項必須使用的參數(shù)對于短選項時也是必需使用的。-m, --mode=模式 設置權限模式(類似chmod),而不是rwxrwxrwx 減umask-Z, --context=CTX 將每個創(chuàng)建的目錄的SELinux 安全環(huán)境設置為CTX--help 顯示此幫助信息并退出--version 顯示版本信息并退出 可以看到創(chuàng)建管道時是可以添加權限的:
創(chuàng)建管道 # mkfifo -m 0666 myfifo查看管道權限 # ls -la myfifo prw-rw-rw- 1 root root 0 Jun 3 14:52 myfifo即使是毫無親緣關系的進程,也可以通過管道文件通信。 //在一個終端執(zhí)行: # echo 'hello,FIFO!' > myfifo //在另一個終端執(zhí)行: # cat myfifo hello,FIFO! 管道文件在磁盤上只有 i 節(jié)點沒有數(shù)據(jù)塊,也不保存數(shù)據(jù)。

(2)基于有名管道實現(xiàn)進程間通信的邏輯模型


(3)函數(shù) mkfifo

有名管道不僅可以用于 shell 命令,也可以在代碼中使用。 shell編程之前講過了,參看:UNIX再學習 -- shell編程
基于有名管道實現(xiàn)進程間的通信的編程模型:

其中除了 mkfifo 函數(shù)時專門針對有名管道的,其它函數(shù)都與操作普通文件沒有任何差別。 有名管道是文件系統(tǒng)的一部分,如不刪除,將一直存在。

下面介紹一下函數(shù) mkfifo:

#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 返回值:成功返回 0,失敗返回 -1.
《1》參數(shù)解析
pathname:文件路徑名 mode:權限模式
《2》函數(shù)功能
創(chuàng)建有名管道文件
《3》示例說明
#include<stdio.h> #include<sys/stat.h> #include<fcntl.h> #include<stdlib.h> #define MYFIFO "myfifo" int main(void) { char buffer[256]; pid_t pid; int fd; unlink(MYFIFO); if(mkfifo(MYFIFO,0666) < 0){ perror("mkfifo"); return -1; } if((pid = fork())<0){ perror("fork"); return -1; }else if(pid > 0){ char s[] = "hello world!"; fd = open(MYFIFO,O_RDWR); write(fd,s,sizeof(s)); close(fd); }else{ fd = open(MYFIFO,O_RDONLY); read(fd,buffer,256); printf("%s\n",buffer); close(fd); exit(0); } waitpid(pid,NULL,0); return 0; } 輸出結果: hello world!

4、FIFO用途

FIFO有以下兩種用途: (1)shell 命令使用 FIFO 將數(shù)據(jù)從一條管道傳送到另一條時嗎,無需創(chuàng)建中間臨時文件。
(2)客戶進程-服務器進程應用程序中,FIFO 用作匯聚點,在客戶進程和服務器進程二者之間傳遞數(shù)據(jù)。

3、有名管道和無名管道區(qū)別

講了這么多,我們來看看兩者的區(qū)別。 參看:進程中通信的‘無名管道’和‘有名管道’的用法和二者的區(qū)別 根據(jù)基于無名/有名管道實現(xiàn)進程間通信的邏輯模型我們可以得出:
若管道對象在使用時內核產(chǎn)生,不使用時就不產(chǎn)生時,那么這一定是無名管道;若在使用時內核中產(chǎn)生了一個管道文件,且不使用時還于內核中存在,那么往往是有名管道。 (1)無名管道特點 《1》只能用于具有親緣關系的進程之間通信(父子進程或者兄弟進程)。
《2》是一個單工(半雙工)的通信模式,具有固定的讀寫端。
《3》每次使用都需要創(chuàng)建管道對象。
(2)有名管道特點 《1》可以在互不相關的進程之間實現(xiàn)通信。
《2》該管道是通過路徑名來指出,在文件系統(tǒng)中是可以看到的,在建立管道后可以當做普通文件來使用讀寫操作。
《3》嚴格遵循先進先出的規(guī)則,對管道及 FIFO 的讀總是從開始處返回數(shù)據(jù),對它們的寫則把數(shù)據(jù)添加到末尾。且不支持如 lseek()等文件定位操作。

產(chǎn)生的管道文件在磁盤上只有 i 節(jié)點沒有數(shù)據(jù)塊,不保存數(shù)據(jù)。 我們來查看一下管道文件類型: # stat myfifo 文件:"myfifo"大小:0 塊:0 IO 塊:4096 先進先出 設備:801h/2049d Inode:2128483 硬鏈接:1 權限:(0666/prw-rw-rw-) Uid:( 0/ root) Gid:( 0/ root) 最近訪問:2017-06-03 14:52:53.952811041 +0800 最近更改:2017-06-03 14:52:53.952811041 +0800 最近改動:2017-06-03 14:52:53.952811041 +0800 創(chuàng)建時間:- 值得注意的是: 當使用 open() 來打開 FIFO 文件時,O_NONBLOCK 旗標會有影響
1、當使用O_NONBLOCK 旗標時,打開 FIFO 文件來讀取的操作會立刻返回,但是若還沒有其他進程打開 FIFO 文件來讀取,則寫入的操作會返回 ENXIO 錯誤代碼。?
2、沒有使用 O_NONBLOCK 旗標時,打開 FIFO 來讀取的操作會等到其他進程打開 FIFO 文件來寫入才正常返回。同樣地,打開 FIFO 文件來寫入的操作會等到其他進程打開 FIFO 文件來讀取后才正常返回。
類似于管道,若用 write 寫一個尚無進程為讀而打開的 FIFO,則產(chǎn)生信號 SIGPIPE。若某個 FIFO 的最后一個寫進程關閉了 FIFO,則將為該 FIFO 的讀進程產(chǎn)生一個文件結束標志。

4、linux下shell編程之管道

在 Linux 下我們可以采用管道操作符 “|”來連接多個命令或進程,在連接的管道線兩邊,每個命令執(zhí)行時都是一個獨立的進程。前一個命令的輸出正是下一個命令的輸入。這些進程可以同時進行,而且隨著數(shù)據(jù)流在它們之間的傳遞可以自動地進行協(xié)調,從而能夠完成較為復雜的任務。管道我們也并不陌生,之前講 xargs 用法時有用到的。參看:C語言再學習 -- Xargs用法詳解一般形式:[命令1] | [命令2] | [命令3]實例:ls 命令查看 # ls sh.sh text.txt 可以可以指定查找腳本文件 # ls | grep *sh sh.sh


總結

以上是生活随笔為你收集整理的UNIX再学习 -- 进程间通信之管道的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。