进程间通信方式--管道

管道


  • 无名管道(pipe)
  • 有名管道(FIFO)。

无名管道(pipe):也简称为管道,是一种半双工的通信方式,数据只能往一个方向流动,且只能在有亲缘关系的进程之间使用,进程间的亲缘关系一般是指父子进程;

有名管道(FIFO):有名管道(FIFO)也是一种半双工的通信方式,可以再没有亲缘关系的进程之间通信;

文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行通信。而命名管道是一个可见的文件,因此,它可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。

有名管道和无名管道的主要区别在于:
有名管道有一个名字,命名管道的名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。


管道的函数


无名管道(pipe)


创建管道(pipe)

int pipe(int filedes[2]);

pipe()会建立管道,并将文件描述词由参数 filedes数组返回。

filedes[0]为管道里的读取端,用read()函数调用。

filedes[1]则为管道的写入端。使用write进行写入操作。

若成功则返回零,否则返回-1,错误原因存于 errno 中。
成功返回0后,数组fds被填入两个有效的文件描述符。第一个元素中的文件描述符供应用程序读取,第二个元素中的文件描述符可以用来供应用程序写入。

错误原因:
EMFILE进程已用完文件描述词最大量
ENFILE 系统已无文件描述词可用。
EFAULT参数 filedes 数组地址不合法。


关闭管道(close)

  • 关闭管道只是将两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。

如果管道的写入端关闭,但是还有进程尝试从管道读取的话,将被返回0,用来指出管道已不可用,并且应当关闭它。如果管道的读出端关闭,但是还有进程尝试向管道写入的话,试图写入的进程将收到一个SIGPIPE信号。


有名管道(mkfifo)


mkfifo函数是在文件系统中创建一个文件,该文件用于提供命名管道(FIFO)功能。原型如下所示:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode );

参数1:
pathname是将要在文件系统中创建的一个专用文件。

参数2:
mode用来规定FIFO的读写权限。

成功返回0;失败返回-1

 ret = mkfifo( "/home/ubuntu/test_fifo", S_IFIFO | 0666 );

利用/home/ubuntu/目录下的test_fifo文件创建了一个有名管道,就可以打开这个文件进行读写操作,并以此进行通信了。命名管道一旦打开,就可以利用典型的输入输出函数从中读取内容。

fgets函数来从管道中读取内容

fp = fopen( "/home/ubuntu/test_fifo", "r" );
ret = fgets( buf, MAX_LINE, fp);

fopen打开/home/ubuntu/test_fifo文件,返回文件句柄fp,在用fget函数将管道中的文件读到buf中.

fprintf函数向管道写入数据

fp = fopen( "/home/ubuntu/test_fifo", "w+" );
ret = fprintf( fp, "test success!\n" );

fopen打开/home/ubuntu/test_fifo文件,返回文件句柄fp,在用fprint函数将数据写入管道中

管道的实现


无名管道的实现


无名管道为建立管道的进程及其子孙提供了一条比特流方式传递消息的通信管道
无名管道在逻辑上被看作管道文件,在物理上则由文件系统的高速缓冲区构成,并且很少启动外设;

无名管道的读写
发送进程利用文件系统的系统调用 write(fd[1],buf,size) ,把buf中的长度为size的字符消息送入管道入口fd[1]
接受进程则使用系统调用read(fd[0],buf,size)从管道出口fd[0]读取size字符的消息放入buf

//无名管道为建立管道的进程及其子孙提供了一条比特流方式传递消息的通信管道
//无名管道在逻辑上被看作管道文件,在物理上则由文件系统的高速缓冲区构成,并且很少启动外设;
//发送进程利用文件系统的系统调用 write(fd[1],buf,size) ,把buf中的长度为size的字符消息送入管道入口fd[1]
//接受进程则使用系统调用read(fd[0],buf,size)从管道出口fd[0]独处size字符的消息放入buf中
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define MAX_DATA_LEN 256
#define DELAY_TIME 1

