网站建设贰金手指科捷9,wordpress4.8.3下载,中英文网站建设用两个域名,wordpress移动端悬浮导航代码目录 一、进程间通信的目的#xff1a;二、进程间通信的种类三、什么是管道四、匿名管道#xff08;共同祖先的进程之间#xff09;1.匿名管道的使用2.匿名管道举例3.匿名管道的原理4.管道特点5.管道的读写规则1. 当管道内没有数据可读时2.当管道满的时候3.管道端被关闭4.数… 目录 一、进程间通信的目的二、进程间通信的种类三、什么是管道四、匿名管道共同祖先的进程之间1.匿名管道的使用2.匿名管道举例3.匿名管道的原理4.管道特点5.管道的读写规则1. 当管道内没有数据可读时2.当管道满的时候3.管道端被关闭4.数据写入的原子性 五、命名管道让两个不相干的进程通信1.命名管道的概念与原理2.命名管道的创建与删除3.匿名管道与命名管道的区别4.命名管道的打开规则5.命名管道举例附完整代码1.makefile文件2.wFifo.cpp3.rFifo.cpp 六、关于匿名管道与命名管道的细节问题匿名管道命名管道 一、进程间通信的目的
在学习通信间进程前我们要明白进程间通信的目的意义是是什么从而方便我们在学习的过程中对我们所学的知识有一个清晰的认知
1.传输数据 将一个进程的数据发送给另一个进程
2.资源共享 让多个进程共享所需要的资源
3.通知事件 一个进程向另一个进程发送消息通知它发生了某种事件如进程终止时要通知父进程
4.进程控制 有些进程希望完全控制另一个进程的执行如Debug进程此时控制进程希望能够拦截另一个进程的所有陷入和异常并能够及时知道它的状态改变
二、进程间通信的种类
1.管道
匿名管道命名管道
2.System V
System V 消息队列System V 共享内存System V 信号量
3.POSIX
消息队列共享内存信号量互斥锁条件变量读写锁
在本章中主要讲解管道通信
三、什么是管道
管道是操作系统中最古老的进程间通信的方式我们把一个进程连接到另一个进程的一个数据流称为一个“管道” 四、匿名管道共同祖先的进程之间
1.匿名管道的使用
#include unistd.h
int pipe(int fd[2]);匿名管道的功能是创建一个无名管道
fd文件描述符数组其中fd[0]表示读端fd[1]表示写端返回值成功返回0失败返回错误代码 如果上述看的不太明白的话可以去看看这篇文章里面有对文件描述符及数组的详细讲解 https://blog.csdn.net/liuty0125/article/details/140828056?spm1001.2014.3001.5502
2.匿名管道举例
1.先创建管道 进而创建子进程 父子进程使用管道进行通信
父进程向管道当中写“i am father”
子进程从管道当中读出内容, 并且打印到标准输出
#includeunistd.h
#includesys/types.h
#includestdio.h
#includestdlib.h
#includestring.h
#includestring
using namespace std;
int main()
{int fd[2];int fpipe(fd);char buf[100];const char* faI am father;int lenstrlen(fa);if(f-1){perror(make pipe);exit(1);}int pid fork();if(pid-1){perror(fork failed);return 1;}if(pid0)//父进程{write(fd[1],fa,len);}if(pid0){read(fd[0],buf,len);printf(%s,buf);}return 0;
}2.从键盘读取数据写入管道读取管道写到屏幕
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#includesys/types.h
#includestring
int main(void)
{int fds[2];char buf[100];int len;if (pipe(fds) -1)perror(make pipe), exit(1);// read from stdinwhile (fgets(buf, 100, stdin)) {len strlen(buf);// write into pipeif (write(fds[1], buf, len) ! len) {perror(write to pipe);break;}memset(buf, 0x00, sizeof(buf));// read from pipeif ((len read(fds[0], buf, 100)) -1) {perror(read from pipe);break;}// write to stdoutif (write(1, buf, len) ! len) {perror(write to stdout);break;}}
}3.匿名管道的原理
我们知道进程将一个文件以读方式打开一次会产生一个FILE*再以写方式打开一次也会产生一个FILE*但是这两个文件流指针指向的是同一个缓存区
内容只有一份基于文件的让不同进程看到同一份资源的通信方式叫做管道管道只能被设计成为单向通信
如ajx | ps | … 这中间的管道是兄弟关系
我们可以让父子进程都打开这个文件再让父进程关闭读端子进程关闭写端这样就形成了一条由父进程通向子进程的管道反之则形成由子进程通向父进程的管道 在这个地方其实有个bug
通过父进程创建多个子进程用管道文件连接父进程与各个子进程产生的管道文件有多个读端因为每个子进程的文件描述符表都会和创建他的父进程的文件描述符表一样父进程的读端没有关闭所以与接连创建的各个子进程的管道会带有上个父进程的读端
解决方案1最后一个管道一定只有一个读写端可以从后往前删
解决方案2每次创建管道文件时记录父进程的fd然后每次创建时通过记录的上个父进程的文件描述符关闭不需要的fd
4.管道特点
匿名管道只能用于共同祖先的进程之间进行通信一般都是一个管道由一个进程创建然后该进程调用fork此后父子进程之间就可以应用改管道一般而言匿名管道的生命周期随进程进程退出管道释放管道是半双工的数据只能向一个方向流动需要双方通信时则需要建立起两个管道 5.管道的读写规则
管道读写操作的行为会根据是否设置了非阻塞标志O_NONBLOCK以及管道的状态是否有数据可读、是否已满等有所不同
1. 当管道内没有数据可读时
O_NONBLOCK 未设置阻塞模式 read调用阻塞即进程暂停执行一直等到有数据来到为止 O_NONBLOCK 已设置非阻塞模式 read调用返回-1errno值为EAGAIN 2.当管道满的时候
O_NONBLOCK 未设置阻塞模式 write调用阻塞直到有进程读走数据 O_NONBLOCK 已设置非阻塞模式 调用返回-1errno值为EAGAIN 3.管道端被关闭
所有管道的写端对应的文件描述符被关闭 read 调用会返回 0表示已经到达文件末尾EOF没有更多的数据可以读取 所有管道的读端对应的文件描述符被关闭 write 调用会产生 SIGPIPE 信号默认情况下这会导致进程终止。可以通过捕获 SIGPIPE 信号来避免进程终止 4.数据写入的原子性
当要写入的数据量不大于 PIPE_BUF 时 Linux 保证写入的原子性即要么全部写入成功要么完全不写入。这有助于避免数据在管道中被分割成多个部分从而保证了数据的完整性。 当要写入的数据量大于 PIPE_BUF 时 Linux 不再保证写入的原子性数据可能会被分割成多个部分写入管道。这可能导致读端在读取数据时需要多次调用 read 才能获取完整的数据。 五、命名管道让两个不相干的进程通信
1.命名管道的概念与原理
学习了上面的匿名管道我们知道匿名管道其实是有缺陷的它只能在具有共同祖先具有亲缘关系的进程间通信,那如果我们想在不相关的进程之间交换数据呢
想要在两个不相干进程之间进行通信我们的目的是让不同进程看到同一份资源
比如进程A通过文件描述符表打开一个文件会为文件创建一个struct FILE并通过文件缓冲区与磁盘进行交互
进程B也通过文件描述符打开同一个文件也会为文件再创建一个struct FILE但会使用同一个缓冲区指向同一个内存空间因此我们就可以通过这段内存空间进行通信
不过我们只是需要两个进程之间进行通信并不需要把打开的文件内容写到缓冲区中因此这里的文件是一个特殊的文件也就是我们下面说的命名管道
如果我们想在不相关的进程之间交换数据可以使用FIFO文件来做这项工作它经常被称为命名管道
怎么保证两个进程打开的是同一个文件
只需要保证文件的路径与文件名一样即可因为路径具有唯一性且同一路径下不能有同名文件
2.命名管道的创建与删除
1.在命令行上创建
使用 mkfifo filename p开头为管道文件
2.在程序中创建
int mkfifo(const char *filename,mode_t mode);int main(int argc, char *argv[])
{mkfifo(p2, 0644);return 0;
}3.删除文件 使用unlink()删除文件可以用于析构函数
unlink(filename)也就是说想让两个不相干的进程通信只需通过mkfifo创建一个管道文件一个进程写一个进程读即可
在程序中管道文件只能创建一个一个进程创建后另一个进程只需直接操作就行不需要再创建管道文件
管道中的通信都是基于文件的可以说管道通信是基于文件的二次创新
如果写端没打开而去先打开读端读端open时会阻塞直到把写端打开读端才会返回
3.匿名管道与命名管道的区别
匿名管道由pipe函数创建并打开命名管道由mkfifo函数创建打开用openFIFO命名管道与pipe匿名管道之间唯一的区别在它们创建与打开的方式不同一但这些工作完成之后它们具有相同的语义
4.命名管道的打开规则
为读而打开FIFO
O_NONBLOCK 未设置阻塞模式 如果当前没有进程为写而打开该FIFO则打开操作会阻塞直到有进程以写模式打开它。这意味着如果FIFO是空的并且没有写端打开那么尝试以读模式打开FIFO的进程将被挂起直到另一个进程以写模式打开它 O_NONBLOCK 已设置非阻塞模式 如果当前没有进程为写而打开该FIFO则打开操作会立即成功但是后续的read操作可能会失败并返回-1同时设置errno为EAGAIN表示资源暂时不可用可以在以后重试。 为写而打开FIFO
O_NONBLOCK 未设置阻塞模式 如果当前没有进程为读而打开该FIFO则打开操作会阻塞直到有进程以读模式打开它。这意味着如果FIFO没有读端那么尝试以写模式打开FIFO的进程将被挂起直到另一个进程以读模式打开它 O_NONBLOCK 已设置非阻塞模式 如果当前没有进程为读而打开该FIFO则打开操作会立即失败并返回-1同时设置errno为ENXIO表示设备不存在或没有这样的设备文件。 在非阻塞模式下如果没有读端可用写端不能立即打开FIFO。 5.命名管道举例附完整代码 创建出来的命名管道可以供两个进程通信 进程A 向管道当中写 “i am process A” 进程B 从管道当中读 并且打印到标准输出 1.makefile文件
.PHONY:all
all:w rw:wFifo.cppg -o $ $^ -stdc11r:rFifo.cppg -o $ $^ -stdc11
PHONY:clean
clean:rm -f r w2.wFifo.cpp
#include sys/stat.h
#include sys/types.h
#include fcntl.h
#include string
#include string.h
#include errno.h
#include strings.h
#include stdio.h
#includeunistd.hvoid errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s/home/xiaoliu/code/Fifo/__fifo;char a[1024]i am process A;mkfifo(s,0666);int wfdopen(s,O_WRONLY);if(wfd 0){errS(open);}int wwrite(wfd,a,sizeof(a-1));if(w0){errS(write);}return 0;
}3.rFifo.cpp
#include sys/stat.h
#include sys/types.h
#include fcntl.h
#include string
#include string.h
#include errno.h
#include strings.h
#include stdio.h
#includeunistd.h
#include iostreamvoid errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s/home/xiaoliu/code/Fifo/__fifo;char buf[1024];int rfdopen(s,O_RDONLY);if(rfd0){errS(open);}int n read(rfd,buf,sizeof(buf)-1);if(n0){buf[n]\0;std::coutA said: buf;}else if(n0){std::coutA didnt speak;}else{std::cerr read failed, errno: errno , errstring: strerror(errno) std::endl;}return 0;
}六、关于匿名管道与命名管道的细节问题
匿名管道
进程之间可以通过地址访问进行相互通行吗 错误进程之间具有独立性拥有自己的虚拟地址空间因此无法通过各自的虚拟地址进行通信A的地址经过B的页表映射不一定映射在什么位置 所有的进程间通信都是通过内核中的缓冲区实现的吗 错误除了内核中的缓冲区之外还有文件以及网络通信的方式可以实现 管道的容量仅受磁盘容量大小限制吗? 错误管道的本质是内核中的缓冲区通过内核缓冲区实现通信命名管道的文件虽然可见于文件系统但是只是标识符并非通信介质 一个管道只能有一个读进程或写进程对其操作吗 错误多个进程只要能够访问同一管道就可以实现通信不限于读写个数 程对管道进行读操作和写操作都可能被阻塞吗 正确管道自带同步没有数据读阻塞缓冲区写满写阻塞与互斥 管道的本质是内核中的一块缓冲区 正确管道本质是内核中的一块缓冲区多个进程通过访问同一块缓冲区实现通信 命名管道
.命名管道可以用于同一主机上的任意进程间通信 正确命名管道可用于同一主机上的任意进程间通信 向命名管道中写入的数据越多则管道文件越大 错误管道的通信本质是通过内核中一块缓冲区内存时间数据传输而命名管道的管道文件只是一个标识符用于让多个进程能够访问同一块缓冲区不是磁盘文件 多个进程在通过管道通信时删除管道文件则无法继续通信 错误管道的生命周期随进程本质是内核中的缓冲区命名管道文件只是标识用于让多个进程找到同一块缓冲区删除后之前已经打开管道的进程依然可以通信 命名管道的本质和匿名管道的本质相同都是内核中的一块缓冲区 正确 .命名管道在磁盘空间足够的情况下可以持续写入数据 错误管道在缓冲区写满后会写阻塞跟磁盘空间并无关系 若以只读或只写的方式打开命名管道时则会阻塞 正确当一个进程尝试以只读方式打开命名管道时如果该管道尚未被任何进程以写方式打开则打开操作会阻塞阻塞将持续到另一个进程以写方式打开该管道为止。此时读进程可以继续进行并尝试从管道中读取数据。反之亦然