进程间通信(一)管道
一、概述
在Linux系統中,以進程為單位來分配和管理資源。
由于保護的緣故,一個進程不能直接訪問另一個進程的資源,也就是說,進程之間互相封閉。
在一個復雜的應用系統中,通常會使用多個相關的進程來共同完成一項任務,因此要求進程之間必須能夠互相通信,從而來共享資源和信息。
所以,一個操作系統內核必須提供進程間的通信機制。
管道
管道和有名管道是最早的進程間通信機制之一,管道可用于具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信。
管道是指用于連接一個讀進程和一個寫進程,以實現它們之間通信的共享方式,又稱pipe文件。
管道是Linux支持的最初Unix IPC形式之一,具有以下特點:
管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;
只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);
單獨構成一種獨立的文件系統:管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統,而是自立門戶,單獨構成一種文件系統,并且只存在與內存中。
數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,并且每次都是從緩沖區的頭部讀出數據。
管道是所有Unix都提供的一種IPC機制
一個進程將數據寫入管道,另一個進程從管道中讀取數據
在shell中使用管道的例子
命令:“ls | more”
使用pipeline “|”將兩個命令”ls”和“more”連接起來,使得ls的輸出成為more的輸入
也可以使用如下的兩個命令
命令1:“ls > tmp”
命令2:”more < tmp”
命令1把ls的輸出重定向到tmp文件中;
命令2把more的輸入重定向到tmp文件
創建管道
管道的創建:
#include <unistd.h>
int pipe(int fd[2])
該函數創建的管道的兩端處于一個進程中間,在實際應用中沒有太大意義,因此,一個進程在由pipe()創建管道后,一般再fork一個子進程,然后通過管道實現父子進程間的通信。
注意:fd[0] 用于讀取管道,fd[1] 用于寫入管道。
管道讀寫
管道主要用于不同進程間通信。實際上,通常先創建一個管道,再通過fork函數創建一個子進程。
子進程寫入和父進程讀的命名管道:
管道的讀寫規則
管道兩端可分別用描述字fd[0]以及fd[1]來描述。
需要注意的是,管道的兩端是固定了任務的。即一端只能用于讀,由描述字fd[0]表示,稱其為管道讀端;另一端則只能用于寫,由描述字fd[1]來表示,稱其為管道寫端。
如果試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將導致錯誤發生。
一般文件的I/O函數都可以用于管道,如close、read、write等等。
系統文件 write(fd[1],buf,size)
功能:把buf中的長度為size字符的消息送入管道入口fd[1]
fd[1]—pipe入口
buf:存放消息的空間
size :要寫入的字符長度
系統文件 read(fd[0],buf,size)
功能:從pipe出口fd[0]讀出size字符的消息置入 buf中。
fd[0]――Pipe的出口
進程通信 管道 (pipe系統調用)
pipe(fd): 創建一個管道,fd[0]為管道的讀端;fd[1]為管道的寫端。 管道可用來實現父進程與其子孫進程之間的通信。管道以FIFO方式傳送消息。
#include <stdio.h> ...... main() { int x,fd[2]; char buf[30],s[30]; pipe(fd); /*創建管道*/ while((x=fork()) = = -1); /*創建子進程失敗時,循環*/ if(x = = 0) { sprintf(buf,“This is an example\n”); write(fd[1],buf,30); /*把buf中的字符寫入管道*/ exit(0); } wait(); read(fd[0],s,30); /*父進程讀管道中的字符*/ printf(“%s”,s); }有名管道
管道應用的一個重大限制是它沒有名字,因此,只能用于具有親緣關系的進程間通信,在有名管道(named pipe或FIFO)提出后,該限制得到了克服。
FIFO不同于管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存在于文件系統中。這樣,即使與FIFO的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進程以及FIFO的創建進程之間)。
因此,通過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。
有名管道的創建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
該函數的第一個參數是一個普通的路徑名,也就是創建后FIFO的名字。
第二個參數與打開普通文件的open()函數中的mode 參數相同。
如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那么只要調用打開FIFO的函數就可以了。一般文件的I/O函數都可以用于FIFO,如close、read、write等等。
有名管道比管道多了一個打開操作:open。
FIFO的打開規則:
如果當前打開操作是為讀而打開FIFO時,若已經有相應進程為寫而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程為寫而打開該FIFO(當前打開操作設置了阻塞標志);或者,成功返回(當前打開操作沒有設置阻塞標志)。
如果當前打開操作是為寫而打開FIFO時,如果已經有相應進程為讀而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程為讀而打開該FIFO(當前打開操作設置了阻塞標志);或者,返回ENXIO錯誤(當前打開操作沒有設置阻塞標志)。
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> main() { char buffer[80]; int fd; char *FIFO=“pipe1”; mkfifo(FIFO,0666); if(fork()>0){ char s[ ] = "hello!\n";fd = open (FIFO,O_WRONLY); write(fd,s,sizeof(s)); close(fd); } else{ fd= open(FIFO,O_RDONLY);read(fd,buffer,80); printf("%s",buffer); close(fd); } }管道的局限性
從IPC的角度看,管道提供了從一個進程向另一個進程傳輸數據的有效方法。但是,管道有一些固有的局限性:
1.因為讀數據的同時也將數據從管道移去,因此,管道不能用來對多個接收者廣播數據。
2.管道中的數據被當作字節流,因此無法識別信息的邊界。
如果一個管道有多個讀進程,那么寫進程不能發送數據到指定的讀進程。同樣,如果有多個寫進程,那么沒有辦法判斷是它們中哪一個發送的數據。
轉載于:https://www.cnblogs.com/yanghaishu/archive/2012/05/10/2495201.html
總結
以上是生活随笔為你收集整理的进程间通信(一)管道的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++算法与数据结构学习笔记-----
- 下一篇: JSF框架在NetBeans下的编码