Linux内核如何实现不同进程之间的共享内存机制?
进一步阐述,每个进程都有自己的地址空间。例如,与进程 B 中的地址 0x1000 相比,进程 A 中的地址 0x1000 是不同的位置。
那么内核如何保证一块内存在不同的进程之间共享,拥有不同的地址空间呢?
提前致谢。
Linux内核如何实现不同进程之间的共享内存机制?
进一步阐述,每个进程都有自己的地址空间。例如,与进程 B 中的地址 0x1000 相比,进程 A 中的地址 0x1000 是不同的位置。
那么内核如何保证一块内存在不同的进程之间共享,拥有不同的地址空间呢?
提前致谢。
包含示例代码和详细说明:
还有一堆关于 Linux 上 IPC 的几乎所有内容的参考资料。
进程间通信机制
进程相互通信并与内核通信以协调它们的活动。Linux 支持许多进程间通信 (IPC) 机制。信号和管道是其中的两种,但 Linux 也支持以它们首次出现的 Unix TM 版本命名的 System V IPC 机制。
信号
信号是 Unix TM 系统使用的最古老的进程间通信方法之一。它们用于向一个或多个进程发送异步事件信号。信号可能由键盘中断或错误条件产生,例如进程试图访问其虚拟内存中不存在的位置。Shell 还使用信号向其子进程发送作业控制命令。内核可以生成一组定义的信号,或者可以由系统中的其他进程生成,前提是它们具有正确的权限。您可以使用 kill 命令 (kill -l) 列出系统的一组信号。
管道
常见的 Linux shell 都允许重定向。例如
$ ls | 公关 | lpr
将列出目录文件的 ls 命令的输出通过管道传输到对它们进行分页的 pr 命令的标准输入中。最后,pr 命令的标准输出通过管道传输到 lpr 命令的标准输入,该命令在默认打印机上打印结果。管道是单向字节流,它将一个进程的标准输出连接到另一个进程的标准输入。两个进程都不会意识到这种重定向,并且会像往常一样运行。正是外壳在进程之间建立了这些临时管道。
在 Linux 中,管道是使用两个文件数据结构实现的,这两个文件数据结构都指向同一个临时 VFS inode,而 VFS inode 本身指向内存中的物理页面。图显示每个文件数据结构包含指向不同文件操作例程向量的指针;一个用于写入管道,另一个用于从管道读取。
插座
假设您有许多协作进程从单个数据文件读取记录并将记录写入单个数据文件。您会希望对文件访问进行严格协调。您可以使用初始值为 1 的信号量,并在文件操作代码周围放置两个信号量操作,第一个用于测试和减少信号量的值,第二个用于测试和增加它。访问该文件的第一个进程将尝试减少信号量的值并且它会成功,信号量的值现在为 0。这个过程现在可以继续并使用数据文件,但如果另一个希望使用它的进程现在尝试减少信号量的值它会失败,因为结果是-1。该进程将暂停,直到第一个进程处理完数据文件。当第一个进程处理完数据文件后,它将增加信号量的值,使其再次变为 1。现在可以唤醒等待进程,这一次它增加信号量的尝试将成功。
引自tldp.org。
Linux 中有两种共享内存。
如果 A 和 B 分别是 Parent 进程和 Child 进程,它们各自使用自己的 pte 来访问共享内存。共享内存由 fork 机制共享。所以一切都很好,对吧?(更多细节请看内核函数copy_one_pte()和相关函数。)
如果 A 和 B 不是父子,它们使用公钥来访问共享内存。
假设 A 通过 System V shmget() 使用密钥创建共享内存,相应地,内核在 shmem/tmpfs 中为进程 A 创建一个文件(文件名为“SYSTEMV+key”),这是一个内部 RAM-基于文件系统。它由 kenrel 安装(检查 shmem_init())。共享内存区域由 shmem/tmpfs 处理。基本上,当进程 A 访问共享内存区域时,它由页面错误机制处理。如果进程 B 想要访问由进程 A 创建的共享内存区域。进程 B 应该使用 shmget() 和进程 A 使用的相同密钥。然后进程 B 可以找到文件(“SYSTEMV+key”)并将文件映射到进程 B 的地址空间。