linux进程间通信------命名管道
1.命名管道命名管道FIFO是一种通过文件路径标识的特殊文件能够为不相关进程提供流式通信能力任意进程只需要通过统一路径打开该文件即可实现跨进程数据交换其内核缓冲区独立于创建者生命周期存在但本质仍是无消息边界的单向字节流通道。1.1创建命名管道命名管道可以从命令上创建mkfifo filename从程序上创建//int mkfifo(const char* filename,mode_t mode); int main() { mkfifo(h1,0644); return 0; }mode_t mode是文件掩码用于指定管道权限规则同open/chmod实际权限mode~umask能够支持标准宏或自定义八进制值常用权限0644所有者读写其余只读0600仅所有者读写0666读写权限最终0666~umask注意mode只控制新建管道的访问权限不含执行权限mkfifo函数成功返回0失败返回-1同时设置errno1.2匿名管道与命名管道的区别匿名管道由pipe函数创建并打开命名管道由mkfifo创建用open打开FIFO命名管道与pipe匿名管道之间的唯一区别在于它们创建与打开的方式不同一旦这些工作完成后就具有相同的语义了且它们都是单向通道1.3命名管道的打开方式当前打开操作为读而打开FIFO时O_NONBLOCK disable阻塞直到有进程为写而打开FIFOO_NONBLOCK enable立即返回成功当前打开操作为写而打开FIFO时O_NONBLOCK disable阻塞直到有进程为读而打开FIFOO_NONBLOCK enable立即返回失败错误码为ENXIO2.用命名管道实现文件拷贝linux内核中规定FIFO管道必须有读和写两端同时打开才能正常通信通过两个进程分别运行不同的程序实现使用命名管道进行文件拷贝进程A进行创建命名管道tp并以只读的模式打开在当前目录下的普通文件abc作为进行拷贝的源文件。以只写模式打开刚创建的tp管道后会被阻塞挂起等待另一个进程B以只读模式打开同一个tp管道随后将源文件的数据写入tp中。#includecstdio #includefcntl.h #includesys/stat.h #includesys/types.h #includecstdlib #includecerrno #includeunistd.h #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) int main(int argc,char* argv[]) { if(mkfifo(tp,0644)-1errno!EEXIST) ERR_EXIT(mkfifo); int infd; infdopen(abc,O_RDONLY); if(infd-1) ERR_EXIT(open); int outfd; outfdopen(tp,O_WRONLY); if(outfd-1) ERR_EXIT(open); char buf[1024]; ssize_t n; while((nread(infd,buf,1024))0) { write(outfd,buf,n); } close(infd); close(outfd); return 0; }进程B以只写的形式打开当前目录下普通文件abc.bakO_CREAT|O_TRUNC指的是文件若不存在则创建若已存在则清空文件内容。以只读的形式打开已存在的管道tp此时写端进程A的阻塞会被解除两端开始进行连接随后循环不断读取tp管道内的内容写入文件abc.bak直到管道数据读取完毕也就是收到EOF时关闭文件描述符调用unlink删除管道文件tp清理资源#includecstdio #includefcntl.h #includesys/stat.h #includesys/types.h #includecstdlib #includecerrno #includeunistd.h #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) int main(int argc,char* argv[]) { int outfd; outfdopen(abc.bak,O_WRONLY|O_CREAT|O_TRUNC,0644); if(outfd-1) ERR_EXIT(open); int infd; infdopen(tp,O_RDONLY); if(infd-1) ERR_EXIT(open); char buf[1024]; ssize_t n; while((nread(infd,buf,1024))0) { write(outfd,buf,n); } close(infd); close(outfd); unlink(tp); return 0; }3.使用命名管道实现客户端与服务器端通信这是一个基于命名管道FIFO单向通信服务端其设计为单客户端模式单次运行仅负责接收一个客户端所发的消息并且引入了信号处理机制通过捕捉信号SIGINT信号也就是ctrlc可以确保用户强行中断进程时能够自动清理残留的管道文件tp客户端#includeiostream #includecstring #includefcntl.h #includecerrno #includeunistd.h using namespace std; #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0); int main() { int wfdopen(tp,O_WRONLY); if(wfd-1) ERR_EXIT(open); char buf[1025]; coutplease wait...endl; while(1) { fflush(stdout); ssize_t nread(0,buf,sizeof(buf));//read会从buf[0]开始写 if(n0) { buf[n]0; ssize_t swrite(wfd,buf,n); if(s0) ERR_EXIT(write); } else if(n0) { break; } else { ERR_EXIT(read); } } if(close(wfd)-1) ERR_EXIT(close); return 0; }服务器端#includeiostream #includecstring #includefcntl.h #includecerrno #includeunistd.h #includesignal.h #includesys/types.h #includesys/stat.h using namespace std; #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0); const char* file_nametp; void clean_and_exit(int sig) { unlink(file_name); _exit(0); } int main() { umask(0); unlink(file_name);//删除同名文件 if(mkfifo(file_name,0644)-1) ERR_EXIT(mkfifo); //注册信号处理函数处理使用ctrlc发送结束进行的信号 signal(SIGINT,clean_and_exit); int rfdopen(file_name,O_RDONLY); if(rfd0) ERR_EXIT(open); char buf[1024]; coutplease wait...endl; while(1) { ssize_t nread(rfd,buf,sizeof(buf)); if(n0) { coutclient says# ; cout.write(buf,n);//按精度长度输出避免越界 } else if(n0) { coutclient disconnectedendl; break; } else { if(errnoEINTR) continue; ERR_EXIT(read); } } close(rfd); unlink(file_name); return 0; }
