进程间通讯--管道

来源:互联网 发布:ubuntu syslog 配置 编辑:程序博客网 时间:2024/06/05 20:40

何为进程间通讯呢?就是进程间数据的交换。

假设两个没有关系的进程想要进行数据交换,怎样才能实现呢?定义一个全局变量?显然不行,就连有关系的父子进程之间全局变量、堆区以及栈区都是不共享的,更何况两个无关的进程。那么问题来了,父子进程对于文件偏移量是共享的,我们是不是也可以通过向磁盘上写文件来实现进程间通讯呢?比如有两个进程A和B,想通过A进程写一个hello world传递给B进程。要实现这种目的,就得A写完B去读,否则A没写B通过read函数将返回0;不能做到协同工作,并且没有意义,重要的一点就是,每次的读写都需要I/O操作;I/O操作太慢,因此这种想法,并不能达到我们所说的进程间通讯。

为了进程间通讯,在操作中有好几种方法可供实现,比如信号、管道、信号量、消息队列、共享存储(也称共享内存)、套接字。

本篇文章主要来说管道。何为管道?就是在内存上开辟一个独立的空间,用于做两个进程间通讯的桥梁,就叫做管道。管道是半双工通讯,一个进程(A进程)向管道写数据,另一个进程(B进程)从管道读数据,好比一个循环队列的入队和出队,当写入的数据读完时,管道中的数据就会被清空。计算机这样做当然有它的优势所在,首先,进程和管道都在内存上,相对从磁盘上读速度就快。其次,当进程没有读到的时候会阻塞运行。

管道也有分类,包括有名管道和无名管道。

有名管道就是文件目录树中的一个文件标示,实际不占磁盘空间。

创建的时候有两种方法:a.以命令mkfifo创建;b.用mkfifo()函数创建;

其它的操作跟普通文件一样:打开用open函数;写数据用write函数;读数剧用read函数;关闭用close函数。


现在要提到一个概念:

阻塞运行函数:函数运行以后不会直接返回,需要等待某些条件的发生。

(1).open操作管道文件时就是阻塞函数,如果一个进程以只写的方式打开时,open就会阻塞运行,直到有一个进程以只读或者可读可写方式打开管道文件时,程序才会运行,open才会实现。反之,当一个进程以只读或者可读可写方式打开时,open会阻塞,直到一个进程以只写的方式打开。

(2).read也会阻塞运行,直到管道中有数据或者所有的写端都关闭。

(3).write也会阻塞,只是不经常,就是在管道被写满时阻塞。


下面写一个例子,A进程负责循环接受用户输入的数据,以end为结束标志;B进程负责统计用户输入数据的个数并做以统计。

用mkfifo创建一个管道文件,名为AB

A进程向管道中写数据(a.c)

#include<stdio.h>#include<stdlib.h>#include<assert.h>#include<unistd.h>#include<fcntl.h>#include<string.h>int main(){int fd=open("AB",O_WRONLY);if(fd==-1){exit(0);}printf("AB open success\n");char buff[128]={0};fgets(buff,128,stdin);while(strncmp(buff,"end",3)!=0){write(fd,buff,strlen(buff));memset(buff,0,128);fgets(buff,128,stdin);}printf("write success\n");close(fd);}
B进程从管道中读数据(b.c)

#include<stdio.h>#include<assert.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<fcntl.h>int main(){int fd=open("AB",O_RDONLY);if(fd==-1){exit(0);}printf("open success\n");int count=0;char buff[128]={0};while(read(fd,buff,127)>0){printf("%s\n",buff);count++;}printf("numbers::count=%d\n",count);}

运行:


无名管道:相对于有名管道而言,无名管道在使用时产生,不使用后释放。在使用前没有任何的标示,并且不会在系统上留下任何蛛丝马迹,鉴于这样“神秘”的特性,两个无关的进程无法知道管道的存在,也就无法通过无名管道来进行进程间通讯。然而在父子进程中,子进程会复制父进程的文件数组(浅拷贝),父子进程之间有一定的联系,所以无名管道只能适用于父子进程间。

知道了无名管道的定义和依赖关系,接下来就要讨论怎样操作无名管道了,同样的它也是创建、打开、读数据、写数据、关闭。

创建它一定就是为了用它,所以创建和打开我们用一个函数:int pipe(int  fd[2])。管道用于两个进程间的通讯,创建一定在fork()之前。当一个管道创建时,会打开两个文件描述符

fd[0] :从管道中读数据;

fd[1]:向管道中写数据。

它成功时返回0,失败时返回-1。

读数据时read(fd[0],buff,size);

写数据时write(fd[1],buff,len);

关闭时close(fd[0])或者close(fd[1])。


需要注意的是:管道是半双工通讯,无名管道打开,fork()之后,父进程和子进程都有一对读写,因此要在父进程和子进程中分别关闭读和写或者写和读,以便达成一个写一个读相统一。

 

原创粉丝点击