【问题标题】:how does linux kernel implement shared memory between 2 processeslinux内核如何实现2个进程之间的共享内存
【发布时间】:2013-07-04 10:17:18
【问题描述】:

Linux内核如何实现不同进程之间的共享内存机制?

进一步阐述,每个进程都有自己的地址空间。例如,与进程 B 中的地址 0x1000 相比,进程 A 中的地址 0x1000 是不同的位置。

那么内核如何保证一块内存在不同的进程之间共享,拥有不同的地址空间呢?

提前致谢。

【问题讨论】:

    标签: linux-kernel operating-system shared-memory


    【解决方案1】:

    进程间通信机制

    进程相互通信并与内核通信以协调它们的活动。 Linux 支持许多进程间通信 (IPC) 机制。信号和管道是其中的两种,但 Linux 也支持以它们首次出现的 Unix TM 版本命名的 System V IPC 机制。

    信号

    信号是 Unix TM 系统使用的最古老的进程间通信方法之一。它们用于向一个或多个进程发送异步事件信号。信号可能由键盘中断或错误条件产生,例如进程试图访问其虚拟内存中不存在的位置。 Shell 还使用信号向其子进程发送作业控制命令。 内核可以生成一组定义的信号,或者可以由系统中的其他进程生成,前提是它们具有正确的权限。您可以使用 kill 命令 (kill -l) 列出系统的一组信号。

    管道

    常见的 Linux shell 都允许重定向。例如

    $ ls |公关 | lpr

    将列出目录文件的 ls 命令的输出通过管道传输到 pr 命令的标准输入中,该命令对它们进行分页。最后,pr 命令的标准输出通过管道传输到 lpr 命令的标准输入,该命令在默认打印机上打印结果。管道是单向字节流,它将一个进程的标准输出连接到另一个进程的标准输入。两个进程都不会意识到这种重定向,并且会像往常一样运行。正是 shell 在进程之间建立了这些临时管道。

    在 Linux 中,管道是使用两个文件数据结构实现的,这两个文件数据结构都指向同一个临时 VFS inode,而 VFS inode 本身指向内存中的物理页面。图显示每个文件数据结构包含指向不同文件操作例程向量的指针;一个用于写入管道,另一个用于从管道读取。

    套接字

    • 消息队列:消息队列允许一个或多个进程写入消息,这些消息将被一个或多个读取进程读取。 Linux 维护了一个消息队列列表,即 msgque 向量;其中的每个元素都指向一个完整描述消息队列的 msqid_ds 数据结构。创建消息队列时,会从系统内存分配一个新的 msqid_ds 数据结构并插入到向量中。
    • System V IPC 机制:Linux 支持三种类型的进程间通信机制,最早出现在 Unix TM System V (1983) 中。这些是消息队列、信号量和共享内存。这些 System V IPC 机制都共享通用的身份验证方法。进程只能通过系统调用将唯一的引用标识符传递给内核来访问这些资源。使用访问权限检查对这些 System V IPC 对象的访问,就像检查对文件的访问一样。 System V IPC 对象的访问权限由对象的创建者通过系统调用设置。每个机制都使用对象的引用标识符作为资源表的索引。它不是一个简单的索引,但需要一些操作来生成索引。
    • 信号量:信号量最简单的形式是内存中的一个位置,其值可以由多个进程测试和设置。就每个进程而言,测试和设置操作是不可中断的或原子的;一旦开始,没有什么可以阻止它。测试和设置操作的结果是信号量的当前值和设置值相加,可以是正数也可以是负数。根据测试和设置操作的结果,一个进程可能必须休眠,直到信号量的值被另一个进程更改。信号量可用于实现关键区域,即一次只能执行一个进程的关键代码区域。

    假设您有许多协作进程从单个数据文件读取记录并将记录写入单个数据文件。您会希望对文件访问进行严格协调。您可以使用初始值为 1 的信号量,并在文件操作代码周围放置两个信号量操作,第一个用于测试和减少信号量的值,第二个用于测试和增加它。访问该文件的第一个进程将尝试减少信号量的值并且它会成功,信号量的值现在为 0。这个过程现在可以继续并使用数据文件,但如果另一个希望使用它的进程现在尝试减少信号量的值它会失败,因为结果是-1。该进程将暂停,直到第一个进程处理完数据文件。当第一个进程处理完数据文件后,它将增加信号量的值,使其再次变为 1。现在可以唤醒等待进程,这一次它增加信号量的尝试将成功。

    • 共享内存:共享内存允许一个或多个进程通过出现在其所有虚拟地址空间中的内存进行通信。虚拟内存的页面由每个共享进程的页表中的页表条目引用。它不必在所有进程的虚拟内存中位于相同的地址。与所有 System V IPC 对象一样,对共享内存区域的访问是通过密钥和访问权限检查来控制的。一旦内存被共享,就不会检查进程如何使用它。它们必须依赖其他机制(例如 System V 信号量)来同步对内存的访问。

    引用自tldp.org

    【讨论】:

    • -1 问题是关于共享内存的实现。您提供了可用 IPC 机制的概述,其中大部分不相关,但并未真正回答问题。此外,“受启发”不是直接从其他来源复制的内容的正确英文单词。您应该清楚地表明您的帖子是引用和cite the exact source url
    【解决方案2】:

    看看Beej's definitive guide to IPC on Linux

    包含示例代码详细解释

    1. fork()
    2. 信号
    3. 管道
    4. 先进先出
    5. 文件锁定
    6. 消息队列
    7. 信号量
    8. 共享内存段
    9. 内存映射文件
    10. Unix 套接字

    还有一堆关于 Linux 上 IPC 的几乎所有内容的参考资料。

    【讨论】:

      【解决方案3】:

      Linux 中有两种共享内存。

      1. 如果A和B分别是Parent process和Child process,它们各自使用自己的pte来访问共享内存。共享内存通过fork机制共享。所以一切都很好,对吧?(更多细节请看内核函数copy_one_pte()和相关函数。)

      2. 如果 A 和 B 不是父子关系,则它们使用 a 公钥来访问共享内存。

      假设 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 的地址空间。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-08
        • 2011-01-01
        • 2021-03-26
        • 2014-07-18
        • 1970-01-01
        • 2012-07-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多