進程間通信---管道

文章模塊:

1.進程間通信的實質
2.管道
3.管道的特點
4.管道的四種情況
5.命名管道
6.匿名管道的代碼實現
7命名管道的代碼實現

一、進程間通信的實質

      每個進程都有各自不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到。所以進程之間要交換數據必須要通過內核,在內核中開闢一塊緩衝區,進程1把數據從用戶空間考到內核緩衝區,進程2載從內核緩衝區把數據讀走,內核提供的這種機制稱爲進程間通信。

如圖:

二、管道

管道是一種最基本的IPC機制,由pipe函數創建。

調用pipe函數,在內核中開闢一塊緩衝區(稱爲管道)用於通信,它有一個讀端和一個寫端,然後通過函數參數傳出給用戶程序兩個文件描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端。所以,管道在用戶程序看起來就像一個打開的文件。可通過read、write函數向文件讀寫數據。向這個文件讀寫數據其實是在讀寫內核緩衝區。pipe函數調用成功返回0,失敗返回-1。

pipe函數的調用過程:

1.父進程創建管道

2.創建一個子進程

3.父進程關閉讀端fd[0],子進程關閉寫端fd[1]


     父進程調用pipe函數開闢管道,得到兩個文件描述符指向管道的兩端。


    父進程調用fork創建一個子進程,子進程繼承父進程的文件描述符集,則子進程也有兩個文件描述符指向同一管道的兩端。


    父進程關閉管道讀端,子進程關閉管道寫端。父進程可以往管道里寫,子進程可以從管道里讀。管道是用環形隊列實現的,數據從寫端流入,從讀端流出。這樣就實現了進程間通信。

三、管道的特點

1.只能單向通信

2.匿名管道只適用於具有血緣關係的進程間通信

3.依賴於文件系統,生命週期隨進程

4.自帶同步功能,防止讀到垃圾數據,以互斥爲前提

5.基於無格式字節流

6.管道的緩衝區是有限的(管道存在於內存中,在管道創建時,爲緩衝區分配一個頁面大小)

下面解釋一下

爲什麼管道只能單向通信?

          來看下Linux的實現,數據只能單向移動的意思是FIFO,於是Linux實際中構建了一個循環隊列。具體的則是,在內核申請一個緩衝區,作爲pipe()操作中匿名管道的實體,緩衝區設置兩個指針,一個讀指針,一個寫指針。並保證讀指針向前移動不能超過寫指針,否則喚醒寫進稱並讓讀指針睡眠,直到讀滿需要的字節數。同理,讀指針向前移動也不能超過寫指針,否則喚醒寫進程並讓讀進程睡眠,直到寫滿要求的字節數。說白了,管道在操作系統內部就是一個環形隊列。

pipe()返回的兩個文件句柄最後指向的其實是一個inode,只不過是一個read only一個是write only。試想同時有兩個進程讀[或者寫,假設只有兩個進程]的後果。由於i_count會等於2--如果小於2則說明兩個進程同時關閉了寫句柄,因此會退出讀函數。此時兩個進程會分別認爲對方纔是寫者而反覆醒來,反覆監測,然而沒有數據,於是反覆睡眠。如果有多個進程,兩進程同時讀[或者寫],會造成數據混亂,因爲讀指針只有一個,而你不能保證讀寫的順序。

所以,想要使用管道完成雙向通信,只需要創建兩個管道就可以了。

爲什麼管道只適用於有血緣關係的進程間通信?

     因爲管道是通過文件描述符來控制數據讀寫的。所以只有血緣關係的進程纔會產生指向同一個管道的文件描述符。

管道容量是多少?

     用命令 ulimit -a可以查看,----->512Bytes*8 = 4096Bytes

     也可以用程序代碼驗證

四、管道的四種情況

1.讀端不讀了,但不關閉讀文件描述符,當管道寫滿時

