天天看點

Linux - 管道 程序間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

程序間通信-管道

一、管道簡述

  • 管道是Unix中最古老的

    程序間通信

    的形式。
  • 我們把從一個程序連接配接到另一個程序的一個資料流稱為一個“

    管道

  • 我們通常把是把一個程序的

    輸出

    連接配接或“

    管接

    ”(經過管道來連接配接)到另一個程序的

    輸入

  • 管道實作

    通信原理

    • 每個程序各自有不同的使用者位址空間,任何一個程序的全局變量在另一個程序中都看不 到,是以程序之間要交換資料必須通過核心,在核心中開辟一塊緩沖區,程序1把資料從使用者空間拷到核心緩沖區,程序2再從核心緩沖區把資料讀走,核心提供的這種機制稱為程序間通信(IPC,InterProcess Communication)。
    • Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

二、在shell中使用管道

  • 連結shell指令

    :把一個程序的輸出直接饋入另一個的輸入,指令格式如下
command1 | command2 | command3
           

操作符是:”

|

”,它隻能處理經由前面一個指令傳出的

正确輸出資訊

,對

錯誤資訊

沒有直接處理能力。然後,

傳遞

給下一個指令,作為

标準的輸入

.

管理指令的輸出說明:

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

【指令1】 正确輸出,作為 【指令2】的輸入 然後**【指令2】的輸出作為【指令3】的輸入 ,【指令3】輸出就會直接顯示在螢幕**上面了。

通過管道之後【指令1】和【指令2】的正确輸出不顯示在螢幕上面

【提醒注意】:

  1. 管道指令隻處理前一個指令正确輸出,不處理錯誤輸出;
  2. 管道指令右邊指令,必須能夠接收标準輸入流指令才行;

舉例應用:

  1. 讀出logcat.log檔案的内容,通過管道轉發給grep作為輸入内容
cat logcat.log | grep –n ‘ActivitManager’
           
  1. who|grep tty|wc –l 顯示從控制台登入的人數
who|grep tty|wc –l 
           
Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

三、pipe管道

3.1- pipe函數說明

管道是一種最基本的IPC機制,可由pipe函數建立:

#include <unistd.h>
int pipe(int filedes[2]);
           
  • pipe管道

    作用于

    有血緣關系

    的程序之間,通過

    fork

    來傳遞
  • 調用

    pipe函數

    時在

    核心

    中開辟一塊

    緩沖區(稱為管道)

    用于通信,它有一個讀端一個

    寫端,然後通過

    filedes參數

    傳出給使用者程式兩個

    檔案描述符

    ,filedes[0]指向管道的讀

    端,filedes[1]指向管道的寫端(很好記,就像0是标準輸入1是标準輸出一樣)。是以管道 在使用者程式看起來就像一個打開的檔案,通過

    read(filedes[0]);

    或者

    write(filedes[1]);

    向這個檔案讀寫資料其實是在讀寫核心緩沖區。
  • pipe函數

    調用成功傳回 ,調用失敗傳回

    -1

3.2- pipe函數實作通信的原理

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道
  • 1.

    父程序

    調用pipe開辟管道,得到兩個

    檔案描述符

    指向

    管道的兩端

  • 2.父程序調用

    fork

    建立

    子程序

    ,那麼子程序也有兩個檔案描述符指向同一

    管道

  • 3.

    父程序關閉管道讀端,子程序關閉管道寫端

    。父程序可以往管道裡寫,子程序可以從管道裡讀,管道是用

    環形隊列

    實作的,資料從寫端流入從讀端流出,這樣就實作了

    程序間通信

3.3-應用示例: 父程序寫,子程序讀

#include <unistd.h>
#include <stdio.h>
int main()
{
	int fd[2];
	pid_t pid;
	char str_write[1024] = "hello, I am content.";
	char str_read[1024] = {0};

	//建立管道,參數fd作為要傳出去的兩個檔案描述符 
	if(pipe(fd) < 0)
	{
		//建立管道失敗
		perror("pipe");
		exit(1);	
	}
	
	//建立子程序 
	pid = fork();
	
	//fd[0] 讀端 , fd[1] 寫端
	
	//父程序 
	if(pid > 0)
{
		close(fd[0]);//父程序 關閉讀端 
		write(fd[1],str_write,strlen(str_write));
		wait(NULL);//立即阻塞父程序,等待子程序退出 
	}
	//子程序 
	else if (pid == 0)
	{
		int len;
		close(fd[1]);//子程序 關閉寫端 
		len = read(fd[0],str_read,sizeof(str_read));
		write(STDOUT_FILENO,str_read,len);//把str_read輸出到終端 
	 } 
	
	return 0;
}
           

