命名管道
管道的一个不足之处就是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道提出后,该限制得到克服. FIFO不用于管道之处在
于它提供一个路径名与之关联,以FIFO的文件形式存储在文件系统中。命名管道是一个设备文件,因此,及时进程与创建FIFO的进程不存在亲缘
关系,只要可以访问该路径,就能够通过FIFO相互通信. FIFO按照先进先出原则工作,你先被写入就先被读出.
那么问题来了,我们如何创建一个命名管道,以及它是怎么使用的呢?
Linux下有两种方式创建命名管道.
一是在Shell下交互地建立一个命名管道
二是在程序使用函数建立命名管道
mknod nameedpipe
创建命名管道的系统函数有两个: mknod和mkfifo. 两个函数均定义在sys/stat.h
#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char* path,mode_t mode,dev_t dev);
int mkfifo(const char* path,mode_t mode);
函数mknod参数中path为创建的命名管道的全路路径: mod为创建的命名管道的模式,指明其存取权限; dev为设备值,该值取决于文件创建的种
类,它只是在创建文件时才会用到,这两个函数调用成功都是返回0 失败返回 -1
举个例子: mknod("/tmp/fifo",S_IFIFO|666)
这里的 S_IFIFO|666 指明创建一个命名管道且存取权限为0666,即创建者,与创建者同组的用户,其他用户对该命名管道的访问权限
都是可读可写.命名管道创建后就可以使用了,命名管道和管道的使用方法基本相同. 只是使用命名管道时,必须先调用open()将其打开.因为命
名管道是一个存在于硬盘上的文件,而管道是存在于内存的特殊文件.
需要注意的是,调用open打开命名管道的进程可能会被阻塞.但如果同时用读写方式(O_RDWR)打开,则一定不会阻塞; 如果以只读方式
(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方法打开管道; 同样以写方式打开也会阻塞直到有读方式打开管道.
我们可以使用两个函数之一来创建一个命名管道,他们的原型如下:
#include<sys/types.h>
#include<sys/stat.h>
int mkffo(const char* filename,mode_t mode);
int mknod(const char* filename,mode_t mode|S_IFIFO,(dev_t)0);
这两个函数都能创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename制定了文件名,而mode则指定了文件的读写权限.
这里注意一下mknod函数已经过时了,这里mkfifo是年轻人,所以尽可能用年轻人,让老年人休息休息.
mkfifo函数的作用是创建一个文件,该文件用于提供FIFO功能,既命名管道.上一篇博客所有的管道都是匿名管道。对于文件系统来说,匿名管
道是不可见的,它的作用权限是在父进程和子进程之间通信.既命名管道是一个可见的额文件,因此,它的作用可以用于任何两个进程之间,注
意这里的任意!但是命名管道还是存在致命缺点->单项通信,这个没办法解决,因为这是管道的性质.
代码举例:
写端代码:
//fifowrite.c
/*************************************************************************
> File Name: fifowrite.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Mon 19 Jun 2017 10:15:40 PM PDT
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#define _PATH_ "./mypipe"
#define _SIZE_ 100
int main()
{
//首先创建一个_PATH_管道,注意这里的_PATH_是一个宏.
int ret = mkfifo(_PATH_, 0666 | S_IFIFO);
if (ret == -1)
{
printf("mkfifo error\n");
return 1;
}
int fd = open(_PATH_, O_WRONLY);
if (fd < 0)
printf("Open error\n");
char buf[_SIZE_];
memset(buf, '\0', sizeof(buf));
while (1){
scanf("%s", buf);
//这里开始往_PATH_管道里面写东西.
int ret = write(fd, buf, strlen(buf) + 1);
if (ret < 0)
{
printf("write error\n");
break;
}
if (strncmp(buf, "quit", 4) == 0){
break;
}
}
close(fd);
return 0;
}
读端代码:
//fiforead.c
/*************************************************************************
> File Name: fiforead.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Mon 19 Jun 2017 09:33:56 PM PDT
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#define _PATH_ "./mypipe"
#define _SIZE_ 100
int main()
{
//先打开管道.
int fd = open(_PATH_, O_RDONLY);
char buf[_SIZE_];
memset(buf, '\0', sizeof(buf));
while (1)
{
//然后从管道里面拿出数据
int ret = read(fd, buf, sizeof(buf));
if (ret <= 0)
{
printf("read end or error\n");
break;
}
printf("%s\n", buf);
if (strncmp(buf, "quit", 4) == 0)
{
break;
}
}
close(fd);
return 0;
}
当这里运行fifowrite.c后,我们就可以看到文件里面里面多形成了一个mypipe.c
然后我们再同时运行读端和写端程序,就可以发生命名管道之间的通信->
注意这里必须要同时运行读端和写端,可以使用Makefile我这里为了直观我没有使用makefile.
命名管道的这些知识大致就这么多,它的应用也大致是这样,他可以实现任意进程之间的通信,但是他还是只有单向通信,所以
我们应该还有更好的通信方式,比如消息队列,信号量,共享内存接下来我会逐一介绍到.