如果指向管道讀端的文件描述符沒有關閉,(管道寫端的引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程從管道的寫端寫數據,那麼管道在被寫滿時再次write會阻塞,直到管道中有了空位置才寫入數據並返回。

2.寫端不寫了,但不關閉寫文件描述符,當管道中數據讀完

如果指向管道寫端的文件描述符沒有關閉,(管道寫端的引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會被阻塞,直到管道中有數據可讀了纔會讀取數據並返回。

3.寫端一直寫,讀端關閉讀文件描述符

如果所有指向管道讀端的文件描述符都關閉了,(管道寫端的引用計數等於0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終止。

4.讀端一直讀,寫端關閉寫文件描述符

如果所有指向管道寫端的文件描述符都關閉了,(管道寫端的引用計數等於0),而仍有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會返回0,就像讀到文件末尾一樣。

1、2體現了同步機制

五、命名管道 FIFO

可以用於兩個無血緣關係的進程間通信。命名管道和管道不通的在於它提供一個路徑名與之關聯,以FIFO的文件形式存儲於2文件系統中,命名管道是一個設備文件,因此,即使進程與創建命名管道的進程之間無任何血緣關係,只要可以訪問該路徑,就能夠通過命名管道相互通信。命名管道總是按照先進先出的原則工作。

六、代碼實現--------------匿名管道

mypipe

/*************************************************************************
	> File Name: mypipe.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sat 13 May 2017 06:49:35 PM PDT
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd[2];

	int ret = pipe(fd);
	if(ret < 0)
	{
		perror("pipe failed");
	}
	pid_t id = fork();
	if(id == 0)
	{
		//child
		close(fd[0]);//write -- close read
		int i = 0;
		char* msg = "hello world!";
		while(i < 10)
		{
			ssize_t s = write(fd[1], msg, strlen(msg));
			printf("i am writing! %d\n",i++);
			fflush(stdout);
		}
	}
	else
	{
		//father
	//	close(fd[1]);
	//	char buf[1024];
	//	while(1)
	//	{
	//		ssize_t s = read(fd[0], buf, sizeof(buf));
	//		if(s > 0)
	//		{
	//			buf[s] = 0;
	//			printf("father read: %s\n", buf);
	//		}
	//		if(s == 0)
	//		{
	//			printf("end of file!\n");
	//		}
	//	}

		int status = 0;
		waitpid(id, &status, 0);
	}

	return 0;
}


七、代碼實現--------命名管道

server

/*************************************************************************
	> File Name: server.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sat 13 May 2017 07:26:10 PM PDT
 ************************************************************************/

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

int main()
{
	int ret = mkfifo("./mypipe", S_IFIFO | 0666);
	if(ret < 0)
	{
		perror("mkfifo failed!");
		return 1;
	}

	int fd = open("./mypipe", O_RDONLY);
	if(fd < 0)
	{
		perror("open failed!");
		return 2;
	}
	else
	{
		while(1)
		{
			char buf[1024];
			memset(buf, '\0', sizeof(buf));
			ssize_t s = read(fd, buf, sizeof(buf));
			if(s > 0)
			{
				buf[s-1] = 0;
				printf("server read:%s", buf);
			}
			else if(s == 0)
			{
				printf("client closed!server is closing!\n");
				break;
			}
		}
	}
	return 0;
}
/*************************************************************************
	> File Name: client.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sat 13 May 2017 07:26:10 PM PDT
 ************************************************************************/

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

int main()
{
	int fd = open("./mypipe", O_WRONLY);
	if(fd < 0)
	{
		perror("open failed!");
		return 1;
	}
	else
	{
		while(1)
		{
			printf("Please Enter: ");
			fflush(stdout);
			char msg[1024];
			memset(msg, '\0', sizeof(msg)-1);
			ssize_t s = read(0, msg, sizeof(msg));
			write(fd, msg, sizeof(msg));
		}
	}
	return 0;
}










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章