3.4- 管道讀寫規則

  • 如果試圖

    從管道寫端讀取資料

    ,或者向管道讀端寫入資料都将導緻錯誤發生
  • 沒有資料可讀時

    ,read調用就會阻塞,即程序暫停執行,一直等到有資料來到為止。

    如果管道的

    另一端已經被關閉

    ,也就是沒有程序打開這個管道并向它寫資料時,read調用就會阻塞
  • 如果管道的

    寫端不存在

    ,則認為已經讀到了資料的末尾,讀函數傳回的讀出位元組數為0;
  • 當管道的

    寫端存在

    時,如果請求的位元組數目大于PIPE_BUF,則傳回管道中現有的資料位元組數,如果請求的位元組數目不大于PIPE_BUF,則傳回管道中現有資料位元組數(此時,管道中資料量小于請求的資料量);或者傳回請求的位元組數(此時,管道中資料量不小于請求的資料量)。
  • 向管道中

    寫入資料

    時,linux将不保證寫入的

    原子性

    ,管道緩沖區一有空閑區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀走管道緩沖區中的資料,那麼寫操作将一直阻塞。

四、fifo有名管道

4.1-fifo有名管道簡述

  • 匿名管道

    應用的一個限制就是隻能在具有

    共同祖先

    (具有

    親緣關系

    )的程序間通信。

    如果我們想在

    不相關

    的程序之間交換資料,可以使用

    FIFO檔案

    來做這項工作,它經常被稱為

    命名管道

  • 命名管道可以從指令行上建立,指令行方法是使用下面這個指令:
    • $ mkfifo filename
                 
  • 命名管道也可以從程式裡建立,相關函數有:
  • int mkfifo(const char *filename,mode_t mode);
     	```
               
  • mkfifo

    建立的

    FIFO檔案

    的檔案屬性的第一位是

    p

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

4.2- FIFO的打開規則:

  • 如果目前打開操作是為

    而打開FIFO時,若已經有相應程序為

    而打開該FIFO,則目前打開操作将成功傳回;否則,可能

    阻塞直到有相應程序為寫而打開該FIFO

    (目前打開操作設定了阻塞标志);或者,成功傳回(目前打開操作沒有設定阻塞标志)。
  • 如果目前打開操作是為

    而打開FIFO時,如果已經有相應程序為

    而打開該FIFO,則目前打開操作将成功傳回;否則,

    可能阻塞直到有相應程序為讀而打開該FIFO

    (目前打開操作設定了阻塞标志);或者,傳回ENXIO錯誤(目前打開操作沒有設定阻塞标志)。

4.3-應用示例

用來寫FIFO檔案:fifo_w.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
// write
void sys_err(char *str,int exitno)
{
	perror(str);
	exit(exitno);
}

int main(int argc, char*argv[])
{
	int fd;
	char buf[1024] = "hello world\n";
	if(argc < 2)
	{
		printf("enter fifoname\n");
		exit(1);
	}
	fd = open(argv[1],O_WRONLY);
	if(fd<0)
	{
		sys_err("open",1);
	}
	write(fd,buf,strlen(buf));
	close(fd);
	return 0;
}
           

用來讀FIFO檔案:fifo_r.c

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

//read

void sys_err(char *str,int exitno)
{
        perror(str);
        exit(exitno);
}

int main(int argc, char*argv[])
{
        int fd;
        char buf[1024];
        int len;
	 if(argc < 2)
         {
                printf("enter fifoname\n");
                exit(1);
         }
        fd = open(argv[1],O_RDONLY);
        if(fd<0)
         {
                sys_err("open",1);
         }
        len= read(fd,buf,sizeof(buf));
        write(STDOUT_FILENO,buf,len);

	 close(fd);
        return 0;
}

           

終端上測試:

1.建立FIOF檔案

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

2.編譯檔案

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

3.在

終端1

上執行

fifo_r

(用來讀 建立的 fifo1檔案中的資料)

但是現在沒有程序為

而打開該fifo1,是以終端1

被阻塞

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

4.打開終端2,執行

fifo_w

(用來向 建立的 fifo1檔案中寫入資料)

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

5.此時切換回

終端1

,可以看到已經讀到資料

Linux - 管道 程式間通信-管道 一、管道簡述二、在shell中使用管道三、pipe管道四、fifo有名管道

繼續閱讀