歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux下的進程通信方式(IPC)――管道通信

IPC: 管道、命名管道(FIFO)
管道1、概念
管道是單向的、先進先出、無結構的字節流,它把一個進程的輸出和另一個進程的輸入連接在一起。
寫進程在管道的尾端寫入數據,讀進程在管道的首端讀出數據。數據讀出後將從管道中移走,其它讀進程都不能再讀到這些數據。
管道提供了簡單的流控制機制。進程試圖讀一個空管道時,在數據寫入管道前,進程將一直阻塞。同樣,管道已經滿時,進程再試圖寫管道,在其它進程從管道中讀走數據之前,寫進程將一直阻塞。
2、管道的特點
(1)單向數據通信,具有固定的讀端和寫端
(2)只能用於具有親緣關系的進程之間的通信(也就是父子進程或者兄弟進程之間)
(3)管道所傳輸的是無格式的字節流,要求管道輸入方與輸出方事先約定好數據格式
(4)管道的生命周期隨進程,進程退出,文件會被操作系統回收
(5)LINUX把管道看作是一種文件,采用文件管理的方法對管道進行管理,對於它的讀寫也可以使
用普通的read()和write()等函數。但是它不是普通的文件,並不屬於其他任何文件系統,只
存在於內核的內存空間中。
3、管道創建與關閉
>管道是基於文件描述符的通信方式,當一個管道建立時,它會創建兩個文件描述符fds[0]和fds[1]
>fds[0]固定用於讀管道,fds[1]固定用於寫管道
>管道關閉時只需將這兩個文件描述符關閉即可,可使用普通的close()函數逐個關閉各個文件描述符



4、管道創建函數
所需頭文件 #include<unistd.h>
函數原型
int pipe(int fd[2])
函數傳入值
fd[2]用於保存管道的兩個文件描述符,之後就可以直接操作這兩個文件描述符函數返回值
成功:0
出錯:-1
5、管道讀寫說明
> 使用管道進行父子進程間通信的步驟:
>創建管道:父進程調用pipe()函數創建一個管道
>此時,管道的讀端和寫端都在一個進程之中,這種管道是沒有多大用的。
>父進程通過fork()函數創建一子進程
>子進程會繼承父進程所創建的管道,這時,父子進程中管道的文件描述符對應關系如圖所示。
>確定管道的傳輸方向:在父、子進程中根據需要的傳輸方向關閉無關的讀端或寫端文件描述符
>通信:在寫進程中調用write()函數,在讀進程中調用read()函數
>關閉管道:調用close()關閉管道相關的文件描述符。









>關閉了父進程的寫端fd[1]和子進程的讀端fd[0],這樣就可以建立一條“子進程寫入父進程讀取”
的通道。
> 也可以關閉父進程的讀端fd[0]和子進程的寫端fd[1],這樣就可以建立一條“父進程寫入子進程讀
取”的通道。
>父進程還可以創建多個子進程,各個子進程都繼承了相應的fd[0]和fd[1],這時,只需要關閉相應
端 口就可以建立起各子進程(兄弟進程)之間的通道。
6、使用管道需要注意的4種特殊情況(假設都是阻塞I/O操作,沒有設置O_NONBLOCK標志)
(1)如果所有指向管道寫端的文件描述符都關閉了(管道寫端的引用計數為0),而仍然有進程從管道的讀端讀數據,那麼管道中剩余的數據都被讀取後,再次read會返回 0,就像讀到文件末尾一樣。
(2) 如果所有指向管道寫端的文件描述符都沒關閉(管道寫端的引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩余的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了才讀取數據並返回。
(3)如果所有指向管道讀端的文件描述符都關閉了(管道讀端的引用計數為0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終止。
(4)如果所有指向管道讀端的文件描述符都沒關閉(管道讀端的引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。
7、匿名管道的局限性
>只支持單向數據流
>只能用於具有親緣關系的進程之間通信,沒有名字
>緩沖區有限,管道只存在於主存中,大小為一個頁面
>所傳送的是無格式字節流
8、管道實例
首先創建管道,之後父進程用fork函數創建出子進程,然後通過關閉父進程的讀描述符和子進程的寫描述符,建立起它們之間的管道通信。
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_DATA_LEN 256
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN];
const char data[] = "Hello bit";
int real_read, real_write;
memset((void*)buf, 0, sizeof(buf));
/* 創建管道 */
if (pipe(pipe_fd) < 0)
{
printf("pipe create error\n");
exit(1);
}
if ((pid = fork()) == 0)
{
/* 子進程關閉寫描述符,並通過使子進程暫停3s等待父進程已關閉相應的讀描述符 */
close(pipe_fd[1]);
sleep(3);
/* 子進程讀取管道內容 */
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
}
/* 關閉子進程讀描述符 */
close(pipe_fd[0]);
exit(0);
}
else if (pid > 0)
{
/* 父進程關閉讀描述符,並通過使父進程暫停1s等待子進程已關閉相應的寫描述符 */ close(pipe_fd[0]);
sleep(1);
if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Parent wrote %d bytes : '%s'\n", real_write, data);
}
close(pipe_fd[1]);
/*關閉父進程寫描述符*/
waitpid(pid, NULL, 0); /*收集子進程退出信息*/
exit(0);
}
return 0;
}
運行結果:
[zr@localhost pipe]$ gcc -o pipe pipe.c
[zr@localhost pipe]$ ./pipe
Parent wrote 9 bytes : 'Hello bit'
9 bytes read from the pipe is 'Hello bit'
[zr@localhost pipe]$
命名管道1、概念
FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存儲於文件系統中。命名管道是一個設備文件,因此,即使進程與創建FIFO的進程不存在親緣關系,只要可以訪問該路徑,就能夠通過FIFO相互通信,值得注意的是,FIFO總是按照先進先出的的原則工作,第一個被寫入的數據將首先從管道中讀出。
2、 命名管道的創建和使用
創建:
Linux有兩種方式創建命名管道:
(1)在shell下交互地建立一個命名管道
可使用mknod或mkfifo命令
(2)在程序中使用系統函數建立命名管道
創建命名管道的系統函數有兩個:mknod和mkfifo, 函數原型如下:



這兩個函數都能創建一個真實存在於文件系統中的文件,filename指定了文件名,mode指定了文件的讀取權限。盡量使用mkfifo(簡單、規范)
mkfifo作用是在文件系統中創建一個文件,該文件提供FIFO功能,即命名管道。對文件系統來說,匿名管道不可見,它的作用僅限於在父進程和子進程兩個進程間通信,而命名管道是一個可見文件,因此它可用於任何兩個進程間通信,不管兩個進程間是不是父子進程,也不管兩個進程間有沒有關系。
使用方法:
命名管道的使用方法與管道的使用方法基本相同,只是使用命名管道時,必須先調用open()將其打開。因為命名管道是存在於硬盤上的文件,而管道是存在於內存中的特殊文件。
注意:調用open打開命名管道的進程可能會被阻塞。但如果同時用讀寫方式(O_RDWR)打開,則一定不會阻塞,如果以只讀方式(O_RDONLY)打開,則調用open函數的進程將會被阻塞直到有寫方打開管道;同時以寫方式(O_WRONLY)打開也會阻塞直到有讀方打開管道。
Copyright © Linux教程網 All Rights Reserved