無名管道和有名管道的概念與實現

wzm10455發表於2013-01-16


1.管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;

2.只能用於父子程式或者兄弟程式之間(具有親緣關係的程式);

3.單獨構成一種獨立的檔案系統:管道對於管道兩端的程式而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。

4.資料的讀出和寫入:一個程式向管道中寫的內容被管道另一端的程式讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。

5.從管道中讀取資料:

如果管道的寫端不存在,則認為已經讀到了資料的末尾,讀函式返回的讀出位元組數為0

當管道的寫端存在時,如果請求的位元組數目大於PIPE_BUF,則返回管道中現有的資料位元組數,如果請求的位元組數目不大於PIPE_BUF,則返回管道中現有資料位元組數(此時,管道中資料量小於請求的資料量);或者返回請求的位元組數(此時,管道中資料量不小於請求的資料量)。注:(PIPE_BUFinclude/linux/limits.h中定義,不同的核心版本可能會有所不同。Posix.1要求PIPE_BUF至少為512位元組,red hat 7.2中為4096)。

6.向管道中寫入資料:

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

注:只有在管道的讀端存在時,向管道中寫入資料才有意義。否則,向管道中寫入資料的程式將收到核心傳來的SIFPIPE訊號,應用程式可以處理該訊號,也可以忽略(預設動作則是應用程式終止)。

7。管道的主要侷限性正體現在它的特點上:

只支援單向資料流;

這個實驗建立了一個管道,並且實現了讀寫的操作

/*
 ============================================================================
 Name        : pipe.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main(void) {
	int fd[2];
	int ret;
	char p[20];
	char *buf="hello!,pipe";
	ret=pipe(fd);//將陣列名傳進去
	//p=buf;這個是錯的,buf是一個變數,p[20]是變數,但是p是常量,不能被修改
	//buf=p;這個是對的
	if(ret==-1)
	{
		perror("pipe error");
		exit(0);
	}
	else
	{
		int writeno=write(fd[1],buf,strlen(buf));//接受一下返回值,表示讀了多少個數字
		if(writeno==-1)
		{
			perror("write error");
			exit(1);//這時最好不要用負數,因為會影響到sataus對於負數他是用補碼儲存的負數
		}
		int readno=read(fd[0],p,20);
		if(readno==-1)
		{
			perror("read error");
			exit(1);
		}
		p[readno]='\0';
		printf("read pipe:%s\n",p);
		exit(0);
	}

	return EXIT_SUCCESS;
}
結果:read pipe:hello!,pipe

只能用於具有親緣關係的程式之間;

沒有名字;

管道的緩衝區是有限的(管道制存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小);

管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,比如多少位元組算作一個訊息(或命令、或記錄)等等;

管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程式間通訊

建立管道的方式:

int  fd[2]

pipe(fd);

這裡的pipe函式的返回值:當成功時返回0,失敗時返回1,fd[0]代表這讀端,fd[1]代表著寫端

8.無名管道要注意的幾個問題

(1):交換管道的讀寫端,也就是將fd[0]換成fd[1],執行程式是會出錯的,因為,讀端只有讀許可權,寫端只有寫許可權,一旦調換,許可權將不允許,從而出現錯誤

(2):關閉管道寫端,從讀端讀資料.read函式返回值為0,表示讀管道檔案尾。

(3):關閉管道讀端,從寫端寫資料。 write error: Broken pipe。

            此時出現管道破裂核心會向當前程式傳送SIGPIPE訊號,使用者程式對於此訊號的預設處理方式為終止當進 程。

tcp程式設計中:當通訊的一端將套接字關閉,此時核心會向呼叫write函式的程式傳送SIGPIPE訊號,使用者程式對於此程式的預設處理方式為終止當前程式。

(4):不關閉寫端,從讀端讀資料: 此時read阻塞,直到寫端有資料寫入時,read 函式返回

fcntl函式可以設定讀管道時不阻塞。

(5):不關閉讀端,直接寫資料:是可以寫入的

(6):多次寫入一次讀取。如果讀取的buf夠大,會將管道中所有的資料讀出。

(7):一次寫入(10)多次讀取(10)。這個是好只能讀出一次,第二次讀的時候管道中無資料,read函式阻塞


通過利用同步互斥的原理實現程式間的通訊:


/*
 ============================================================================
 Name        : child2ConnecByLock.c
 Author      :
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
int main(void) {
        int fd[2];
        char buf1[20],buf2[20];
        int ret=pipe(fd);
        pid_t pid,pid2;
        if(ret==-1)
        {
            perror("create pipe wrong!");
            exit(0);
        }
       pid=fork();
        if(pid==0)
        {

            lockf(fd[1],1,0);//鎖定對整個檔案的寫操作
            strcpy(buf2,"child peocess1");
            int writeno1=write(fd[1],buf2,20);
            if(writeno1==-1)
            {
                perror("child1 1 wrong!");
                exit(1);
            }
            sleep(1);
            lockf(fd[1],0,0);//鎖定對整個檔案的寫操作
            exit(1);
        }
        else if(pid>0)
        {
            pid2=fork();
           if(pid2==0)
            {
                    lockf(fd[1],1,0);
                    strcpy(buf2,"child process2");
                    int writeno2=write(fd[1],buf2,20);
                    if(writeno2==-1)
                    {
                        perror("child 2 wrong!");
                        exit(1);
                    }
                    sleep(3);
                    lockf(fd[1],0,0);
                   exit(1);
            }
           else if(pid2>0)
           {
                      wait(NULL);
                      int readno1=read(fd[0],buf1,20);
                       printf("readno1 = %d",readno1);    
                      buf1[readno1]='\0';
                      printf("%s\n",buf1);

                      wait(NULL);

                      int readno2=read(fd[0],buf2,20);

                     buf2[readno2]='\0';//
;本人這裡出現一個非常有意思的地方,當把這個操作刪除後,兩個程式依
                                //次輸出資訊,但是如果使用這個操作,雖然同樣出現了陣列的越界
printf("%s\n",buf2); exit(0); } } else { perror("carete child wrong!"); exit(1); } return EXIT_SUCCESS;}

有關popen和pclose

:

/*
 ============================================================================
 Name        : popen.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

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

int main(void) {
	FILE *fp=NULL;
	char buf[100];
	if((fp=popen("ls -l","r"))==NULL)//
	{
		perror("popen wrong");
		exit(1);
	}
	while(fgets(buf,100,fp)!=NULL)
	{
		printf("%s\n",buf);
	}



   if((fp=popen("cat >text","w"))==NULL)
   {
	   perror("popen wrong");
   	exit(1);
   }
   fputs("helloworld!",fp);
	pclose(fp);

	//cat就是把標準輸入 輸出到標準輸出
	//cat >txt,將標準輸入輸入到txt檔案裡面去
} 
   
   
   
   
   
   
   
   
   
   



相關文章