一、概念
進程間通信是在不同進程間傳播或者交換消息。管道,也就是匿名管道,是linux系統下最常見的進程間通信方式之一,它是在兩個進程間實現一個數據流通的通道,優點:簡單易用;缺點:功能簡單。
管道是linux/unix系統間比較原始的進程間通信方式,實現數據以一種數據流的方式在進程間流動。匿名管道在系統中沒有實名的,並不可以在文件系統中以任何方式看到該管道,管道只是進程的一種資源,隨着進程的結束被系統清除。創建一個管道時生成了兩個文件描述符,但是沒有路徑名,也就是不存在有意義的文件,此處的文件描述符只是在內存中跟某一個索引節點相關聯的兩個文件描述符。
二、管道通信特點:
1,無名字,是匿名管道
2,半雙工通信,需要雙工通信時,需要建立起連個管道
3,只能用於父子進程和兄弟進程之間
4,單獨構成一種文件系統,存在於內存中
5,寫入的內容在管道緩衝區的末尾,然後從管道緩衝區的頭部讀出
6,緩衝區大小有限
7,管道所傳送的是無格式字節流。
三、管道的創建與關閉
Linux環境下使用pipe函數創建一個管道:intpipe(int fd[2]); 若成功返回0,若失敗返回-1.參數fd[2]是一個長度爲2的文件描述符數組,fd[0]是讀出端的文件描述符,fd[1]是寫入端的文件描述符。當函數成功返回後,自動維護了一個從fd[1]到fd[0]的通道。管道的關閉使用close函數。
程序一演示瞭如何使用創建和關閉管道,並使用管道傳輸數據,程序結束時,釋放掉管道佔用的文件資源。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
intfd[2]; /* 管道的文件描述符數組 */
char str[256];
if((pipe(fd)) < 0)
{
printf("create the pipe failed!\n");
exit(1); /*pipe出錯退出*/
}
write(fd[1],"create the pipe successfully!\n", 31 ); /*向管道寫入端寫入數據*/
read(fd[0],str, sizeof(str) ); /*從管道讀出端讀出數據*/
printf("%s", str );
printf("pipe file descriptors are%d,%d \n", fd[0], fd[1]) ;
close(fd[0]); /* 關閉管道的讀出端文件描述符*/
close(fd[1]); /* 關閉管道的寫入端文件描述符*/
return 0;
}
四、管道的讀寫
可以使用read和write函數對管道進行讀寫,需要注意的是,管道的兩段式固定了的,即管道的讀出端只能用於讀取數據,管道的寫入端只能用於寫入數據,一般的文件函數都可以用於管道,如:close read write
管道的寫入規則:向管道中寫入時,管道緩衝區一旦有空閒區域,寫進程就立即試圖向管道中寫入數據,如果讀進程不讀走管道緩衝區的數據,寫操作會一直被阻塞。單獨一個進程操作一個管道時沒有意義的,管道應用一般是在父子進程或者兄弟進程之間。
如果要建立一個父進程到子進程的管道,可以先調用pipe函數緊接着調用fork函數,由於子進程自動繼承父進程的數據段,則父子進程同時擁有管道的所有權。當想要實現父進程到子進程的數據通道,那麼就要在父進程中關閉通道的讀出端,在子進程中關閉通道的寫入端。反之,在父進程中關閉通道的寫入端,在子進程中關閉通道的讀出端。總之使用pipe和fork組合,可以構造出所有的父進程與子進程,或子進程到兄弟進程的管道。
程序二關於管道在父子進程中的應用示例,先使用pipe函數建立通道,在使用fork函數創建子進程,在父子進程中維護管道的數據流動方向,並在父進程中項子進程附送消息,在進程中接收消息並在輸出到標準輸出。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <limits.h>
#define BUFSIZE PIPE_BUF /* PIPE_BUF:管道默認一次性讀寫的數據長度*/
void err_quit(char *msg)
{
printf(msg);
exit(1);
}
int main(void)
{
int fd[2];
charbuf[BUFSIZE] = "hello my child!\n"; /*寫入管道的緩衝區*/
pid_t pid;
int len;
if((pipe(fd)) < 0) /*創建管道*/
{
err_quit("pipe failed\n");
}
if ((pid =fork()) < 0) /*創建一個子進程*/
{
err_quit("fork failed\n");
}
else if (pid> 0)
{
close (fd[0] ); /*父進程中關閉管道的讀出端*/
write(fd[1], buf, strlen(buf)); /*父進程向管道寫入數據*/
exit(0);
}
else
{
close (fd[1] ); /*子進程關閉管道的寫入端*/
len =read (fd[0], buf, BUFSIZE); /*子進程從管道中讀出數據*/
if (len< 0)
{
err_quit ("process failed when read a pipe\n");
}
else
{
write(STDOUT_FILENO, buf, len); /*輸出到標準輸出*/
}
exit(0);
}
}
程序三是管道在兄弟進程間的應用示例,在父進程中創建管道,並使用fork函數創建兩個子進程,在第一個子進程中發送消息到第二個子進程,在第二個子進程中讀出消息並處理。在父進程中,什麼都不做,需要關閉通道的兩端。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <limits.h>
#define BUFSIZE PIPE_BUF /* PIPE_BUF:管道默認一次性讀寫的數據長度*/
void err_quit(char * msg)
{
printf ( msg);
exit(1);
}
int main(void)
{
int fd[2];
charbuf[BUFSIZE] = "hello my brother!\n"; /* 緩衝區 */
pid_t pid;
int len;
if ((pipe(fd)) < 0 ) /*創建管道*/
{
err_quit("pipe failed\n");
}
if ( (pid =fork()) < 0 ) /*創建第一個子進程*/
{
err_quit("fork failed\n");
}
else if (pid == 0 ) /*子進程中*/
{
close (fd[0] ); /*關閉不使用的文件描述符*/
write(fd[1], buf, strlen(buf)); /*寫入消息*/
exit(0);
}
if ( (pid =fork()) < 0 ) /*創建第二個子進程*/
{
err_quit("fork failed\n");
}
else if (pid > 0 ) /*父進程中*/
{
close (fd[0] );
close (fd[1] );
exit ( 0);
}
else /*第二個子進程中*/
{
close (fd[1] ); /*關閉不使用的文件描述符*/
len =read (fd[0], buf, BUFSIZE); /*讀取消息*/
write(STDOUT_FILENO, buf, len); /*將消息輸出到標準輸出*/
exit(0);
}
return 0;
}
在上述代碼中,父進程分別建立了兩個子進程,在子進程中關閉了管道的讀出段,在另一個子進程中關閉了管道的寫入端,在父進程中關閉了通道的兩段。在程序中父進程在創建了第一個子進程時沒有關閉管道兩端,而是在創建了第二進程之後才關閉了兩端,這是爲了在創建第二個子進程時,子進程可以繼承完整的通道,而不是已經關閉了兩段的通道。