int main(void)
{
	pid_t pid;
	char buf[MAX_DATA_LEN];
	const char *data = "transfer";     //将要传输的数据内容
	int real_read,real_write;    //从管道中读到的数据长度,和写入管道的数据长度
	int pipe_fd[2];                             //定义两个管道
	int i = 0;
	int pipe_t = 0;  //存放创建管道的返回值
	
	memset((void *)buf, 0, sizeof(buf));        //清空buf
	
	//创建管道
	for(i = 0; i < 2; i++){
		if((pipe_t =pipe(pipe_fd[i])) < 0){
			printf("pipe %d create failed!\n", i);
			exit(1);
		}
	}
	
	//创建进程
	//fork函数返回值 < 0 ,表示创建进程失败
	if((pid = fork()) < 0){
		printf("fork process failed!\n");
		exit(1);
	}
	//fork函数返回值 = 0 ,表示是子进程
	else if(pid == 0){
		//子进程作为读,pipe_fd[0]
		printf("I am the child process, PID = %d, PPID = %d\n", getpid(), getppid());
		
		//从管道中读数据时,先把写管道关掉
		close(pipe_fd[1]);
		sleep(DELAY_TIME * 3);
		
		//读取失败
		if((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) < 0){
			perror("read\n");
			exit(1);
		}else{
			printf("Child receive %d bytes from pipe :'%s'.\n",real_read,buf);
		}
		close(pipe_fd[0]);
		exit(0);
	}else{
		//父进程写pipe_fd[1]
		printf("I am parent process ,PID = %d, PPID = %d\n",getpid(), getppid());
		
		//管道写操作时,先关掉读通道
		close(pipe_fd[0])sleep(DELAY_TIME * 3);
		
		if((real_write = write(pipe_fd[1], data ,strlen(data))) < 0){
			perror("write\n");
			exit(1);
		}else{
			printf("parent send %d bytes to pipe:'%s'\n", real_write, data);
		}
		close(pipe_fd[1]);
		waitpid(pid,NULL,0);
		
		exit(0);
	}
	return EXIT_SUCCESS;
}

有名管道的实现


读端

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

#define FIFO "my_fifo" //管道名
#define MAX_LEN 1024

int main(int argc, char *argv[])
{
	char buf[MAX_LEN];
	int read_d;
	int fd;
	
	//看管道是否存在,不存在则创建管道
	if(access(FIFO, F_OK) == -1){
		if((mkfifo(FIFO, 0666) < 0)){
			printf("create FIFO failed!\n");
			exit(1);
		}
	}
	
	//以只读方式打开管道
	if((fd = open(FIFO, O_RDONLY)) < 0){
		printf("open FIFO failed!\n");
		exit(1);
	}
	
	//清空buf内存
	memset(buf, 0, MAX_LEN);
	
	//读数据
	if((read_d = read(fd, buf, MAX_LEN)) < 0){
		printf("read failed!\n");
		exit(1);
	}
		
	
	printf("read %d bytes from FIFO is : %s\n", read_d, buf);

	
	close(fd);
	return 0;
}

写端

//写端

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

#define FIFO "my_fifo"  //管道名
#define MAX_LEN 1024

int main(int argc, char *argv[])
{
	char buf[MAX_LEN];
	int fd;
	int write_d;
	
	if(argc <= 1){
		printf("need two argc!\n");
		exit(1);
	}
	
	sscanf(argv[1], "%s", buf);
	
	//	判断管道是否存在,不存在则创建管道
	 if(access(FIFO, F_OK) == -1)
    {
        if((mkfifo(FIFO, 0666) < 0)
        {
            printf("Can NOT create fifo file!\n");

            exit(1);
        }
    }

	//以只写方式打开管道
	if((fd = open(FIFO, O_WRONLY)) < 0){
		printf("open FIFO failed!\n");
		exit(1);
	}
	
	//调用write将buff写到文件描述符fd指向的FIFO中
	if((write_d = write(fd, buf, MAX_LEN)) < 0){
		printf("write failed!\n");
		exit(1);
	}
	printf("There are %d bytes write in FIFO is : %s\n", write_d, argv[1]);
	
	close(fd);
	return 0;
	
	
}

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44715649/article/details/88869439