-
有名管道,有名管道API,有名管道实现进程的单向通信(本机IPC)【linux】(zx)
2020-03-07 20:16:28有名管道为什么叫“有名管道”有名管道特点能够用于非亲缘进程之间的通信进入阻塞读端口被关闭的处理有名管道的使用步骤有名管道APIAPI说明有名管道实现进程的单向通信代码演示小结:图解分析 有名管道 为什么叫...有名管道
为什么叫“有名管道”
无名管道因为没有文件名,被称为了无名管道,同样的道理,有名管道之所以叫“有名管道”,是因为它有文件名。
也就是说当我们调用相应的API创建好“有名管道”后,会在相应的路径下面看到一个叫某某名字的“有名管道文件”。
不管是有名管道,还是无名管道,它们的本质其实都是一样的,它们都是内核所开辟的一段缓存空间。
进程间通过管道通信时,本质上就是通过共享操作这段缓存来实现,只不过操作这段缓存的方式,是以读写文件的形式来操作的。有名管道特点
能够用于非亲缘进程之间的通信
因为有文件名,所以进程可以直接调用open函数打开文件,从而得到文件描述符,不需要像无名管道一样,必须在通过继承的方式才能获取到文件描述符。
所以任何两个进程之间,如果想要通过“有名管道”来通信的话,不管它们是亲缘的还是非亲缘的,只要调用open函数打开同一个“有名管道”文件,然后对同一个“有名管道文件”进行读写操作,即可实现通信。
A进程 —————————> 有名管道 ————————> B进程
总之,不管是亲缘进程还是非亲缘进程,都可以使用有名管道来通信。
进入阻塞
读管道时,如果管道没有数据的话,读操作同样会阻塞(休眠)
读端口被关闭的处理
当一个进程所有读端都被关闭了的管道时,进程会被内核返回SIGPIPE信号如果不想被该信号终止的话,我们需要忽略、捕获、屏蔽该信号。
不过一般情况下,不需要对这个信号进行处理。有名管道的使用步骤
(1)进程调用mkfifo创建有名管道
(2)open打开有名管道
(3)read/write读写管道进行通信
对于通信的两个进程来说,创建管道时,只需要一个人创建,另一个直接使用即可。
为了保证管道一定被创建,最好是两个进程都包含创建管道的代码,谁先运行就谁先创建,后运行的发现管道已经创建好了,那就直接open打开使用。有名管道API
API说明
函数原型
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
功能
创建有名管道文件,创建好后便可使用open打开。
如果是创建普通文件的话,我们可以使用open的O_CREAT选项来创建,比如:
open("./file", O_RDWR|O_CREAT, 0664);但是对于“有名管道”这种特殊文件,这里只能使用mkfifo函数来创建。
参数
pathname:被创建管道文件的文件路径名。
mode:指定被创建时原始权限,一般为0664(110110100),必须包含读写权限。
使用open函数创建普通文件时,指定原始权限是一样的。
open("./file", O_RDWR|O_CREAT, 0664);
创建新文件时,文件被创建时的真实权限=mode & (~umask)
umask是文件权限掩码,一般默认为002或者022
mkfifo(“./fifo”, 0664);返回值
成功返回0,失败则返回-1,并且errno被设置。有名管道实现进程的单向通信
代码演示
有两个进程想要通信,而且还是非亲缘进程,此时我们就可以使用“有名管道”来通信。
我们首先在当前代码创建一个管道文件:#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <strings.h> #include <signal.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #define FIFO_NAME1 "./fifo1" void print_err(char * estr) { perror(estr); exit(-1); } int main() { char buf[100] = {0}; int ret = 0; int fd = 0; ret = mkfifo(FIFO_NAME1,0664); if(ret == -1) print_err("mkfifo fail"); fd = open(FIFO_NAME1,O_WRONLY); if(fd == -1) print_err("open fail"); while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); write(fd,buf,sizeof(buf)); } return 0; }
运行结果为:
我们再次运行程序:
我们可以看到管道文件已经存在,那么我们就可以知道mkfifo这个函数在创建管道文件的时候如果管道文件已经存在的时候就会出错返回,并且打印文件已经存在的错误。
那么我们对于上面代码进行修改:
把代码:if(ret == -1) print_err("mkfifo fail");
修改为:
if(ret == -1 && EEXIST ) print_err("mkfifo fail");
那么我们当文件存在的时候我们不需要程序去报错退出,而是直接忽略。
那么我们需要的是:
如果文件不存在就创建文件并且在后面进行打开,如果文件已经存在就直接跳过直接执行后面代码。那么文件存在就不会报错并且返回退出进程。那么就上面修改之后就只会报文件存在以外的错误,也就是说我们不认为文件存在时已经错误。那么当我们进程运行起来之后我们需要通过管道来进行通信,进程结束之后我们就没有进程通信的需要了,那么我们就在进程运行结束的时候把管道文件删除,那么通过我们按下ctrl+c进行进程结束,那么我们就可以对于SIGINT信号进行捕获,然后把管道文件进行删除再退出进程。
我们把上面整个代码进行优化:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <strings.h> #include <signal.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <errno.h> #define FIFO_NAME1 "./fifo1" void print_err(char * estr) { perror(estr); exit(-1); } void signal_fun(int signal) { remove(FIFO_NAME1); exit(-1); } int main() { char buf[100] = {0}; int ret = 0; int fd = 0; signal(SIGINT,signal_fun); ret = mkfifo(FIFO_NAME1,0664); if(ret == -1 && errno!=EEXIST) print_err("mkfifo fail"); fd = open(FIFO_NAME1,O_WRONLY); if(fd == -1) print_err("open fail"); while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); write(fd,buf,sizeof(buf)); } return 0; }
运行结果为:
但是signal(SIGINT,signal_fun);不能放在
fd = open(FIFO_NAME1,O_WRONLY);
代码之后,因为当前只有一个写端进行打开,管道文件的读端没有打开的时候,代码就会在
fd = open(FIFO_NAME1,O_WRONLY);
阻塞,不会运行后面的代码。我们可以看到创建出来的管道文件,但是当前只有一个从键盘向管道文件写入数据的操作,并没有一个从从管道读取文件的操作,所以我们先将进程退出,接下来我们编写读取管道数据的代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <strings.h> #include <signal.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <errno.h> #define FIFO_NAME1 "./fifo1" void print_err(char * estr) { perror(estr); exit(-1); } void signal_fun(int signal) { remove(FIFO_NAME1); exit(-1); } int main() { char buf[100] = {0}; int ret = 0; int fd = 0; signal(SIGINT,signal_fun); ret = mkfifo(FIFO_NAME1,0664); if(ret == -1 && errno!=EEXIST) print_err("mkfifo fail"); fd = open(FIFO_NAME1,O_RDONLY); if(fd == -1) print_err("open fail"); while(1) { read(fd,buf,sizeof(buf)); printf("rev:%s\n",buf); } return 0; }
运行结果为:
我们在另一个中断窗口运行read
我们可以看到实现了两个进程之间数据交换。
小结:图解分析
上面的图和无名管道的单向数据交换逻辑是一样的,只不过创建管道文件的方法不一样。
-
Linux -- 有名管道mkfifo单向读写实例
2019-08-08 21:16:08/*有名管道文件标识*/ # define QUIT_STR "quit" int main ( void ) { int fd ; char buf [ 100 ] = { 0 } ; struct stat statbuf ; if ( - 1 == access ( MYFIFO , F_OK ) ) { /*...fifo_write.c
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #define MYFIFO "/tmp/myfifo" /*有名管道文件标识*/ #define QUIT_STR "quit" int main(void){ int fd; char buf[100]={0}; struct stat statbuf; if( -1 == access(MYFIFO, F_OK)){ /*检查文件是否存在*/ if(-1==mkfifo(MYFIFO, 0666)){ /*创建管道文件*/ perror("mkfifo"); exit(EXIT_FAILURE); } }else{ /*文件存在*/ if(-1==lstat(MYFIFO, &statbuf)){ /*获取文件的类型*/ perror("lstat"); } if(!S_ISFIFO(statbuf.st_mode)){ /*判断是否是管道文件*/ unlink(MYFIFO); /*不是管道文件,删除*/ if(-1==mkfifo(MYFIFO, 0666)){ /*创建管道文件*/ perror("mkfifo"); exit(EXIT_FAILURE); } } } if((fd=open(MYFIFO, O_WRONLY))==-1){ perror("open_write"); exit(EXIT_FAILURE); } while(1){ bzero(buf,100); fgets((char *)buf, sizeof(buf)-1, stdin); //fgets写成替换成read(STDIN_FILENO, buf, sizeof(buf)-1); write(fd, buf, sizeof(buf)); if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ /*输入quit退出(不区分大小写)*/ break; } } close(fd); /*关闭文件*/ return 0; }
fifo_read.c
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #define MYFIFO "/tmp/myfifo" /*有名管道文件标识*/ #define QUIT_STR "quit" int main(void){ int fd; char buf[100]={0}; struct stat statbuf; if( -1 == access(MYFIFO, F_OK)){ /*检查文件是否存在*/ if(-1==mkfifo(MYFIFO, 0666)){ /*创建管道文件*/ perror("mkfifo"); exit(EXIT_FAILURE); } }else{ /*文件存在*/ if(-1==lstat(MYFIFO, &statbuf)){ /*获取文件的类型*/ perror("lstat"); } if(!S_ISFIFO(statbuf.st_mode)){ /*判断是否是管道文件*/ unlink(MYFIFO); /*不是管道文件,删除*/ if(-1==mkfifo(MYFIFO, 0666)){ /*创建管道文件*/ perror("mkfifo"); exit(EXIT_FAILURE); } } } if((fd=open(MYFIFO, O_RDONLY))==-1){ perror("open_read"); exit(EXIT_FAILURE); } while(1){ bzero(buf,100); read(fd, buf, sizeof(buf)); printf("%s",buf); if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ /*输入quit退出(不区分大小写)*/ break; } } close(fd); return 0; }
-
通过有名管道实现两个进程单向通讯
2016-07-11 16:22:24写进程 #include #include #include #include #include #include #include #include void writefifo() { char buf[128]; memset(buf, 0, sizeof(buf)); int fd = open("fifo", O_WRONLY)... if (fd == -1)写进程
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> void writefifo() { char buf[128]; memset(buf, 0, sizeof(buf)); int fd = open("fifo", O_WRONLY); if (fd == -1) { printf("error is %s\n", strerror(errno)); } while (1) { scanf("%s", buf); if (buf[0] == '0') break; write(fd, buf, sizeof(buf)); //memset(buf, 0, sizeof(buf)); } close(fd); } int main(void) { writefifo(); return EXIT_SUCCESS; }
读进程void listenfifo() { int len = 0; char buf[128]; memset(buf, 0, sizeof(buf)); int fd = open("fifo", O_RDONLY); if (fd == -1) { printf("error is %s\n", strerror(errno)); } while ((len = read(fd, buf, sizeof(buf))) > 0) { printf("%s\n", buf); //memset(buf, 0, sizeof(buf)); } close(fd); } int main(void) { listenfifo(); return EXIT_SUCCESS; }
-
有名管道
2016-05-07 09:00:14//命名管道特点: //1.如果打开管道的一方仅以读或写的方式打开管道,必须需要另一方的介入,管道才能打开 //双方都调用open //可以一方打开,以读写的方式打开open...单向写入,单向写出 //问答形式 #include #inclu//命名管道特点: //1.如果打开管道的一方仅以读或写的方式打开管道,必须需要另一方的介入,管道才能打开 //双方都调用open //可以一方打开,以读写的方式打开open(DEF_FIFO_PATH,O_RDWR) //发送少量简单无格式的数据 //双方读写都存在,才能打开管道 //计时性存储数据 //2.存在的文件 //3.单向写入,单向写出 //问答形式 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define DEF_FIFO_PATH ("./FIFO") #define DEF_STD_ERROR (-1) int main(int argc,char *argv[]) { int ret; int fd; char str[1024]; //if argv[1] > 0,writer if(atoi(argv[1])>0) { ret = mkfifo(DEF_FIFO_PATH,S_IWUSR | S_IRUSR); if(ret == DEF_STD_ERROR) { if(errno == EEXIST) { printf("Fifo always exist\n"); } else { printf("%d\n",strerror(errno)); return -1; } } //open fifo fd = open(DEF_FIFO_PATH,O_WRONLY)//O_WRONLY紧以读的方式打开,两个进程 if(fd == DEF_STD_ERROR) { printf("%d\n",strerror(errno)); //delete pipe打开失败删除文件(管道) unlink(DEF_FIFO_PATH); return -1; } //write while(1) { printf("Please input string:\n"); scanf("%s",str); write(fd,str,strlen(str)+1); if(!strcmp(str,"goodbye")) { break; } } //close fifo close(fd); } else { //if argv[1] <= 0,reader //check fifo ret = access(DEF_FIFO_PATH,W_OK); if(ret == DEF_STD_ERROR) { printf("%d\n",strerror(errno)); return -1; } //open fifo fd = open(DEF_FIFO_PATH,O_RDONLY); if(fd == DEF_STD_ERROR) { printf("%d\n",strerror(errno)); return -1; } //read fifo while(1) { read(fd,str,sizeof(str)); printf("Receive data:[%s]\n",str); if(strcmp("goodbye",str)==0) { break; } } //close fifo close(fd); } return 0; }
-
有名管道和无名管道
2015-07-28 11:11:57管道: 1. 管道是Unix系统最古老的进程间通信方式,其实质是一个虚拟文件,是在内核中维护了一个消息队列... 有名管道(fifo): 1. 概念:基于有名文件(管道文件)的管道通信 2. 命令形式: # mkfifo fifo 创建管道 -
无名管道和有名管道区别
2016-10-13 17:58:26①管道(无名管道) (1)管道通信的原理:内核维护的一块内存,有读端和写端(管道是单向通信的) (2)管道通信的方法:父进程创建管理后fork子进程,子进程...(1)有名管道的原理:实质也是内核维护的一块内存,表 -
无名管道pipe和有名管道FIFO
2017-03-22 21:04:13定义: 1、管道是单向的,先进先出的,它把一个进程的输入和一个进程的输出连接在一起。...3、管道包括有名管道和无名管道,无名管道只能用于父子进程之间的通信,有名管道可以用于一个系统中任意两个管道之间的通信 -
管道通信的特点及有名管道和无名管道的区别
2019-11-04 13:08:10管道通信的特点 1.管道通信是单向的,有固定的读端和写端。 2.数据被进程从管道读出后,在管道中该数据就不存在了 3.当进程去读取空管道的时候,进程会...无名管道是无名的,有名管道是有名的; 无名管道只能用于... -
进程间通信——有名管道
2019-08-04 11:25:39进程间通信——有名管道 什么是有名管道:有名管道与无名管道的原理... 有名管道的特点:使用mkfifo()系统调用创建管道文件,只要给以进程管道文件的位置,可以用在任意两个进程之间通信,通信的方式是单向的,读... -
进程间通信——管道(有名管道、无名管道)
2017-10-21 03:42:04进程间通信:多个进程之间数据相互交换。 进程间通信的方式:信号、管道...有名管道:应用于两个进程之间数据的单向传递。 创建:命令方式:mkfifo 函数方式:mkfifo() 打开:open 写数据:write 读数据:read -
Linux进程管道通信(有名管道、无名管道)
2018-11-15 17:45:21相关理论 管道是UNIX系统中最早为两个进程之间提供的一种通信机制。管道是一种单向的、先入先出的... 管道分为无名管道和有名管道。无名管道没有名字,所以只能提供给进程家族中的父子进程间通信使用,而有... -
管道(无名管道)和FIFO(有名管道)
2016-05-25 09:55:54进而有名管道(FIFO)应运而生,有名管道有一个路径名与之关联,所以允许无亲缘关系的进程访问同一个FIFO。以下具体介绍管道: 管道的创建:管道由函数 int pipe(int fd[2]) 创建,提供一个单向数据流,该函数返回... -
C语言进程间有名管道和无名管道通信
2019-08-20 21:06:34管道时UNIX系统最古老的...1、有名管道(建立实体文件) 命令:mkfifo 函数:int mkfifo(const char* pathname,mode_t mode); 功能:创建管道文件 Pathname:文件路径 Mode:权限 返回值:文件描述符 返回值:成功返回... -
Linux——Linux系统编程之进程间的通信-无名管道与有名管道的使用总结
2021-01-10 19:37:20在RTOS中,任务之间的通信手段有信号量、邮箱、消息队列,在Linux进程间通信,常用的包括:无名管道、有名管道、消息队列、信号、信号量、共享内存、套接字(socket)。下面分别看下这些通信方式,并有对应的例程... -
进程间通讯——有名管道
2017-10-18 00:04:40进程间通讯:多个进程之间数据相互交换。 进程间通讯的方式: 信号 管道: 无名管道 ... 有名管道: 应用于任意两个进程之间数据的单向传递。 创建: 命令方式: mkfifo 函数方式: mkfifo(); 打开: open -
C 进程间通信--有名管道,无名管道
2018-05-25 17:26:131.管道的概念 管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。...同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞 管道包括无名管道和有名管道两种,前... -
【Linux】进程间通信——管道(有名管道、无名管道)
2018-11-26 12:31:53进程之间通讯方式:管道 信号量 消息队列 共享内存 socket(网络,用于不同主机间通讯) 多进程编程能同时完成多个任务 多进程工作时,进程之间...1.有名管道(命令管道):在磁盘上会存储一个管道文件标... -
透过实例看有名管道与无名管道
2019-03-05 12:18:10***无名管道 创建:int pipe(int fd[2]) 其中fd[0]用来读、fd[1... 管道是单向通道,只能在一端进行数据的写入,另一端进行数据的读出,不能复用。 由于管道属于队列,所以管道中的东西在读取之后就会被删除,... -
管道和FIFO(有名管道) (一)
2018-04-10 20:57:05FIFO: 有名管道,可在无亲缘关系的进程间使用。以上都使用read/write函数访问。管道由pipe函数创建,提供一个单向数据流。#include <unistd.h> int pipe (int fd[2]);该函数返回两个文件描述符... -
进程间通信(管道和有名管道)
2019-07-28 22:02:00管道(Pipe)是两个进程之间进行单向通信的机制,因为它的单向性,所以又称为半双工管道。它主要用于进程间的一些简单通信。 数据只能由一个进程流向另一个进程(一个写管道,一个读管道);如果要进行全双工通信...