1. 程式人生 > >Linux程式設計學習筆記----程序間通訊——管道

Linux程式設計學習筆記----程序間通訊——管道

程序通訊概述

在Linux系統中,程序是一個獨立的資源管理單元,但是獨立而不孤立,他們需要之間的通訊,因此便需要一個程序間資料傳遞、非同步、同步的機制,這個機制顯然需要由OS來完成管理和維護。如下:

1、同一主機程序間資料互動機制:無名管道(PIPE),有名管道(FIFO),訊息佇列(Message Queue)和共享記憶體(Share Memory)。無名管道多用於親緣關係的程序間通訊,但是管道位單向,多程序使用一個同一個管道導致交叉讀寫。訊息佇列可以實現同主機上任意多的程序間通訊,但是訊息佇列可存放的資料量有限,用與少量的資料傳遞。共享記憶體可以實現同主機任意程序間的大量資料傳遞,但因為共享資料空間訪問時存在競爭問題。

2、同主機程序間同步機制:訊號量(semaphore)

3、同主機程序間非同步機制:訊號(signal)

4、網路主機間資料互動機制:套介面(Socket)

本節主要記錄UNIX程序間通訊機制:PIPE,FIFO。

What's 管道?

管道是Linux支援的最初Unix IPC形式之一,具有以下特點:

  • 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;
  • 只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);
  • 單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。
  • 資料的讀出和寫入:一個程序向管道中寫的內容被管道另一端的程序讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。

管道的實現機制:

    管道是由核心管理的一個緩衝區,相當於我們放入記憶體中的一個紙條。管道的一端連線一個程序的輸出。這個程序會向管道中放入資訊。管道的另一端連線一個程序的輸入,這個程序取出被放入管道的資訊。一個緩衝區不需要很大,它被設計成為環形的資料結構,以便管道可以被迴圈利用。當管道中沒有資訊的話,從管道中讀取的程序會等待,直到另一端的程序放入資訊。當管道被放滿資訊的時候,嘗試放入資訊的程序會等待,直到另一端的程序取出資訊。當兩個程序都終結的時候,管道也自動消失。


程序間通訊——PIPE

使用管道的示例:rpm -qa | grep telnet

使用管道”|“可以將兩個命令連線起來,從而改變標準的輸入輸出方式。在下面的shell命令中,命令”rpm-qa“ 的輸出作為telnet的輸入。連線輸入輸出的中間裝置即為一個管道檔案,因此,使用管道可以將一個命令的輸出作為一命令的輸入這種管道是臨時的,命令執行完成後,將自動消失,這類管道成為無名管道PIPE。

PIPE 和普通檔案有著很大的區別,首先在程序通訊的兩端退出後,管道將自動消失並釋放核心資源,不能像普通檔案那樣儲存大量的資訊。

PIPE的基本操作

建立PIPE

包含標頭檔案<unistd.h>

功能:建立一無名管道

原型:

int pipe(int fd[2]);

引數:

fd:檔案描述符陣列,其中fd[0]表示讀端, fd[1]表示寫端

返回值:成功返回0,失敗返回錯誤程式碼

man幫助說明:

DESCRIPTION       

       pipe() creates a pipe, a unidirectional data channel that can be used
       for interprocess communication.  The array pipefd is used to return
       two file descriptors referring to the ends of the pipe.  pipefd[0]
       refers to the read end of the pipe.  pipefd[1] refers to the write
       end of the pipe.  Data written to the write end of the pipe is
       buffered by the kernel until it is read from the read end of the
       pipe.  For further details, see pipe(7).

讀寫PIPE

