Linux进程通信之管道
Linux中进程通信方式,即IPC通信方式主要有以下七种:
(1)无名管道(pipe)
(2)有名管道(FIFO)
(3)信号
(4)消息队列
(5)共享内存
(6)信号量
(7)套接字(Socket)
在后面的学习中,我将重点学习这些知识,这一节我学习了无名管道和有名管道,在此将学习总结写下来。
在进行linux管道通信编程之前,我们首先要了解其通信原理,Linux管道通信模式是一个进程在管道的尾部写入数据,另外一个进程从管道的头部读出数据。管道包括无名管道和有名管道两种,前者只能用于父子进程间的通信,后者则可用于运行于同一系统中的任意两个进程间的通信。管道的通信有如下特点:
1、管道通讯是单向的,有固定的读端和写端。
2、数据被进程从管道读出后,在管道中该数据就不存在了。
3、当进程去读取空管道的时候,进程会阻塞。
4、当进程往满管道写入数据时,进程会阻塞。
5、管道容量为64KB。
在linux系统中,无名管道一旦创建完成后,就会产生两个端子,分为读端
和写端,此时操作无名管道就相当于是操作文件,采用文件描述符来描述无名管道的两端。
下面我们就来看看具体的函数操作吧,在linux中,可以用下面的函数来创建一个无名管道
int pipe(intpipefd[2]);
该函数如果成功创建一个管道,则返回0,否则返回-1,其参数是一个int
类型的数组,这个数组并非是作为一个传递值,而是作为一个接收的数组,当我们调用这个函数的时候,系统会把分配的描述符存放在这个数组中,pipefd[0]是表示读端的描述,pipefd[1]表示写端的描述,如此,有了描述符,我们便可以像操作一个文件一样来操作无名管道了,下面我就一个实例程序来模拟一下无名管道之间的通讯:
#include<stdio.h>
#include<unistd.h>
void main()
{
pid_t pid;
int pipefd[2];
char buf[8];
pipe(pipefd); //创建一个管道,必须在创建子进程前创建
pid = fork();
if(pid >0)
{
write(pipefd[1] , "hello" , 6); //向管道写入数据
wait(); //等待子进程读取管道数据退出
exit(0);
}
else if(pid == 0)
{
read(pipefd[0] , buf , 6); //读取管道中的数据
printf("the data is %s\n" , buf);//打印读取到的数据
exit(0);
}
}
程序中必要的位置已做了详细的注释,需要再次说明的是,这是无名管道通讯方式,只有父子进程间才可以使用这种方式。分析下上面的程序,最终结果应该是什么?根据我们前面的分析介绍,应该不难得出,这个程序应该会打印出
the data is hello
在linux下运行这个程序,可以很容易得到这个结果。
上面说了无名管道,下面就说一下有名管道,有名管道又称为FIFO文件,因此对有名管道的操作也可以像对文件一样,使用诸如open,write,read等函数方法。但是FIFO文件和普通的文件还是有部分区别的:
读取Fifo文件的进程只能以“O_RDONLY”方式打开fifo文件,而写Fifo文件的进程只能以“O_WRONLY”方式打开fifo文件;Fifo文件里面的内容读取后就消失了,但是普通文件里面的内容读取后还存在。
下面我们就来看看如何创建一个FIFO文件,创建FIFO类似于创建普通文件,Linux中为我们提供了下面的函数来创建一个FIFO;
intmkfifo(const char *pathname , mode_t mode);
这个函数成功创建一个FIFO文件后就返回0否则返回-1,该函数第一参数指的是文件创建的路径,这个文件路径必须不存在,不然创建不成功,第二个参数之前我们有接触过,就是在讲文件编程时open函数的一个参数,其表示一个文件选项,用来标注创建的文件是“只读”、“只写”等等。创建完一个FIFO文件后,我们就可以用open函数打开它。另外我们还有一个函数,叫unlink,该函数可用于删除一个FIFO文件,其原型如下:
int unlink(const char *pathname);
参数为待删除文件的路径。下面我们就来用一个程序实例看看我们FIFO文件的用法,这个实例是任意两个进程采用有名管道来进行通信,因此我们需要新建两个文件:
向管道写数据的pipename.C文件代码如下:
</pre><pre name="code" class="cpp">#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
void main()
{
int fd;
int result = 2;
fd = open("/home/share/fun_call/fifl",O_WRONLY);
if(fd == -1)
{ printf("name pipe is not exist,we will create it\n");
result = mkfifo("/home/share/fun_call/fifo" , O_WRONLY);
if(result!=0)
{
printf("create named pipe fail result = %d\n",result);
return -1;
}
fd= open("/home/share/fun_call/fifo" , O_WRONLY);
}
向管道读取数据的代码如下:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
voidmain()
{
int fd;
char buf[15];
fd =open("/home/share/fun_call/fifo" , O_RDONLY);
read(fd , buf , 15);
printf("readthe buf is %s\n" , buf);
close(fd);
unlink("/home/share/fun_call/fifo");
}
以上两个程序,pipename.c向管道写入了hello fifo 11个字节的数据,readfifo.c就是读取这个数据,如果运行readfifo.c的编译后的可执行文件打印出了hell fifo则说明我们实现了利用有名管道在两个进程间通信。需要说明的是,在运行读数据可执行文件之间,我们首先要执行写入的可执行文件,也就是pipename.c编译后产生的文件,这时候由于无人读取,界面会进入阻塞状态等待其它进程来读取,因此我们要重启一个命令行界面来运行readfifo.c生产的可执行文件。运行结果如下:
可见我们的程序确实实现了任意进程间通过有名管道来进行通信。
总之,对于管道的操作,我们完全可以把它看做是对两个文件来进行操作,以上便是Linux应用编程中有关管道的知识,纰漏之处希望得到广大好友的指正。