营销活动策划网站,黄山网站建设,南通网站公司网站,深圳排名网站【Linux】进程间通信之管道 进程间通信进程间通信目的进程间通信的方式 管道#xff08;内核维护的缓冲区#xff09;匿名管道#xff08;用于父子间进程间通信#xff09;简单使用阻塞状态读写特征非阻塞状态读写特征 匿名管道特点命名管道 匿名管道与命名管道的区别 进程… 【Linux】进程间通信之管道 进程间通信进程间通信目的进程间通信的方式 管道内核维护的缓冲区匿名管道用于父子间进程间通信简单使用阻塞状态读写特征非阻塞状态读写特征 匿名管道特点命名管道 匿名管道与命名管道的区别 进程间通信
进程之间具有独立性进程间通信必定需要一块公共的区域用来作为信息的存放点操作系统需要直接的或间接给通信进程双方提供内存空间。 如果内存空间是文件系统提供的那么就是管道通信 如果OS提供的就是共享内存 通信的本质就是让不同的进程看到同一份内存空间
进程间通信目的
数据传输一个进程需要将它的数据发送给另一个进程
资源共享多个进程之间共享同样的资源
通知事件一个进程需要向另一个或一组进程发送消息通知它它们发生了某种事件如进程终止时要通知父进程
进程控制有些进程希望完全控制另一个进程的执行如Debug进程
进程间通信的方式
管道 匿名管道命名管道 System VSystem V 消息队列System V 共享内存System V 信号量 POSIX消息队列共享内存信号量互斥量条件变量读写锁
管道内核维护的缓冲区
管道是基于文件系统的通信方式
任何一个文件包括两套资源 1.file的操作方法 2.有属于自己的内核缓冲区所以父进程和子进程有一份公共的资源文件系统提供的内核缓冲区父进程可以向对应的文件的文件缓冲区写入子进程可以通过文件缓冲区读取此时就完成了进程间通信这种方式提供的文件称为管道文件。管道文件本质就是内存级文件不需要IO。
匿名管道用于父子间进程间通信
通过父进程fork创建子进程让子进程拷贝父进程中的文件描述符表两个进程便能看到同一个管道文件这个管道文件是一个内存级文件并没有名字所以被称为匿名管道
匿名管道中父子进程必须关闭一个文件描述符形成单向通信
简单使用
#include unistd.h
int pipe(int pipefd[2]);
fd文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
成功时返回零错误时返回 -1#include iostream
#include unistd.h
#include cassert
#include cstring
#include sys/wait.h
#include sys/types.h
using namespace std;
//父进程进行读取子进程负责写入
int main()
{//第一步创建管道文件打开读写端//fds[0]读 fds[1]写int fds[2];int npipe(fds);assert(n0);//forkpid_t idfork();assert(id0);if(id0){//子进程写入先关闭读close(fds[0]);//子进程通信代码const char* s我是子进程我正在给你发信息;int cnt0;while (true){cnt;char buffer[1024];//只有子进程能访问//格式化写入snprintf(buffer,sizeof(buffer),chile-parent say: %s[%d][%d],s,cnt,getpid());//子进程向【fd[1]】不断写父进程【fd[0]】延迟读导致写阻塞write(fds[1],buffer,strlen(buffer));//coutcountcntendl;//sleep(5);//延迟写父进程read阻塞//break;//写一次直接关闭读端读端read返回0,}//出来还是子进程close(fds[1]);//子进程关闭写端fdcout子进程关闭了自己的写端endl;exit(0);}//父进程读取close(fds[1]);//父进程通信代码while(true){//sleep(3000);//写端不断写读端延迟读写端阻塞//sleep(2);//写端不断写读端缓读数据积累在一起被读出char buffer[1024];//如果管道中没有数据读端在读默认阻塞当前读进程由R-S状态//coutparent_read_begin...endl;ssize_t s read(fds[0],buffer,sizeof(buffer)-1);//阻塞式调用//coutparent_read_end...endl;if(s0){buffer[s]0;cout父进程读到了# buffer | my_pid: getpid()endl;}else if(s0){//读到了文件末尾coutread: over sendl;break;}break;//读一次直接退出父进程准备关闭读端os会发送信号杀掉写进程}//读未关闭// nwaitpid(id,nullptr,0);// assert(nid);// close(fds[0]);//关闭读端程序无效close(fds[0]);cout父进程关闭写端endl;int status0;nwaitpid(id,status,0);获取进程终止信号coutwaitpid-n : (status0x7f)endl;return 0;
}阻塞状态读写特征
1如果管道中没有数据读取端进程再进行读取会阻塞当前正在读取的进程
2如果写入端写满了再写就会对该进程进行阻塞需要等待对方对管道内数据进行读取
3如果写入进程关闭了写入fd读取端将管道内的数据读完后read的返回值为0正常退出
4如果读关闭写就没有意义了操作系统会给写端发送13号信号SIGPIPE终止写端。信号参考
非阻塞状态读写特征
创建的匿名管道默认是阻塞状态的如果要设置成非阻塞状态需要使用fcntl 第一种写设置为非阻塞 1读端不关闭写端一直写写满之后wirte会返回-1报错当前资源不可用 2读端直接关掉写端一直在写当前进程会收到SIGPIPE信号写端的程序直接被杀死这种现象叫做管道破裂 第二种读设置为非阻塞 1写端不关闭读端一直读read调用返回-1errno为EAGAIN 2写端关闭读端进行读read是正常调用返回0表示没有读到
匿名管道特点
1只能用于具有共同祖先的进程具有亲缘关系的进程之间进行通信通常一个管道由一个进程创建然后该进程调用fork此后父、子进程之间就可应用该管道
2管道提供流式服务
3进程退出管道释放所以管道的生命周期随进程
4内核会对管道操作进行同步与互斥
5管道是半双工。需要双方通信时需要建立起两个管道 6管道的大小是64K(65536) 7创建匿名管道返回的文件描述符属性默认是阻塞的
命名管道
上面说到匿名管道只能用于具有共同祖先的进程具有亲缘关系的进程之间进行通信两个不相关的进程如果要通信需要借助命名管道
创建命名管道
int mkfifo(const char *pathname, mode_t mode);
pathname命名管道文件所在路径
mode文件权限删除命名管道
#include unistd.h
int unlink(const char *path);命名管道可用于无血缘关系的进程间通信两个进程均打开同一路径下的同名文件看到同一份资源命名管道也是一个内存级文件 comm.hpp提供命名管道的创建和销毁方法
#pragma once
#include iostream
#include string
#include cstring
#include cerrno
#include cassert
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define NAMED_PIPE ./myname_pipebool CreateFifo(const std::string path)
{umask(0);int nmkfifo(path.c_str(),0600);if(n0)return true;else{std::couterrno: errno err string: strerror(errno)std::endl;return false; }
}void removeFifo(const std::string path)
{int nunlink(path.c_str());assert(n0);std::coutremoveFifo success ....std::endl;(void)n;//避免爆出waring未引用的变量n
}server.cc读端先运行
#include comm.hppint main()
{bool rCreateFifo(NAMED_PIPE);assert(r);(void)r;//先运行此文件会在open时阻塞//管道文件要求读写都要打开文件才能执行后面的功能std::cout server begin std::endl;int rfdopen(NAMED_PIPE,O_RDONLY);std::cout server end std::endl;if(rfd0) exit(1);//readchar buffer[1024];while (true){ssize_t sread(rfd,buffer,sizeof(buffer)-1);if(s0){buffer[s]0;std::coutcilent -server# bufferstd::endl;}else if(s0){std::coutcilent quit,me tool std::endl;break;}else{std::couterr string: strerror(errno)std::endl;break;}}close(rfd);sleep(10);removeFifo(NAMED_PIPE);return 0;}client.c写端
#include comm.hppint main()
{//如果先运行此文件由于文件还未创建无法写入std::cout client begin std::endl;int wfdopen(NAMED_PIPE,O_WRONLY);std::cout client end std::endl;if(wfd0) exit(1);//writechar buffer[1024];while (true){std::coutPlease Say#;fgets(buffer,sizeof buffer,stdin);if(strlen(buffer)0)buffer[strlen(buffer)-1]0;ssize_t nwrite(wfd,buffer,strlen(buffer));assert(nstrlen(buffer));(void)n; }close(wfd);return 0;}匿名管道与命名管道的区别
1匿名管道由pipe函数创建并打开。 2命名管道由mkfifo函数创建打开用open 3FIFO命名管道与pipe匿名管道之间唯一的区别在它们创建与打开的方式不同一但这些工作完成之后它们具有相同的语义