任何程序讀寫PIPE都要確定還有一個程序(這個程序可以是自己),該程序以寫讀的方式(讀對應寫,寫對應讀)訪問管道(即可以操作相應的檔案描述符)。讀寫管道使用的系統呼叫是write和read,兩者都預設以阻塞的方式讀寫管道,如果要修改這兩個函式的行為可以通過fcntl函式實現。

 用 pipe() 建立的管道兩端處於同一個程序中,由於管道主要是用於在不同的程序間通訊的,因此,在實際應用中沒有太大意義。實際上,通常先是建立一個管道,再呼叫fork()函式建立一個子程序,該子程序會繼承父程序所建立的管道,這時,父子程序管道的檔案描述符對應關係如下圖

      

   此時的關係看似非常複雜,實際上卻已經給不同程序之間的讀寫創造了很好的條件。父子程序分別擁有自己的讀寫通道,為了實現父子程序之間的讀寫,只需把無關的讀端或寫端的檔案描述符關閉即可。例如,圖4中,將父程序的寫端fd[1]和子程序的讀端fd[0]關閉,則父子程序之間就建立起一條“子程序寫入父程序讀取”的通道。   同樣,也可以將父程序的讀端fd[0]和子程序的寫端fd[1]關閉,則父子程序之間就建立起一條“父程序寫入子程序讀取”的通道

   

   另外,父程序還可以建立多個子程序,各個子程序都繼承了相應的fd[0]和fd[1],此時,只需要關閉相應的埠就可以建立各子程序之間的的通道。

管道讀寫注意點

   ●  只有在管道的讀端存在時,向管道寫入資料才有意義。否則,向管道寫入資料的程序將收到核心傳來的 SIGPIPE 訊號(通常為 Broken pipe錯誤)。

   ●  向管道寫入資料時,Linux將不保證寫入的原子性,管道緩衝區一有空閒區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀取管道緩衝區中的資料,那麼寫程序將會一直阻塞。

   ●  父子程序在執行時,它們的先後次序並不能保證。因此,為了保證父子程序已經關閉了相應的檔案描述符,可在兩個程序中呼叫 sleep()函式。當然,這種呼叫不是很好的解決方法,以後我會用程序之間的同步與互斥機制來修改它的!

利用管道進行父子程序間資料傳輸
示例一:子程序向管道中寫資料,父程序從管道中讀出資料
/*************************************************************************
	> File Name: fathson.c
	> Author:SuooL 
	> Mail:[email protected] 
	> Created Time: 2014年08月08日 星期五 20時46分17秒
 ************************************************************************/

#include<stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> 

int main(void)
{
    int fds[2];
    if(pipe(fds) == -1){
        perror("pipe error");
        exit(EXIT_FAILURE);
    }
    pid_t pid;
    pid = fork();
    if(pid == -1){
        perror("fork error");
        exit(EXIT_FAILURE);
    }
    if(pid == 0){
        close(fds[0]);//子程序關閉讀端
        write(fds[1],"hello",5);
        exit(EXIT_SUCCESS);
    }

    close(fds[1]);//父程序關閉寫端
    char buf[10] = {0};
    read(fds[0],buf,10);
    printf("receive datas = %s\n",buf);
    return 0;
}

結果:


示例二:利用管道實現ls |wc –w功能

