linux 進程通訊 管道

一、 管道概述及相關API應用

1.1 管道相關的關鍵概念

管道是Linux支持的最初Unix IPC形式之一,具備如下特色:node

  • 管道是半雙工的,數據只能向一個方向流動;須要雙方通訊時,須要創建起兩個管道;
  • 只能用於父子進程或者兄弟進程之間(具備親緣關係的進程);
  • 單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,而且只存在與內存中。
  • 數據的讀出和寫入:一個進程向管道中寫的內容被管道另外一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,而且每次都是從緩衝區的頭部讀出數據。

1.2管道的建立:

#include <unistd.h>
int pipe(int fd[2])

  

該函數建立的管道的兩端處於一個進程中間,在實際應用中沒有太大意義,所以,一個進程在由pipe()建立管道後,通常再fork一個子進程,而後經過管道實現父子進程間的通訊(所以也不難推出,只要兩個進程中存在親緣關係,這裏的親緣關係指的是具備共同的祖先,均可以採用管道方式來進行通訊)。linux

1.3管道的讀寫規則:

管道兩端可分別用描述字fd[0]以及fd[1]來描述,須要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其爲管道讀端;另外一端則只能用於寫,由描述字fd[1]來表示,稱其爲管道寫端。若是試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將致使錯誤發生。通常文件的I/O函數均可以用於管道,如close、read、write等等。函數

從管道中讀取數據:server

  • 若是管道的寫端不存在,則認爲已經讀到了數據的末尾,讀函數返回的讀出字節數爲0;
  • 當管道的寫端存在時,若是請求的字節數目大於PIPE_BUF,則返回管道中現有的數據字節數,若是請求的字節數目不大於PIPE_BUF,則返回管道中現有數據字節數(此時,管道中數據量小於請求的數據量);或者返回請求的字節數(此時,管道中數據量不小於請求的數據量)。注:(PIPE_BUF在include/linux/limits.h中定義,不一樣的內核版本可能會有所不一樣。Posix.1要求PIPE_BUF至少爲512字節,red hat 7.2中爲4096)。

關於管道的讀規則驗證:blog

/**************
 * readtest.c *
 **************/
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
main()
{
	int pipe_fd[2];
	pid_t pid;
	char r_buf[100];
	char w_buf[4];
	char* p_wbuf;
	int r_num;
	int cmd;
	
	memset(r_buf,0,sizeof(r_buf));
	memset(w_buf,0,sizeof(r_buf));
	p_wbuf=w_buf;
	if(pipe(pipe_fd)<0)
	{
		printf("pipe create error\n");
		return -1;
	}
	
	if((pid=fork())==0)
	{
		printf("\n");
		close(pipe_fd[1]);
		sleep(3);//確保父進程關閉寫端
	      r_num=read(pipe_fd[0],r_buf,100);
          printf(	"read num is %d   the data read from the pipe is %d\n",r_num,atoi(r_buf));
		
		close(pipe_fd[0]);
		exit();
	}
	else if(pid>0)
	{
	close(pipe_fd[0]);//read
	strcpy(w_buf,"111");
	if(write(pipe_fd[1],w_buf,4)!=-1)
		printf("parent write over\n");
	close(pipe_fd[1]);//write
		printf("parent close fd[1] over\n");
	sleep(10);
	}	
}
 /**************************************************
 * 程序輸出結果:
 * parent write over
 * parent close fd[1] over
 * read num is 4   the data read from the pipe is 111
 * 附加結論:
 * 管道寫端關閉後,寫入的數據將一直存在,直到讀出爲止.
 ****************************************************/

  

向管道中寫入數據:進程

  • 向管道中寫入數據時,linux將不保證寫入的原子性,管道緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據。若是讀進程不讀走管道緩衝區中的數據,那麼寫操做將一直阻塞。 
    注:只有在管道的讀端存在時,向管道中寫入數據纔有意義。不然,向管道中寫入數據的進程將收到內核傳來的SIFPIPE信號,應用程序能夠處理該信號,也能夠忽略(默認動做則是應用程序終止)。

對管道的寫規則的驗證1:寫端對讀端存在的依賴性ip

#include <unistd.h>
#include <sys/types.h>
main()
{
	int pipe_fd[2];
	pid_t pid;
	char r_buf[4];
	char* w_buf;
	int writenum;
	int cmd;
	
	memset(r_buf,0,sizeof(r_buf));
	if(pipe(pipe_fd)<0)
	{
		printf("pipe create error\n");
		return -1;
	}
	
	if((pid=fork())==0)
	{
		close(pipe_fd[0]);
		close(pipe_fd[1]);
		sleep(10);	
		exit();
	}
	else if(pid>0)
	{
	sleep(1);  //等待子進程完成關閉讀端的操做
	close(pipe_fd[0]);//write
	w_buf="111";
	if((writenum=write(pipe_fd[1],w_buf,4))==-1)
		printf("write to pipe error\n");
	else	
		printf("the bytes write to pipe is %d \n", writenum);
	
	close(pipe_fd[1]);
	}	
}

  

 

1.1 FIFO 命名管道

命名管道是一種特殊類型的文件,由於Linux中全部事物都是文件,它在文件系統中以文件名的形式存在。內存

在程序中,咱們能夠使用兩個不一樣的函數調用來創建管道:get

#include <sys/types.h>cmd

#include <sys/stat.h>

 

int mkfifo(const char *filename, mode_t mode);

int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );

 

1、實驗:使用FIFO實現進程間通訊

兩個獨立的程序:

1.      生產者程序,它在須要時建立管道,而後儘量快地向管道中寫入數據。

2.      消費者程序,它從FIFO中讀取數據並丟棄它們。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "../fifoname"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 5 )

int main()
{
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    
    int byte = 0;
    char buffer[BUFFER_SIZE + 1];
    
    if (-1 == access(FIFO_NAME, F_OK)) {
        res = mkfifo(FIFO_NAME, 0777);
        if (res != 0) {
            printf("error create fifo pipe\n");
            exit(EXIT_FAILURE);
        }
    }
    
    printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);
    
    if (-1 != pipe_fd) {
        while (byte < TEN_MEG) {
            res = write(pipe_fd, buffer, BUFFER_SIZE);
	    printf("write to pipe %d data! and buffsize if %d\n", res, BUFFER_SIZE);
            if (-1 == res) {
                printf("error write to pipe\n");
                exit(EXIT_FAILURE);
            }
            
            byte += res;
        }
        
        close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished\n", getpid());
    
}

 消費者:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "../fifoname"
#define BUFFER_SIZE PIPE_BUF

int main()
{
    
    int pipe_fd;
    
    int open_mode = O_RDONLY;
    
    int res;
    char buffer[BUFFER_SIZE + 1];
    int byte = 0;
    
    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);
    
    if (-1 != pipe_fd) {
        do
        {
            res = read(pipe_fd, buffer, BUFFER_SIZE);
	    printf("read from server %d bytes data!\n", res);
            byte += res;
	    sleep(1);
        }while (res > 0);
        
        close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished, %d bytes read\n", getpid(), byte);
    
    
    return 0;
    
}
相關文章
相關標籤/搜索