进程通信之有名管道

有名管道的几个特性

①非亲缘进程也可进行通信

②有名管道是一个设备文件,以FIFO的文件形式存储于文件系统中。

③FIFO总按照先进先出的原则工作,第一个被写入的数据首先被从管道中读出来。

1.创建管道

方法1,shell下直接使用命令

mknod namedpipe

 或

mkfifo namedpipe

 即可,mknod和mkfifo函数原型如下

#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char *path, mode_t mod, dev_t dev);
int mkfifo(const char *path, mode_t mode);
/*path为有名管道全路径名,mod为有名管道的模式,指明存取权限,dev为设备值,该值取决于文件创建的种类*/
/*调用成功返回0,失败返回-1*/

 方法2,使用函数创建有名管道

使用mknod创建

umask(0);
if(mknod("/tmp/fifo", D_FIFO | 0666, 0) == -1)
{
perror("mknod error!\n");
exit(1);
}

 “S_IFIFO | 0666”指明创建一个有名管道,且存取权限为0666,即Creater, Creater group,vister对有名管道的访问权限都是可读可写。

mkfifo同理

 

要注意的是,调用open()打开有名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,则一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调用open()的函数的进程将会被阻塞直至有写方法打开管道;同样以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。

例程

//procread.c 读进程

#include<errno.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #define FIFO_NAME "myfifo" #define BUF_SIZE 1024 int main(void) { int fd; char buf[BUF_SIZE]; umask(0); fd = open(FIFO_NAME, O_RDONLY); read(fd, buf, BUF_SIZE); printf("Read content: %s\n", buf); close(fd); exit(0); }

 

//procwrite.c 写进程
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>

#define FIFO_NAME       "myfifo"
#define BUF_SIZE        1024

int main(void)
{
	int fd;
	char buf[BUF_SIZE] = "Hello prowrite, I come from process named procread!";

	umask(0);

	if(mkfifo(FIFO_NAME, S_IFIFO | 0666) == -1)
	{
		perror("mkfifo error!\n");
		exit(1);
	}

	if((fd = open(FIFO_NAME, O_WRONLY)) == -1)//以只写方式打开,会被阻塞至有以读方式打开管道的进程来的时候
	{
		perror("fopen error!\n");
		exit(1);
	}

	write(fd, buf, strlen(buf)+1);
	close(fd);
	exit(0);
}

 先运行procwrite,被阻塞后打开另一个终端运行procread

 通过建立两个有名管道实现进程间的全双工通信。

例程

#include<stdio.h>/*server.c*/
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>

#define FIFO_READ	"readfifo"
#define FIFO_WRITE	"writefifo"
#define BUF_SIZE	1024


int main()
{
	int	wfd, rfd;
	char	buf[BUF_SIZE];
	int 	len;
	errno = 0;
	umask(0);
	if(mkfifo(FIFO_WRITE, S_IFIFO|0666))
	{
		printf("Can't create FIFO %s because %s\n", FIFO_WRITE, strerror(errno));
		exit(1);
	}
	umask(0);
	wfd = open(FIFO_WRITE, O_WRONLY);
	if(wfd == -1)
	{
		printf("Open FIFO %s error: %s\n", FIFO_WRITE, strerror(errno));
		exit(1);
	}
	while((rfd = open(FIFO_READ, O_RDONLY)) == -1)
	{
		sleep(1);
	}

	while(1)
	{
		printf("Server: ");
		fgets(buf, BUF_SIZE, stdin);
		buf[strlen(buf)-1] = '\0';
		if(strncmp(buf, "quit", 4) == 0)
		{
			close(wfd);
			unlink(FIFO_WRITE);
			close(rfd);
			exit(0);
		}
		write(wfd, buf, BUF_SIZE);
		len = read(rfd, buf, BUF_SIZE);
		if(len > 0)
		{
			buf[len] = '\0';
			printf("Client: %s\n", buf);
		}
	}
	return 0;
}

 

#include<sys/types.h>/*client.c*/
#include<sys/stat.h>
#include<string.h>
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>

#define FIFO_READ	"writefifo"
#define FIFO_WRITE	"readfifo"
#define BUF_SIZE	1024


int main()
{
	int	wfd, rfd;
	char	buf[BUF_SIZE];
	int 	len;
	errno = 0;
	umask(0);
	if(mkfifo(FIFO_WRITE, S_IFIFO|0666))
	{
		printf("Can't create FIFO %s because %s\n", FIFO_WRITE, strerror(errno));
		exit(1);
	}

	while((rfd = open(FIFO_READ, O_RDONLY)) == -1)
	{
		sleep(1);
	}
	wfd = open(FIFO_WRITE, O_WRONLY);
	if(wfd == -1)
	{
		printf("Open FIFO %s error: %s", FIFO_WRITE, strerror(errno));
		exit(1);
	}

	while(1)
	{

		len = read(rfd, buf, BUF_SIZE);
		if(len > 0)
		{
			buf[len] = '\0';
			printf("Server: %s\n", buf);
		}
		printf("Client: ");
		fgets(buf, BUF_SIZE, stdin);
		buf[strlen(buf)-1] = '\0';
		if(strncmp(buf, "quit", 4) == 0)
		{
			close(wfd);
			unlink(FIFO_WRITE);
			close(rfd);
			exit(0);
		}
		write(wfd, buf, BUF_SIZE);
	}
	return 0;
}

 用两个有名管道做了个服务器客户端模型,服务器先写,客户端先读。即服务器的写管道,客户端用来读,服务器的读管道,客户端用来写。避免两个同时写一个管道导致信息混乱。看看运行结果

 

 启动sever和client以后就可以从server端输入消息了。(ps:客户端最后一句话的“去去”是已经删除的文字,但是由于中文文本的原因还在显示)

顺便说一下unlink()这个函数

功能详解:unlink从文件系统中中删除一个名字,若这个名字是指向这个文件的最后一个链接,并且没有进程处于打开这个文件的状态,则删除这个文件,释放这个文件占用的空间。

           如果这个名字是指向这个文件的最后一个链接,但有某个进程处于打开这个文件的状态,则暂时不删除这个文件,要等到打开这个文件的进程关闭这个文件的文件描述符后才删除这个文件。

           如果这个名字指向一个符号链接,则删除这个符号链接。

           如果这个名字指向一个socket、fifo或者一个设备,则这个socket、fifo、设备的名字被删除,当时打开这些socke、fifo、设备的进程仍然可以使用它们。

 

返回值:调用成功返回0,不成功返回-1.

posted @ 2019-10-29 17:53  C_hp  阅读(385)  评论(0)    收藏  举报