在 shmdt() 之后和 shmctl(shmid, ipc_RMID, 0) 之前访问进程中的共享内存

Accessing shared memory in a process after shmdt() and before shmctl(shmid, ipc_RMID, 0)

假设我有一个指向先前分配的共享内存的指针*p

如果其中一个进程调用 shmdt() 来分离共享内存段,然后尝试分配一个值,例如:

*p = 0;

在调用 shmctl(shmid, IPC_RMID, 0) 进行销毁之前。

这样做会不会出错?我无法理解是什么以及为什么。

是的,这是一个错误,很可能会导致段错误。

当您调用 shmget(2) 分配共享内存段时,它不会立即放在进程的虚拟地址 space 中的任何位置。也就是说,没有您可以写入的地址会将数据写入该段。

shmat(2) 的工作是将段放入(映射)到您进程的地址 space 中。 (在 System V 共享内存的说法中,这称为 attaching 段,但该术语在其他地方使用不多。Mapping 更常见。)成功调用 shmat() 后,该段将出现在某个地址,该地址作为 shmat().

的结果返回

在先前附加的段上调用 ​​shmdt(2) 会使该段再次从您进程的虚拟地址 space 中消失。尝试写入以前属于映射的地址是错误的,因为映射不再存在。这并不意味着写入段的数据丢失了——它只是没有映射到任何地方。您可以通过再次调用 shmat(2) 来重新映射(重新附加)该段以再次访问数据。

只有在使用 shmctl()IPC_RMID 销毁段后,内存才会真正释放(一旦段不再附加到任何地方)。

为了使事情更具体一些,以下是共享内存的简单实现在较高层次上的工作方式:

  • shmget() 为段分配您请求的物理内存。

  • shmat()MMU 进行编程并在内核中进行设置,以便进程中的某些地址范围映射到该段。

  • shmdt() 执行反向操作并删除映射。

  • shmctl()IPC_RMID 释放段的物理内存(将其标记为空闲)。

附带说明一下,可以使用 shmat(2) 将同一段映射到地址 space 中的多个位置。这是可能的,因为它纯粹是一个虚拟内存操作。