我经常听到与访问线程之间的进程内存相比,访问进程之间的共享内存段不会降低性能。换句话说,多线程应用程序不会比使用共享内存的一组进程更快(不包括锁定或其他同步问题)。
但我有疑问:
1)shmat()将本地进程虚拟内存映射到共享段。必须为每个共享内存地址执行此转换,并且转换可能会花费大量成本。在多线程应用程序中,不需要额外的转换:所有VM地址都转换为物理地址,就像在不访问共享内存的常规过程中一样。
2)共享内存段必须由内核 以某种方式 维护。例如,关闭与shm关联的所有进程时,shm段仍处于启动状态,并且最终可以由新启动的进程重新访问。在shm段上可能有一些与内核操作有关的开销。
多进程共享内存系统是否与多线程应用程序一样快?
1)shmat()将本地进程虚拟内存映射到共享段。必须对每个共享内存地址执行此转换,并且相对于shm访问的数量而言,这种转换可能会花费大量成本。在多线程应用程序中,不需要额外的转换:所有VM地址都转换为物理地址,就像不访问共享内存的常规过程一样。
除了建立共享页的初始成本外,与常规内存访问相比,没有任何开销shmat()-在调用过程中填充页表- 在大多数类型的Linux中,每4KB共享内存为1页(4或8字节) 。
shmat()
无论是共享页面分配还是在同一流程中分配,(所有相关比较)成本都相同。
2)共享内存段必须由内核以某种方式维护。我不知道“某种程度上”在性能方面意味着什么,但是例如,当拆除了与shm相连的所有进程时,shm段仍处于启动状态,并且最终可以由新启动的进程重新访问。在shm段的生存期内,内核必须至少检查某种程度的开销。
无论是否共享,内存的每个页面都具有一个“结构页面”,并带有有关该页面的一些数据。其中一项是参考计数。当将页面分配给进程时(无论是通过“ shmat”还是其他机制),引用计数都会增加。通过某种方式释放它时,引用计数将减少。如果减少的计数为零,则实际上释放了页面-否则“此后再也没有发生”。
与分配的任何其他内存相比,开销基本上为零。无论如何,相同的机制也用于页面的其他目的-例如,您有一个页面也被内核使用- 并且您的进程终止了,内核需要知道在释放该页面之前,不释放该页面,因为以及用户流程。
创建“叉子”时会发生同样的事情。派生一个进程时,父进程的整个页表实际上都将复制到子进程中,并且所有页都变为只读状态。每当发生写操作时,内核都会发生错误,从而导致该页面被复制- 因此该页面现在有两个副本,执行写操作的进程可以修改该页面,而不会影响其他进程。一旦子进程(或父进程)死亡,则当然这两个进程仍然拥有所有页面(例如,从未写入的代码空间,以及可能从未接触过的一堆公共数据,等等)显然无法释放,直到两个进程都“死”为止。同样,引用计数的页面在这里很有用,因为我们只对每个页面上的引用计数进行计数,
共享库确实发生了同样的事情。如果一个进程使用共享库,则该进程结束时将释放它。但是,如果两个,三个或100个进程使用同一个共享库,则代码显然必须保留在内存中,直到不再需要该页面为止。
因此,基本上,整个内核中的所有页面都已被引用计数。开销很少。