做企业邮箱的网站,比较好的推广平台,网站建设费用选择网络专业,个人网站开发技术要求文章目录 一、进程通信原理#xff08;让不同进程看到同一份资源#xff09;二、管道通信2.1 管道原理及其特点2.1 匿名管道和命名管道 三、共享内存通信3.1 共享内存原理3.2 创建和关联共享内存3.3 去关联、ipc 指令和删除共享内存 四、消息队列和信号量#xff08;了解让不同进程看到同一份资源二、管道通信2.1 管道原理及其特点2.1 匿名管道和命名管道 三、共享内存通信3.1 共享内存原理3.2 创建和关联共享内存3.3 去关联、ipc 指令和删除共享内存 四、消息队列和信号量了解4.1 消息队列4.2 信号量4.3 system V 一、进程通信原理让不同进程看到同一份资源 尽管每个进程在其独立的地址空间中运行它们之间并没有直接共享的内存区域但所有进程都共享同一个OS操作系统。这个共享的操作系统会提供相应的进程通信机制如共享内存、消息传递、信号、管道、套接字等。这些机制允许进程间实现数据的转发和共享即便它们在内存中没有直接共享的空间。 因此进程通信Inter-Process Communication, IPC的原理就是操作系统是所有进程共享的第三方实体为进程提供了一套丰富的通信工具和协议使得进程能够在保持独立性的同时又让不同的进程看到同一份资源以此实现数据的共享和任务的协调。这种设计不仅提高了系统的稳定性和效率还确保了通信的安全性和有效性能够管理了进程间的同步和互斥防止数据在传输过程中发生冲突。
二、管道通信
2.1 管道原理及其特点 我们把从一个进程连接到另一个进程的一个数据流称为一个 管道这个数据流就是一段内核缓冲区默认大小为 4kb会根据实际情况做适当的调整。也可以叫做伪文件不会刷新到磁盘。管道的操作形式是基于文件的它只允许单向通信如果要双向通信的话需要建立两个管道“互相读写” 管道通信时可能会遇到以下四种情况 1、读写端正常管道如果为空读端就要阻塞2、读写端正常管道如果被写满写端就要阻塞3、读端正常读写端关闭读端就会读到0表明读到了文件(pipe)结尾不会被阻塞4、写端正常写读端关闭了。操作系统就要向目标文件发送 SIGPIPE(13) 信号杀掉正在写入的进程 管道通信特点 1、管道是基于文件的且文件生命周期是跟随进程的2、为了保护管道文件的数据安全进程之间需要进行进程协同同步与互斥3、匿名管道仅允许有血缘关系的进程常用于父子进行通信且是单向通信的4、是面向字节流的面向字节流读取, 跟写的时候的格式无关, 读取的时候只跟字节数有关 2.1 匿名管道和命名管道
匿名管道int pipe(int pipefd[2]); pipefd 数组是一个输入输出型参数创建成功返回 0创建失败返回 -1并设置错误码pipefd[0] 表示读端文件描述符pipedf[1] 表示写端文件描述符。匿名管道只允许有血缘关系的进程之间进行进程间通信管道是单向通信的在我们 fork 之后应让父子进程中的一方写入另外一方读取。
命名管道int mkfifo(const char *pathname, mode_t mode); 创建成功返回 0失败返回 -1并设置错误码参数 pathname 为文件名 mode 是其对应的权限 。命名管道是一种进程间通信机制它和匿名管道的区别是它可以让没有血缘关系的进程进行通信。同时命名管道有对应的 inode 可以理解为磁盘上的伪文件。但它不是一个实际的数据存储文件无论写入多少数据其大小都是 0。 命名管道通过路径文件名作为该文件的唯一标识用法和普通文件一样使用 open、write、read 接口但仍得保持其单向通信的特性一端读取一段写入。且需要等待写端打开之后读端才会打开文件否则读端会阻塞等待。
三、共享内存通信
3.1 共享内存原理 共享内存Shared Memory是进程间通信的一种方式它允许两个或多个进程访问同一块物理内存区域。每个进程都有自己的虚拟地址空间共享区存在于地址空间上的栈区和堆区之间。在用户需要申请共享内存时操作系统在物理内存中申请一块空间每个进程在自己的共享区中与这块物理内存单独建立页表映射这种多个进程共同映射同一块物理内存的操作就叫做共享内存即让不同的进程看到同一份资源 这些进程可以通过共享区页表映射直接读写这块物理内存如同访问本进程的私有内存一样进行数据交互。因此它是内存级别的通信没有额外的复制开销使得通信速度非常快。但由于有多个进程访问同一块空间也得考虑同步和互斥控制我们可以通过使用管道、条件变量、消息队列、信号量加锁等方法来避免竞态条件和数据不一致的问题。 3.2 创建和关联共享内存 从不同角度上来理解实现共享内存的步骤 操作系统角度 ①创建共享内存 ②删除共享内存 进程角度 ③关联共享内存 ④去关联共享内存 因此我们需要按①③④②顺序区管理一个共享内存 创建共享内存 int shmget(key_t key, size_t size, int shmflg); 创建成功返回 shmid失败返回 -1 错误码被设置。size 为要申请的共享内存大小由于操作系统对内存管理的最小单位是页(4KB)所以 size 建议设置成为页的整数倍。 shmflg为创建共享内存的选项常见的有两个选项 IPC_CREAT创建共享内存不存在就创建存在就获取IPC_EXCL不单独使用必须和IPC_CREAT配合使用。如果不存在指定的共享内存就创建。存在则出错返回。这样可以保证如果shmget函数调用成功一定是一个全新的共享内存。并且 shmflg 可以按位或文件权限设置共享内存权限类似shmget(keySIZEflags | 0666); 现在关键的问题来了你怎么保证不同的进程看到的是同一个共享内存呢 操作系统中可能有很多个共享内存在被使用所以我们就需要用一个唯一值来标识每一个共享内存即 key 值。在内核中让不同的进程看到同一份共享内存让他们拥有同一个 key 即可。
那么这个 key 由谁来提供呢 如果由操作系统来提供那么创建共享内存的进程可以知道 key 值但是其它要使用这个共享内存的进程如何获取这个 key 呢不可能让创建共享内存的进程通信给他们吧。因为此时你要解决的就是双方通信的问题这就变成了鸡生蛋、蛋生鸡的问题。所以 key 值必须由用户之间指定、用户之间的约定的这样才能确保看到的是同一个共享内存。
获取 key 值key_t ftok(const char *pathname, int proj_id); 将文件路径和一个项目标识符通过一套算法转化为唯一 key 值这里的路径名和项目标识符就是用户之间约定好的成功返回一个唯一的 key 值失败返回 -1错误码被设置。 创建共享内存成功返回共享内存标识符 shmid 它是一个由系统分配的唯一的整数值用于唯一标识一个共享内存段。key 在操作系统内标定唯一性而shmid 只在你的进程内用来表示资源的唯一性接下来的对共享内存的操作关联、去关联、删除我们都采用 shmid 作为参数而非 key
关联共享内存void *shmat(int shmid, const void *shmaddr, int shmflg); 参数 shmid共享内存标识符由 shmget 函数返回shmaddr指定附加共享内存的位置。如果设置为 NULL则由系统选择地址shmflg控制共享内存的访问方式和其他选项的标志一般设置为0使用默认行为 成功返回附加到进程地址空间的共享区的地址。失败返回 (void *) -1错误码被设置。使用类似于 molloc使用时需要强转为需要的指针类型。对此我们就像访问自己的私有内存一样与其它进程进行数据交互通信
3.3 去关联、ipc 指令和删除共享内存
去关联共享内存int shmdt(const void *shmaddr); 成功返回0失败返回-1错误码被设置。参数为共享区中 shmat 函数返回的共享区起始地址 ipc 系列相关指令 一、查看IPC ipcs 和 ipcs -a 查看所有IPC对象ipcs -q 查看消息对列对象ipcs -m: 查看共享内存对象ipcs -s 查看信号量对象 二 、删除 IPC 对象 ipcrm -Q key 根据键值key删除指定的消息对列ipcrm -q id根据ID删除指定的消息对列ipcrm -M key根据键值key删除指定的共享内存ipcrm -m id根据ID删除指定的共享内存ipcrm -S key 根据键值key删除指定的信号量ipcrm -s id根据ID删除指定的信号量 删除共享内存int shmctl(int shmid, int cmd, struct shmid_ds *buf); 参数 shmid由 shmget 函数返回的共享内存标识码cmd将要采取的动作IPC_STAT提取 shmid_ds 结构中的数据IPC_SET设置 shmid_ds 结构中的数据IPC_RMID删除共享内存buf指向一个保存着共享内存的模式状态和访问权限的数据结构 毋庸置疑共享内存可能存在多个必然是要先描述后组织起来的shmid_ds 就是描述共享内存的结构体。它包括共享内存的大小、创建者和最后操作者的进程ID、当前有多少进程附加到这个共享内存段等属性。顺着往下找其结构体内部的结构体 ipc_perm 中就保存了由用户提供的 key 值。
四、消息队列和信号量了解
4.1 消息队列 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法读端和写端公用一个队列。当发送方有数据发送时将数据先打包成一个节点然后尾插到内核中的消息队列中去。当接收方接收数据时从队列头部开始去找所需要的节点然后进行解包得到数据。同时每个数据块都会有个记录类型的数据来判断该数据块该被哪个进程读取。 消息队列接口使用几乎和共享内存一样这里就不详细介绍了 获取int msgget(key_t key, int msgflg); 控制int msgctl(int msqid, int cmd, struct msqid_ds *buf); 发送int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 接受ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 4.2 信号量
信号量 信号量本质上就是个计数器它统计的是公共资源资源的剩余量。而公共资源可以被多个进程同时访问如果访没有同步与互斥就会导致数据不一致问题一个进程还在写的时候另一个进程就开始读。而这种通过同步与互斥被保护起来的资源称为临界资源访问临界资源的那部分代码称为临界区其他的代码就称为非临界区。 互斥当有多个执行流想要访问同一份资源的时候我们只允许一个执行流进行访问当这个执行流访问完了下一个执行流能访问 进程在访问公共资源前要先申请信号量需要让多个进程看到同一个计数器避免资源不足而访问失败。看到同一个计数器说明信号量也是个公共资源也需要保护因此对信号量的 pv 操作必须是原子的。 P 操作申请信号量计数器减 1 V 操作释放信号量计数器加 1 原子性要么不做要么做完只有这两种状态的情况
4.3 system V 进程间通信除了通过管道都是基于文件的通信方式还有一种方式是System V 标准的进程间通信方式。System V 标准是一个在OS层面专门为进程通信设计的一个方案它是被精心设计过的。system V IPC 提供的通信方式有三种 共享内存、消息队列和信号量你可以发现它们的系统调用接口都非常相似同时它们都有描述自己字段的结构体 xxx_ds。而在它们的 xxx_ds 结构体中开头都有一个共同的字段 ipc_perm这个 ipc_perm 类似于基类被其它子类继承下去 struct ipc_perm {key_t __key; /* Key supplied to shmget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions SHM_DEST and SHM_LOCKED flags */unsigned short __seq; /* Sequence number */};因为它们的第一个字段 ipc_perm 是一样的所以可以维护一个 struct ipc_perm* 的指针数组存共享内存、消息队列、信号量它们三者中第一个元素的地址也就是 ipc_perm。这样就可以把共享内存、消息队列和信号量三个部分直接管理起来了。而我们知道结构体的第一个成员的地址和结构体对象的地址在数值上是相同的并且操作系统在内部可以识别这个对象是共享内存、消息队列和信号量中的哪一个因此我们拿到这个数组中的 ipc_perm 地址便能访问这结构体的其它成员将它们管理起来