网站图片自动切换怎么做,秦皇岛建设部网站,网站建设人才简历,手机域名注册被骗#x1f466;个人主页#xff1a;Weraphael ✍#x1f3fb;作者简介#xff1a;目前正在学习c和算法 ✈️专栏#xff1a;Linux #x1f40b; 希望大家多多支持#xff0c;咱一起进步#xff01;#x1f601; 如果文章有啥瑕疵#xff0c;希望大佬指点一二 如果文章对… 个人主页Weraphael ✍作者简介目前正在学习c和算法 ✈️专栏Linux 希望大家多多支持咱一起进步 如果文章有啥瑕疵希望大佬指点一二 如果文章对你有帮助的话 欢迎 评论 点赞 收藏 加关注 目录 一、见见猪跑二、命名管道的工作原理三、系统调用接口mkfifo四、命名管道和匿名管道的区别特征五、 命名管道的四种情况六、代码 一、见见猪跑
上篇我们学习到的匿名管道主要用于血缘关系的进程之间进行通信而命名管道则可以在毫不相关的进程之间进行通信。命名管道是基于文件系统的因此在使用命名管道进行通信时需要遵循一定的权限规则和文件路径约定原理部分会说。
在命令行上使用mkfifo即可创建命名管道
mkfifo filenamemkfifo命令创建的命名管道在文件系统中显示为一种特殊的文件类型通常以绿色文本显示以便与普通文件区分开来。 创建命名管道后进程可以像操作普通文件一样对其进行读取和写入操作。一个进程可以将数据写入管道另一个进程可以从管道中读取数据。 如果可以看到左端的echo是一个进程右端的cat也是一个进程它们两是毫无相关的而通过命名管道可以让这两个毫无相关的进程通信。
但需要注意的是如果没有任何进程打开了管道进行读取操作那么写入操作将永远被阻塞直到有进程打开管道进行读取操作为止。这种行为确保了在进程之间进行通信时的同步性即写入数据的进程会等待读取数据的进程准备好后再继续执行从而避免了数据丢失或混乱。
二、命名管道的工作原理 我们知道当进程打开文件时操作系统会在内核中创建struct file结构体对象来描述这个被打开的文件。而一个进程可以打开多个文件那进程结构体对象task_struct就要存储哪些文件是由哪一个进程打开的。因此每个进程的task_struct对象都要和打开的文件建立关系所以每个task_struct对象其实有一个指针struct files_struct* files这个指针指向结构体files_struct而这个结构体包含一个指针数组struct file* fd_array[]这个数组我们可以称之为文件描述符表。数组中的每个元素都是指向当前进程所打开文件的指针地址
而如果两个不同的进程打开同一个文件操作系统只会为这个打开的文件创建一个struct file结构体对象只不过这个文件的结构体对象有一个引用计数计数器字段会是2表示当前有两个进程打开此文件。
这意味着多个进程可以共享相同的文件资源各自进程的文件描述表指向同一个文件结构体对象这不就是进程间通信的本质让不同的进程能共享同一份资源。一个进程可以向文件写入数据而另一个进程则可以从文件中读取这些数据从而实现了通信
而在上面我们看到当一个进程向管道文件写入数据时另一个进程在查看该管道文件属性时其文件大小却是0但还是可以从该管道文件中获取数据现象。这是为什么
其原因是不管是匿名管道还是命名管道写入数据的时候并不是将数据直接刷新到磁盘访问磁盘有I/O效率问题而是存储在内核缓冲区中因为操作系统管理着内核中每一个被打开的文件那么操作系统就很清楚每一个被打开文件的类型。因此如果操作系统识别是普通文件那么就根据内核缓冲区的刷新策略直接刷新到磁盘上而如果识别到是管道文件那么数据不会马上刷新到磁盘上而是存储在内核缓冲区中等待另外一个进程读取数据。因此命名管道或匿名管道通常被视为内存级文件。
接下来还有一个边角问题在匿名管道中我们可以通过子进程通过继承父进程的文件描述符表使得不同的进程可以打开同一个管道文件那在命名管道中我们怎么保证两个不同的进程打开的是同一个管道文件呢
这就设计到【文件系统】的知识了因为磁盘中一定会存在大量没有被打开的文件那么就需要管理磁盘中每一个文件的属性注定了存在大量的inode结构文件属性的集合。因此为了区分每个文件的inode结构操作系统会为每个inode结构取一个唯一编号即inode编号。所以想要对文件操作操作系统只认inode编号但用户都是通过文件名来对文件操作的呀这又是因为文件名和inode编号的关系是由目录来建立和维护的目录文件的数据块存储着文件名和inode编号的映射关系。
因此当不同进程通过需要通过同一个命名管道文件进行通信必然要找到命名管道的文件路径那么操作系统会根据文件名到指定的目录下的数据块中查找文件名映射的inode编号。因为inode编号是唯一的从而可以确保这些进程打开的是同一个管道文件。
三、系统调用接口mkfifo
除了可以用makefifo命令来创建命名管道之外还可以使用系统调用接口mkfifo函数来创建。
#include sys/types.h
#include sys/stat.hint mkfifo(const char *pathname, mode_t mode);其中 pathname指定了要创建的命名管道的路径包含文件名 mode指定了创建出来管道文件的权限类似于系统调用接口open函数的第三个参数。 返回值 成功创建命名管道时mkfifo函数返回0 失败则返回-1
代码样例思路首先为了能让两个毫不相干的进程能够通信创建两个源文件分别是服务端server.cc和客户端client.cc。服务端主要是创建命名管道、读取管道数据和最后回收命名管道而客户端主要是向管道中写入数据。 补充.cc结尾的就是C源文件的后缀名。常见后缀名有很多种如.cpp、.cc 或者 .cxx等这取决于个人或团队的偏好。 comm.hpp这个头文件主要是用来存放整个项目所需要的头文件以及自定义的错误码。 server.cc创建命名管道、读取管道数据和最后回收命名管道。详细内容可以看代码注释非常详细 client.cc向管道中写入数据。详细内容可以看代码注释非常详细 程序演示 需要注意的是
必须要先运行sever端进程因为要先将管道文件创建好后才能开始通信。服务端启动后因为是读端所以会阻塞等待客户端写端写入数据所以你会看到运行完服务端后会有光标一直在闪烁。命名管道文件需要写端和读端都打开的时候才可以开始通信否则只打开其中一个端会进入阻塞。我们可以理解为这是为了防止只打开读端而不打开写端这是因为不打开写端读端read函数就会返回0那么读端也会退出
四、命名管道和匿名管道的区别特征 命名管道的特征大致和匿名管道一样
匿名管道和命名管道都是通过文件描述符进行通信的无论是匿名管道还是命名管道都是单向通信的即数据只能从一端流向另一端。面向字节流进程是会协同的同步与互斥的
不同点在于
匿名管道通常用于有亲缘关系的进程之间的通信比如父子进程而命名管道可以用于没有亲缘关系的进程之间的通信它是一种独立的通信方式。匿名管道直接通过pipe函数创建使用然后创建子进程进行通信而命名管道需要先通过mkfifo函数创建然后再通过open打开使用。在匿名管道中如果父进程创建多个子进程会出现写端重复继承的情况导致子进程之间也能互相通信而命名管道不会出现这种情况。
五、 命名管道的四种情况
命名管道的四种情况和匿名管道的四种情况差不多主要的区别在于第四点 读写端正常管道如果为空读端就会被阻塞等待写端的数据。读写端正常管道为满时写端阻塞等待读端读取数据。读端正常读写端关闭那么读取端会在读取完管道中的所有数据后得到一个特殊的信号表明已经到达了管道的末尾。这样读取操作就会返回值为0也就是read函数会返回0而不会再被阻塞不会再等待管道中会有新的数据。因此当写端关闭后读端就知道不会再有新的数据写入管道可以安全地关闭管道结束通信。 当我ctrl c终止了写端对应读端也退出了 写端正常读端关闭。写入操作不会因为读端关闭而阻塞写入进程可以继续向管道写入数据直到读取进程再次打开读端或者管道被关闭或者管道被写满。 这也是唯一和匿名管道的区别对于匿名管道写端正常读端关闭那么操作系统会为写端发送SIGPIPE信号终止该进程。这是因为在匿名管道中通信的两个进程是有血缘关系的读取端关闭后写入端无法将数据传递给任何进程所以操作系统只能终止而命名管道的任意进程之间都可以通信所以写端只管写就行。 六、代码
Gitee代码仓库点击跳转