进程之间的通信——管道

管道

1、特点:
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。

pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。
FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。
管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。
————————————————
版权声明:本文为CSDN博主「zhaohong_bo」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaohong_bo/article/details/89552188

无名管道

需要使用到pipe()函数
fd[2]中的两个成员,fd[0]为读端,fd[1]为写端
一般在一开始就会定好两个进程的读写关系,用close(fd[0])或close(fd[1])来关闭不用的读端或者写端
否则就会出现自己写自己读的尴尬状态。
而且不能用open的方法打开已经关闭的读端或者写端,否则会出现问题。

写管道用write()函数的方式写入数据
读管道用read()函数的方式读取数据

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
	int fd[2];
	if(pipe(fd)<0){
		perror("pipe error:");
	}

	pid_t pid;
	pid = fork();
	char* buf_father;
	char* buf_son;
	if(pid>0){//father
		close(fd[0]);//父进程关闭读端
		buf_father = "father love son";
		write(fd[1],buf_father,strlen(buf_father));
		printf("father write %ld bytes :%s\n",strlen(buf_father),buf_father);
		wait();
	}
	else if(pid == 0){//son
		close(fd[1]);//子进程关闭写端
		buf_son = (char*)malloc(128);
		read(fd[0],buf_son,128);//管道内部如果没有数据就会阻塞在read函数这里
		printf("son read %ld bytes :%s\n",strlen(buf_son),buf_son);
		exit(0);
	}
	else{//fork error
		perror("fork error:");
	}

	return 0;
}

命名管道

需要用到mkfifo创建命名管道,命名管道以文件的形式存在,可以用普通文件的方法进行读写
mkfifo在man手册的第三页

       #include <sys/types.h>
       #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);
       //pathname:文件名
       //mode:权限,一般用0666表示所有用户的可读可写可执行权限

而直接用mkfifo创建命名管道可能会返回错误信息——因为管道文件已存在可能会返回错误信息,根据man手册中的内容我稍微做了下总结

//注意 : 以下的errno需要调用头文件errno.h
ERRORS
       EACCES : 文件权限问题,只要有其中一个文件的权限不允许读写就会使得错误码errno = EACCES 
       EDQUOT : 用户在文件系统上的磁盘块或索引节点的配额已经用完,一般出现在磁盘空间不够的时候errno = EDQUOT 
       EEXIST : 文件已存在,但是调用了mkfifo就会导致错误码errno = EEXIST
       ENAMETOOLONG : 文件名长度超限,错误码errno = ENAMETOOLONG            
       ENOENT : pathname中的目录组件不存在,或者是一个悬空的符号链接,错误码errno = ENOENT 
       ENOSPC : 目录或文件系统没有存放新文件的空间,错误码errno = ENOSPC 
       ENOTDIR :在pathname中用作目录的组件实际上不是目录,错误码errno = ENOTDIR 
       EROFS : 参数中的文件名为只读的,错误码errno = EROFS 
       EBADF : dirfd 不是一个有效的文件描述符		//不懂??
       ENOTDIR : pathname是一个相对路径,dirfd是一个引用文件而不是目录的文件描述符。

读取端

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


int main(){
	if(mkfifo("./exchange",0600)==-1&&errno!=EEXIST){//调用mkfifo函数失败,且失败原因不是文件已存在。
													  //目的是即使fifo文件存在也不要报错
		perror("mkfifo error:");
	}

	int fd = open("./exchange",O_RDONLY);
	printf("open success\n");
	
	char buf[30];
	int n_read;
	while(1){
		n_read = read(fd,buf,30);
		printf("read %d bytes : %s\n",n_read,buf);
	}
	close(fd);

	return 0;
}

发送端

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

int main(){

	int fd = open("./exchange",O_WRONLY);
	printf("open success\n");

	char* buf = "hello world";
	while(1){
		write(fd,buf,25);	
		sleep(1);
	}
	close(fd);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值