Linux进程间通信-管道之命名管道 原创 Linux平台 2022年2月11日 19:14 夏至未至 2962 当前内容 3698 字,在路上,马上到,马上到 ### 目录 [TOC] 前面总结了匿名管道,现在来看命名管道:由于匿名管道的一个限制就是:只能是有血缘关系的进程间才可以通信,比如:有两个同祖先的子进程,父子进程等;为了突破这一个限制,想让没有任何关系的两个进程间也能正常通信,所以就就有了命名管道这样的一个通信机制 ### 命名管道原理 管道的一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道(named pip或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。 值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。 ### 命名管道实现 #### 命名管道创建 Linux下有两种方式创建命名管道。一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。Shell方式下可使用mknod或mkfifo命令,下面命令使用 mknod创建了一个命名管道: mknod namedpipe 创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头⽂文件sys/stat.h,函数原型如下: #include #include int mknod(const char *path,mode_t mod,dev_t dev); int mkfifo(const char *path,mode_t mode); 函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。 这两个函数调用成功都返回0,失败都返回-1。 #### 命名管道读写 ##### 实操代码 client.c #include"comm.h" int main() { Read(); return 0; } server.c #include"comm.h" int main() { Write(); return 0; } comm.h #pragma once #include #include #include #include #include #include #define _PATH_ "/tmp/file.tmp" #define _SIZE_ 100 void Read(); void Write(); comm.c #include"comm.h" //读 void Read() { int fd = open(_PATH_,O_RDONLY); //只读的方式打开,成功返回零,失败返回-1 if(fd < 0) { printf("打开文件失败!\n"); return 1; } char buf[_SIZE_]; memset(buf,'\0',sizeof(buf)); //给缓冲区初始化 while(1) { int ret = read(fd,buf,sizeof(buf)); if(ret < 0) { printf("到文件结尾 或者出错\n"); break; } printf("%s\n",buf); //将缓冲区的数据一次性读出 if(strncmp(buf,"caonima",7) == 0) { break; } } close(fd); } //写 void Write() { int ret = mkfifo(_PATH_,0666 | S_IFIFO); if(ret == -1) { printf("创建管道失败\n"); return 1; } int fd = open(_PATH_,O_WRONLY); if(fd < 0) { printf("打开失败\n"); return 2; } char buf[_SIZE_]; memset(buf,'\0',strlen(buf)+1); while(1) { printf("请输入:\n"); scanf("%s",buf); int ret = write(fd,buf,strlen(buf)+1); if(ret < 0) { printf("写出错\n"); break; } if(strncmp(buf,"caonima",7) == 0) { break; } } close(fd); } ##### 使用注意点 `S_IFIFO|0666` 指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写。 命名管道创建后就可以使用了,命名管道和管道的使用方法基本是相同的。只是使用命名管道时,必须用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。 需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,则一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方打开管道;同样以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。 ### 命名管道安全问题 前面的例子两个进程之间的通信问题,也就是说,一个进程向FIFO文件写数据,而另一个进程则在FIFO文件中读取数据。试想这样一个问题,只使用一个FIFO文件,如果有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件中读取数据时,会发生怎么样的情况呢,会发生数据块的相互交错是很正常的?而且个人认为多个不同进程向一个FIFO读进程发送数据是很普通的情况。 为了解决这一问题,就是让写操作的原子化。怎样才能使写操作原子化呢?答案很简单,系统规定:在一个以O_WRONLY(即阻塞方式)打开的FIFO中, 如果写入的数据长度小于等待PIPE_BUF,那么或者写入全部字节,或者一个字节都不写入。如果所有的写请求都是发往一个阻塞的FIFO的,并且每个写记请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据决不会交错在一起 ### 结束语 文件系统中的路径名是全局的,各进程都可以访问,因此可以用文件系统中的路径名来标识一个IPC通道。 命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。 由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。 本文标题: Linux进程间通信-管道之命名管道 本文作者: 夏至未至 发布时间: 2022年2月11日 19:14 最近更新: 2022年2月11日 22:15 原文链接: 许可协议: 署名-非商业性-禁止演绎 4.0 国际(CC BY-NC-ND 4.0) 请按协议转载并保留原文链接及作者 进程间通信(8) 命名管道(1) 上一个 Linux进程间通信-信号 下一个 Linux进程间通信-管道之匿名管道 当前文章评论暂未开放,请移步至留言处留言。