1. 程式人生 > >Linux(高階程式設計)6————程序間通訊2(命名管道)

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

通過匿名管道學習,我們對管道有了一些瞭解,下面我們將進行下一步學習管道,這次來學習一下匿名管道,關於管道這裡就不在贅言了,直接上命名管道。

  • 命名管道:
    1.檔案系統可見。
    2.命名管道是一類特殊型別檔案(管道型別檔案) 。
    3.命名管道可以應用於同一主機上所有的程序之間的通訊(命名管道於匿名管道區別)。
  • 命名管道的建立:
    函式原型:
       #include <sys/types.h>
       #include <sys/stat.h>
       int mkfifo(const char *pathname, mode_t mode);

pathname:建立管道檔案的名字
mode:檔案許可權
成功返回:返回0;失敗返回:-1。
當管道建立好後,即可開啟像操作檔案那樣操作管道了。

  • 命名管道的開啟特性(只有命名管道有這個特性):
    1.如果以只讀的方式開啟命名管道 ,那麼open函式將阻塞,直到其它程序以寫的方式開啟這個命名管道。
    2.如果以只寫的方式開啟命名管道 ,那麼open函式將阻塞,直到其它程序以讀的方式開啟這個命名管道。
    3.如果以讀寫的方式開啟命名管道,則不會阻塞(但這樣會與管道特性半雙工向違背,以讀寫的方式開啟會出現問題的)。
    4讀的特性:
    read特性:if(ret>0)讀到資料;if(ret == 0)寫端關閉。
    瞭解了命名管道的這些特性後就可以來做一個小案例練練手了:
    案例1:通過命名管道實現檔案拷貝
    read_file.c:讀取將要複製的檔案並寫入管道
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#define OUT "out"

int main(void)
{
	//1.建立管道
	if(mkfifo("F_PIPE",0664)<0)
	{
		if(errno != EEXIST)
		{
			perror("mkfifo");
			exit(-1);
		}
	}
	//2.開啟管道檔案,開啟要寫入的檔案
	int fd = open(OUT,O_TRUNC|O_WRONLY|0664);
	int rfd = open("F_PIPE",O_RDONLY);
	if(rfd<0 || fd<0)
	{
		perror("open");
		exit(-1);
	}
	//3.將管道中的檔案寫入要複製的目標檔案中
	ssize_t size = 0;
	char buf[1024];
	while((size = read(rfd,buf,sizeof(buf)-1))>0)
	{
		write(fd,buf,size);
	}
	printf("write FILE over!\n");
	close(rfd);
	close(fd);
	return 0;
}

write_file.c:讀取管道中的檔案並寫入目標檔案

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#define OUT "out"

int main(void)
{
	//1.建立管道
	if(mkfifo("F_PIPE",0664)<0)
	{
		if(errno != EEXIST)
		{
			perror("mkfifo");
			exit(-1);
		}
	}
	//2.開啟管道檔案,開啟要寫入的檔案
	int fd = open(OUT,O_TRUNC|O_WRONLY|0664);
	int rfd = open("F_PIPE",O_RDONLY);
	if(rfd<0 || fd<0)
	{
		perror("open");
		exit(-1);
	}
	//3.將管道中的檔案寫入要複製的目標檔案中
	ssize_t size = 0;
	char buf[1024];
	while((size = read(rfd,buf,sizeof(buf)-1))>0)
	{
		write(fd,buf,size);
	}
	printf("write FILE over!\n");
	close(rfd);
	close(fd);
	return 0;
}

執行效果展示:
在這裡插入圖片描述
當管道以只讀或只寫的方式開啟時,會在open函式處阻塞(命名管道的開啟規則)。
當我們在執行write_file時,管道的讀寫都被開啟,阻塞就被解除。
接下來在執行write_file:
在這裡插入圖片描述
在這裡插入圖片描述
當write_file執行管道讀端被開啟,read_file被解除阻塞!!!,檔案複製完成!!!
案例2:通過命名管道實現server&client通訊
server.c:

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

int main(void)
{
	//建立命名管道
	int rfd = -1;
	if((mkfifo("FIFO",0664))<0)
	{
		if(errno != EEXIST)
		{
			perror("mkfifo!");
			exit(-1);
		}
	}
	//開啟管道
	rfd = open("FIFO",O_RDONLY);
	if(rfd<0)
	{
		perror("open");
		exit(-1);
	}
	//迴圈的從命名管道中讀取資料
	char buf[1024];
	while(1)
	{
		printf("please wait client enter....\n");
		int ret = read(rfd,buf,sizeof(buf)-1);
		buf[ret] = 0;
		if(ret>0)
			printf("client say:%s",buf);
		//當ret == 0時,說明寫端關閉
		else if(ret == 0)
		{
			printf("client has quit,exit now!\n");
			exit(1);
		}
		else
		{
			perror("read!");
			exit(-1);
		}
	}
	close(rfd);
	return 0;
}

client.c:

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

int main(void)
{
	//建立命名管道
	int wfd = -1;
	if((mkfifo("FIFO",0664))<0)
	{
		if(errno != EEXIST)
		{
			perror("mkfifo!");
			exit(-1);
		}
	}
	//開啟管道
	wfd = open("FIFO",O_WRONLY);
	if(wfd<0)
	{
		perror("open");
		exit(-1);
	}
	//迴圈的向命名管道中寫入資料
	char buf[1024];
	while(1)
	{
		buf[0] = 0;
		printf("client enter# ");
		fflush(stdout);
		int ret = read(0,buf,sizeof(buf)-1);
		if(ret>0)
		{
			int size = write(wfd,buf,ret);
			if(size<0)
			{
				perror("write!");
				exit(-1);
			}
		}
		
	}
	close(wfd);
	return 0;
}
	

執行效果展示:
在這裡插入圖片描述
在這裡插入圖片描述
總結(命名管道與匿名管道的區別):

1.匿名管道由pipe建立並開啟;命名管道由mkfifo建立,通過open開啟。
2.匿名管道只能用於具有親緣關係的程序間通訊;命名管道可以應用於同一主機上的所有程序間通訊。
3.命名管道檔案系統可見。
4.命名管道是一個特殊型別(管道型別)檔案。