/*************************************************************************
	> File Name: ls-wc.c
	> Author:SuooL 
	> Mail:[email protected] 
	> Created Time: 2014年08月08日 星期五 20時53分04秒
	> Description: 使用管道實現ls| wc -w命令
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> 

int main(void)
{
    int fds[2];
    if(pipe(fds) == -1){
        perror("pipe error");
        exit(EXIT_FAILURE);
    }
    pid_t pid;
    pid = fork();
    if(pid == -1){
        perror("fork error");
        exit(EXIT_FAILURE);
    }
    if(pid == 0){
        
        dup2(fds[1],STDOUT_FILENO);//複製檔案描述符且指定新複製的fd為標準輸出
        close(fds[0]);//子程序關閉讀端
        close(fds[1]);
        execlp("ls","ls",NULL);
        fprintf(stderr,"exec error\n");
        exit(EXIT_FAILURE);
    }

    dup2(fds[0],STDIN_FILENO);
    close(fds[1]);//父程序關閉寫端
    close(fds[0]);
    execlp("wc","wc","-w",NULL);
    fprintf(stderr, "error execute wc\n");
    exit(EXIT_FAILURE);
}

檔案描述符重定向

1、shell重定向操作

2、重定向程式設計

一般的輸入輸出函式都會預設向指定的文教描述符的檔案讀寫。因此,重定向操作實際上是關閉某個標準I/O 裝置(檔案描述符0,1,2),而將另外一個開啟的普通檔案描述符設定為0,1,2.

輸入重定向:關閉標準輸入裝置,開啟或複製普通檔案,將其檔案描述符設定為0,

輸出重定向,錯誤輸出重定向同理。

使用dup和dup2函式可以實現檔案描述符的複製操作。

函式具體的說明參見網路說明文件。

下面是一個使用dup2函式將輸出重定向到某檔案的示例:

/*************************************************************************
	> File Name: dup_exp.c
	> Author:SuooL 
	> Mail:[email protected] 
	> Created Time: 2014年08月08日 星期五 21時15分31秒
	> Description: 使用dup2實現輸出重定向
 ************************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#define BUFFER_SIZE 1024

int main(int argc, char *argv[])
{
	int fd;
	char buffer[BUFFER_SIZE];
	
	if(argc != 2)
	{
		fprintf(stderr,"Usage:%s utfilename/n/a\n",argv[0]);
		exit(EXIT_FAILURE);
	}
	// 開啟重定向檔案
	if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR)) == -1)
	{
		fprintf(stderr,"Open %s Error:%s/n/a\n",argv[1],strerror(errno));
		exit(EXIT_FAILURE);
	}
	if(dup2(fd,fileno(stdout)) == -1) // 重定向裝置
	{
		fprintf(stderr,"Redirect standard out error:%s/n/a\n",strerror(errno));
		exit(EXIT_FAILURE);
	}
	fprintf(stderr,"Now,please input string\n");
	fprintf(stderr,"(to quit use CTRL+D)/n\n");
	while(1)
	{
		fgets(buffer,BUFFER_SIZE,stdin);   // 從標準輸入讀取
		if(feof(stdin))
			break;
		// 寫資料,重定向到檔案
		write(fileno(stdout),buffer,strlen(buffer));
	}
	exit(EXIT_SUCCESS);
	return 0;
}

實現who|sort命令

即是使用無名管道將執行who命令的程序與執行sort命令的程序聯絡一起,將登陸的使用者的資訊按排序輸出。

需要:

系統建立一個PIPE,在執行who命令的程序將輸出重定向到PIPE的寫端,而在執行sort命令的程序將輸入重定向到管道的讀端,即是用管道將sort的輸入連線到who的輸出。

步驟:

1、主程序建立一個管道,顯然主程序可以訪問管道的兩端。

2、主程序建立兩個子程序分別執行who和sort命令。兩個子程序都繼承了父程序開啟的檔案描述符。

3、關閉每個程序與管道無關聯絡。

4、在兩個子程序中呼叫execX 函式,執行who和sort命令。程式碼如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
	int fds[2];
	if(pipe(fds)==-1)
	{
		perror("pipe");
		exit(EXIT_FAILURE);
	}
	if (fork()== 0) 
	{
		char buf[128];
		dup2(fds[0], 0);
		close(fds[1]);				//must include ,or block
		execlp("sort", "sort", (char *)0);
		//execlp("cat", "cat", (char *)0);
	}
	else 
	{
		if(fork() == 0) 
		{
			dup2(fds[1], 1);
			close(fds[0]);
			execlp("who", "who", (char *)0);
		}
		else 
		{
			close(fds[0]);
			close(fds[1]);
			 wait(NULL);
			 wait(NULL);
		}
	}
	return 0;
}

程序間通訊——FIFO

有名管道相關的關鍵概念

管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程序間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。

FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即使與FIFO的建立程序不存在親緣關係的程序,只要可以訪問該路徑,就能夠彼此通過FIFO相互通訊(能夠訪問該路徑的程序以及FIFO的建立程序之間),因此,通過FIFO不相關的程序也能交換資料。

值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回資料,對它們的寫則把資料新增到末尾。

它們不支援諸如lseek()等檔案定位操作。

有名管道的基本操作

 有名管道的建立

使用mkfifo()函式來建立

包含標頭檔案:

#include <sys/types.h>

#include <sys/stat.h>

函式原型:

int mkfifo(const char * pathname, mode_t mode)

引數:

pathname是一個普通的路徑名,也就是建立後FIFO的名字;

mode指出FIFO檔案的操作許可權,與開啟普通檔案的open()函式中的mode 引數相同。 

常用的mode:

O_CREAT:如果檔案不存在,則建立之;

O_EXCL:確保此次呼叫會建立檔案;

如果mkfifo的第一個引數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的呼叫程式碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那麼只要呼叫開啟FIFO的函式就可以了。一般檔案的I/O函式都可以用於FIFO,如close、read、write等等。

返回值:

建立成功,則返回0;

失敗則返回-1;

錯誤型別:

 EEXIST 路徑名已存在(最常用的判斷)

ENAMETOOLONG/ ENOENT/ENOSPC/ ENOTDIR/ EROFS

有名管道的開啟規則

有名管道比管道多了一個開啟操作:open(),以供讀取或者寫入

包含標頭檔案:

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>

函式原型:

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

引數:

1> pathname:指出檔名

2> flags:至少包含一下一種訪問許可權:O_RDONLY ( read only ),  O_WRONLY( write only ),  or  O_RDWR( read/write ).

falgs需要與一些檔案狀態標識進行“ 或 ”操作

FIFO常用的檔案狀態標識:O_NONBLOCK or O_NDELAY

即表示非阻塞(如果不設定,則預設為阻塞),含義看下文解釋。

3> mode:一般取0即可;

返回值:

如果開啟成功,則返回一個新的檔案描述符;

如果開啟失敗,則返回-1,並設定響應的errno

FIFO常用到的錯誤:

ENXIO:當設定flags為O_NONBLOCK  |  O_WRONLY時,檔案為FIFO,並且程序沒有檔案開啟以供寫;

FIFO的開啟規則:

如果當前開啟操作是為讀而開啟FIFO時,若已經有相應程序為寫而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程序為寫而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,成功返回(當前開啟操作沒有設定阻塞標誌)。

如果當前開啟操作是為寫而開啟FIFO時,如果已經有相應程序為讀而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程序為讀而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,返回ENXIO錯誤(當前開啟操作沒有設定阻塞標誌)。

對FIFO開啟規則的驗證(主要驗證寫開啟對讀開啟的依賴性)

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #define FIFO_SERVER "/tmp/fifoserver"
  6. int handle_client(char*);  
  7. int main()  
  8. {  
  9.     int r_rd;  
  10.     int w_fd;  
  11.     pid_t pid;  
  12.     //建立FIFO,並檢查是否已有同名的FIFO檔案
  13.     if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))  
  14.     {  
  15.         printf("cannot create fifoserver\n");  
  16.     }  
  17.     //建立成功
  18.     handle_client(FIFO_SERVER);  
  19. }  
  20. int handle_client(char* arg)  
  21. {  
  22.     int ret;  
  23.     ret=w_open(arg);  
  24.     switch(ret)  
  25.     {  
  26.     case 0:  
  27.         {   
  28.             printf("open %s error\n",arg);  
  29.             printf("no process has the fifo open for reading\n");  
  30.             return -1;  
  31.         }  
  32.     case -1:  
  33.         {  
  34.             printf("something wrong with open the fifo except for ENXIO");  
  35.             return -1;  
  36.         }  
  37.     case 1:  
  38.         {  
  39.             printf("open server ok\n");  
  40.             return 1;  
  41.         }  
  42.     default:  
  43.         {  
  44.             printf("w_no_r return ----\n");  
  45.             return 0;  
  46.         }  
  47.     }  
  48.     unlink(FIFO_SERVER);  
  49. }  
  50. //返回0,表示開啟失敗,因為沒有檔案
  51. //返回-1,表示開啟失敗,但是因為別的原因
  52. //返回1,表示開啟成功
  53. int w_open(char*arg)  
  54. {  
  55.     if(open(arg,O_WRONLY|O_NONBLOCK,0)==-1)  
  56.     {   
  57.         if(errno==ENXIO)  
  58.         {  
  59.             return 0;  
  60.         }  
  61.         else
  62.         {  
  63.             return -1;  
  64.         }  
  65.     }  
  66.     return 1;  
  67. }  

有名管道的讀寫規則

從FIFO中讀取資料:

約定:

如果一個程序為了從FIFO中讀取資料而阻塞開啟FIFO(open()時不設定O_NONBLOCK ),那麼稱該程序內的讀操作為設定了阻塞標誌的讀操作

如果有程序寫開啟FIFO,且當前FIFO內沒有資料

        1> 則對於設定了阻塞標誌的讀操作來說,將一直阻塞

        2> 對於沒有設定阻塞標誌讀操作來說則返回-1,當前errno值為EAGAIN,提醒以後再試。

對於設定了阻塞標誌的讀操作說,造成阻塞的原因有兩種:

        1> 當前FIFO內有資料,但有其它程序在讀這些資料;

        2> FIFO內沒有資料。

解阻塞的原因則是FIFO中有新的資料寫入,不論信寫入資料量的大小,也不論讀操作請求多少資料量。

讀開啟的阻塞標誌只對本程序第一個讀操作施加作用,如果本程序內有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,其它將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有資料也一樣(此時,讀操作返回0)。

如果沒有程序寫開啟FIFO,則設定了阻塞標誌的讀操作會阻塞。

注:如果FIFO中有資料,則設定了阻塞標誌的讀操作不會因為FIFO中的位元組數小於請求讀的位元組數而阻塞,此時,讀操作會返回FIFO中現有的資料量。

使用read()方法從FIFO中讀取資料

包含標頭檔案:

#include <unistd.h>

函式原型:

ssize_t read(int fd, void *buf, size_t count);

功能:

從檔案描述符fd中讀取count位元組到buf中;

引數:

如果count==0,則函式返回0;如果count大於buf的size,結果不確定??

返回值:

如果讀取成功,則返回讀取的位元組數;

(當申請的位元組數多餘fd中可讀的位元組數時,返回的值小於count)

如果讀取失敗,則返回-1;

FIFO中可能出現的錯誤:

EAGAIN or EWOULDBLOCK:The  file  descriptor  fd refers to a socket and has been marked nonblocking   (O_NONBLOCK),   and   the   read   would    block.

向FIFO中寫入資料:

約定:如果一個程序為了向FIFO中寫入資料而阻塞開啟FIFO(open()時不設定O_NONBLOCK ),那麼稱該程序內的寫操作為設定了阻塞標誌的寫操作

對於設定了阻塞標誌的寫操作:

        1> 當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。

              如果此時管道空閒緩衝區不足以容納要寫入的位元組數,則進入睡眠,直到當緩衝區中能夠容納要寫入的位元組數時,才開始進行一次性寫操作。

        2> 當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性

              FIFO緩衝區一有空閒區域,寫程序就會試圖向管道寫入資料,寫操作在寫完所有請求寫的資料後返回。

對於沒有設定阻塞標誌的寫操作:

        1> 當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。在寫滿所有FIFO空閒緩衝區後,寫操作返回。(不保證所有資料都能寫入)

        2> 當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。

              如果當前FIFO空閒緩衝區能夠容納請求寫入的位元組數,寫完後成功返回;

              如果當前FIFO空閒緩衝區不能夠容納請求寫入的位元組數,則返回EAGAIN錯誤,提醒以後再寫;

要注意幾種不同情況下的區別!

使用write()方法向FIFO寫入資料;

包含標頭檔案:

#include <unistd.h>

函式原型:

ssize_t write(int fd, const void *buf, size_t count);

返回值:

如果成功,則返回寫入的位元組數;

如果失敗,返回-1,並設定相應的errno;

可能出現的錯誤:

EAGAIN or EWOULDBLOCK:The  file  descriptor  fd refers to a socket and has been marked nonblocking   (O_NONBLOCK),   and   the   write   would   block.

對FIFO讀寫規則的驗證:

下面提供了兩個對FIFO的讀寫程式,適當調節程式中的很少地方或者程式的命令列引數就可以對各種FIFO讀寫規則進行驗證。

程式1:寫FIFO的程式

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include "BYHFileOperation.h"
  9. #define FIFO_SERVER "/home/stuy1001593/fifoserver"
  10. void printMkfifoErrorInfo(int err);  
  11. int main()  
  12. {  
  13.     char w_buf[4*1024*2]={0};  
  14.     int real_wnum;  
  15.     //建立FIFO,並檢查路徑名是否存在
  16.     int nRet=mkfifo(FIFO_SERVER,O_CREAT|O_EXCL);  
  17.     if(nRet<0)  
  18.     {  
  19.         int tempErrno=errno;  
  20.         printMkfifoErrorInfo(tempErrno);  
  21.         if(errno==EEXIST)  
  22.         {  
  23.             printf("檔案%s已存在\n", FIFO_SERVER);  
  24.         }  
  25.         else
  26.         {  
  27.             exit(-1);  
  28.         }  
  29.     }  
  30.     //設定非阻塞標誌的只寫操作
  31.     //int open_flag=O_WRONLY|O_NONBLOCK;
  32.     int open_flag=O_WRONLY;  
  33.     int fd=BYHFileOperation::Open(FIFO_SERVER, open_flag);  
  34.     if(fd==-1)  
  35.     {  
  36.         if(errno==ENXIO)  
  37.         {  
  38.             printf("程序沒有檔案開啟,以供寫入\n");   
  39.         }  
  40.         exit(-1);  
  41.     }  
  42.     //寫入小於PIPE_BUFF的資料
  43.     real_wnum=write(fd, w_buf, 2048);  
  44.     if(real_wnum==-1)  
  45.     {  
  46.         if(errno==EAGAIN)  
  47.         {  
  48.             printf("寫入到FIFO中時發生錯誤,FIFO空閒快取區沒有足夠的空間,稍後再試\n");  
  49.         }  
  50.         exit(-1);  
  51.     }  
  52.     else
  53.     {  
  54.         printf("向FIFO空閒快取中寫入了%dByte\n",real_wnum);  
  55.     }  
  56.     //寫入大於PIPE_BUFF的資料
  57.     real_wnum=write(fd, w_buf, 5000);  
  58.     //5000用於測試寫入位元組大於4096時的非原子性
  59.     //real_wnum=write(fd,w_buf,4096);
  60.     //4096用於測試寫入位元組不大於4096時的原子性
  61.     if(real_wnum==-1)  
  62.     {  
  63.         if(errno==EAGAIN)  
  64.         {  
  65.             printf("寫入到FIFO中時發生錯誤,FIFO空閒快取區沒有足夠的空間,稍後再試\n");  
  66.         }  
  67.         exit(-1);  
  68.     }  
  69.     else
  70.     {  
  71.         printf("向FIFO空閒快取中寫入了%dByte\n",real_wnum);  
  72.     }  
  73. }  
程式2:與程式1一起測試寫FIFO的規則,第一個命令列引數是請求從FIFO讀出的位元組數
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #define FIFO_SERVER "/tmp/fifoserver"
  9. int main()  
  10. {  
  11.     char r_buf[4096*2]={0};  
  12.     //FIFO檔案描述符
  13.     int fd;  
  14.     int r_size=1024;  
  15.     int ret_size;  
  16.     //以非阻塞方式開啟FIFO Server(FIFO傳送者)
  17.     fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);  
  18.     //以阻塞方式開啟FIFO
  19.     //fd=open(FIFO_SERVER,O_RDONLY,0);
  20.     if(fd==-1)  
  21.     {  
  22.         printf("開啟FIFO檔案%s失敗\n", FIFO_SERVER);  
  23.         exit(0);   
  24.     }  
  25.     while(true)  
  26.     {  
  27.         //從FIFO中讀取到r_buf中
  28.         ret_size=read(fd, r_buf, r_size);  
  29.         //讀取失敗
  30.         if(ret_size==-1)  
  31.         {  
  32.             if(errno==EAGAIN)  
  33.             {  
  34.                 printf("FIFO快取中沒有資料可讀,請稍後再試\n");  
  35.                 sleep(1);  
  36.             }  
  37.         }  
  38.         printf("從FIFO快取中讀取%dBytes\n",ret_size);  
  39.         sleep(1);  
  40.     }  
  41.     pause();  
  42.     //刪除FIFO檔案
  43.     unlink(FIFO_SERVER);  
  44. }  

程式應用說明:

把讀程式編譯成兩個不同版本:

1>阻塞讀版本

2>非阻塞讀版本nbr

把寫程式編譯成兩個四個版本:

1> 非阻塞且請求寫的位元組數大於PIPE_BUF版本:nbwg

2> 非阻塞且請求寫的位元組數不大於PIPE_BUF版本:版本nbw

3> 阻塞且請求寫的位元組數大於PIPE_BUF版本:bwg

4> 阻塞且請求寫的位元組數不大於PIPE_BUF版本:版本bw

小結

FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關係的程序同樣可以採用先進先出的通訊機制進行通訊。

管道和FIFO的資料是位元組流,應用程式之間必須事先確定特定的傳輸"協議",採用傳播具有特定意義的訊息。

要靈活應用管道及FIFO,理解它們的讀寫規則是關鍵。

管道基本特點總結

1、管道是特殊型別的檔案,在滿足FIFO的原則條件下可能進行讀寫,但不能定位讀寫位置。

2、管道單向,要實現雙向則需要倆個管道。無名管道一般用於親緣關係的程序間通訊,而有名管道則以磁碟檔案的形式存在,可以實現本機任意兩個程序間的通訊

3、阻塞問題。無名管道必須顯示開啟,建立的時候直接返回檔案描述符,而在讀寫的時候需要確定對方的存在,即阻塞於讀寫位置,而有名管道則在開啟的時候確定對方是否存在,否則阻塞。(關於此會具體講述)

NEXT:

程序非同步訊號處理機制

System V 程序間通訊

相關推薦

Linux程式設計學習筆記----程序通訊——管道

程序通訊概述 在Linux系統中,程序是一個獨立的資源管理單元,但是獨立而不孤立,他們需要之間的通訊,因此便需要一個程序間資料傳遞、非同步、同步的機制,這個機制顯然需要由OS來完成管理和維護。如下: 1、同一主機程序間資料互動機制:無名管道(PIPE),有名管道(FIF

Linux程式設計學習筆記----System V程序通訊(訊號量)

關於System V Unix System V,是Unix作業系統眾多版本中的一支。它最初由AT&T開發,在1983年第一次釋出,因此也被稱為AT&T System V。一共發行了4個System V的主要版本:版本1、2、3和4。System V Rel

Linux(高階程式設計)5————程序通訊1(無名管道

程序通訊簡介: IPC(程序間通訊),前面我們學習了程序控制,瞭解程序間隔離的,各自有獨立的虛擬記憶體空間無法直接訪問對方的地址空間、程序排程和均衡則讓程序互不干擾地使用處理器資源。但有時需要多個程序相

Linux(高階程式設計)6————程序通訊2(命名管道

通過匿名管道學習,我們對管道有了一些瞭解,下面我們將進行下一步學習管道,這次來學習一下匿名管道,關於管道這裡就不在贅言了,直接上命名管道。 命名管道: 1.檔案系統可見。 2.命名管道是一類特殊型別檔

Linux學習筆記——程序通訊-檔案和檔案鎖

IPC(Inter-Process Communication,程序間通訊) 系統對檔案本身存在快取機制,使用檔案進行IPC的效率在某些多讀少寫的情況下並不低下 1.競爭條件(racing) 併發100個程序,約定好一個內容初值為0的檔案,每一個程序開

Linux(高階程式設計)9————程序通訊6(訊號量)

訊號量是是什麼? 訊號量是程序間通訊方式之一,用來實現程序間的同步與互斥。訊號量的原理是一種資料操作鎖的概念,它本身不具備資料交換的功能,而是通過控制其他通訊資源(如文字、外部裝置等)來實現程序間通訊。訊號量本身不具備資料傳輸的功能,他只是一種外部資源的標識。 訊號量的本質是:具有等待佇

Linux(高階程式設計)8————程序通訊4(共享記憶體)

共享記憶體是什麼? 因為程序之間是相互獨立的,他們有各自程序地址空間,那麼他們需要通訊時就要藉助核心來為他們建立橋樑,像之前我們瞭解的管道、訊息佇列就是核心做的工作來為程序間通訊架的橋樑。共享記憶體也是核心為程序間通訊駕的一座橋樑,只不過這座橋樑比其他橋樑更優,共享記憶體是核心為需要通訊

Linux(高階程式設計)9————程序通訊6(訊號1)

訊號是什麼? 訊號實質是一種軟中斷,用於通知程序發生了某些事件,實際上訊號也可以算作程序間通訊的一種方式,因為我們可在程序通過另一個程序傳送訊號,來告訴另一個程序發生什麼事。 這樣來講我們聽起來可能還會比較暈。 深入理解訊號: 在我們生活中其實就有訊號的

Linux講解 程序通訊 管道

  今天我們講解程序間的通訊,首先回顧一下程序的概念:程序是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。程式是指令、資料及其組織形式的描述,程

Linux下的程序概論與程式設計三(程序通訊的5種方式)

一、程序間通訊 1、IPC—-InterProcess Communication 每個程序各自有不同的使用者地址空間,任何一個程序的全域性變數在另一個程序中都看不到所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝區,程序1把資料從使用者

程序程式設計程序通訊-管道和訊息佇列

1.程序間通訊 Linux作為一種新興的作業系統,幾乎支援所有的Unix下常用的程序間通訊方法:管道、訊息佇列、共享記憶體、訊號量、套介面等等。 2.管道 管道是程序間通訊中最古老的方式,它包括無名管道(或者匿名管道)和有名管道兩種,前者用於父程序和

Linux 程序通訊 管道

管道 匿名管道:半雙工,資料單向流動,只能用與有親緣關係的程序間。 pipe_read_buf_small.c #include <unistd.h> #include <sys/t

Linux程序程式設計-之二-程序通訊(訊息佇列)

1.1         系統V訊息佇列 訊息佇列中的每個訊息都有如下的資料結構: struct msgbuf { long mtype;         // 訊息型別 char mtext[n];      // 訊息內容,n由使用者自己定義 }; 1.1.1      

33-程序通訊——管道

1. 什麼是管道   管道是unix系統最古老的IPC通訊方式了,適合於有血緣關係的程序之間完成資料傳輸,比如父子程序,兄弟程序。    管道允許一個數據流向另一個程序,管道中的資料流向是單向的。這樣程序可以通過檔案描述符1連線到管道寫入端,另一個程序通過檔案

程序通訊-管道通訊

兩個程序的通訊,每個程序各自有不同的地址空間,每個地址空間的資料資訊是獨立的,任何一個程序的全域性變數在另一個程序中都看不到。例如,父程序中有一個全域性變數a = 0;在子程序中改變a的值不會影響父程序中a值的結果,因為子程序所有的資料資訊都拷貝(寫時拷貝)自父程序,兩個程

程序通訊管道

一、常用的方式 (1) 管道 (2) System V (3) POSIX 二、目的 (1) 資料傳輸 (2) 資源共享 (3) 通知事件 (4) 程序控制 三、 本質 讓兩個不同的程序看到一份公共的資源 四、匿名管道 1. 簡單用法: 

程序通訊-管道(PIPE)和有名管道(FIFO)

1.2有名管道的建立 該函式的第一個引數是一個普通的路勁名,也就是建立後FIFO的名字。第二個引數與開啟普通檔案的open()函式中的mode引數相同。如果mkfifo的一個引數是一個已經存在路勁名時,會返回EEXIST錯誤,所以一般典型的呼叫程式碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,

linux 程式設計學習筆記--編譯多個檔案

把學的東西記下來,複習 假設有三個檔案: main.c   su.c su.h 內容分別為 #include <stdio.h>                                            int sum(int a,int b);  

程序通訊——管道,訊息佇列,共享記憶體

程序間通訊的本質是讓兩個不相干的程序看到同一份資源。這個資源是由作業系統提供的一個檔案。程序間通訊的目的:1.資料傳輸:一個程序需要將它 的資料傳送給另一個程序。2.資源共享:多個程序之間共享同樣的資源。3.通知事件:一個程序需要向另一個(組)程序傳送訊息,通知它們發生了

Linux訊息佇列實現程序通訊

什麼是訊息佇列: 訊息佇列提供了從一個程序向另一個程序傳送一個有型別資料塊的方法。用這種方法可以避免命名管道的同步和阻塞問題。訊息佇列是基於訊息的,而管道是基於位元組流的,並且訊息佇列的讀取不一定是先入先出。 訊息佇列的操作: 訊息佇列的建立或者獲取: int msgg