You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下共享内存清理的最优实现方案探讨

Linux共享内存自动清理方案:进程异常退出时的资源回收

我正在开发一个Rust库,用来简化Linux下的共享内存操作——所有进程只能通过这个库请求共享内存,但无法避免进程panic或被强制终止的情况。这个库用于「运行周期(run)」场景:周期内至少有一个进程在使用共享内存,周期结束后必须清理内存;但既无法保证进程全程运行,也无法保证最后退出的进程是正常退出的。现在需要解决的核心问题是:如何让共享内存在进程运行时保留,在最后一个进程退出/终止后立即自动清理?

我了解Linux下创建共享内存有两种方式:

  • System V风格的shmat
  • POSIX风格的shm_open(个人更倾向这种,因为它创建的共享内存可通过文件系统访问,但这并非硬性要求)

我原本以为如果新进程能附加到已标记删除的System V共享内存,问题就解决了,比如这样:

unsafe {
 let shmid = shmget(KEY, SHMEM_SIZE, IPC_CREAT | IPC_EXCL | 0o770);
 let shmaddr = shmat(shmid, null_mut(), 0);
 shmctl(shmid, IPC_RMID, null_mut()); // 在这里标记为待删除
 let ptr = shmaddr as *mut [u8; SHMEM_SIZE as usize];
 *ptr = [12; SHMEM_SIZE as usize];
}

但在另一个进程中尝试附加时会失败——因为KEY已经被标记删除,shmget无法获取到对应的共享内存:

unsafe {
 let shmid = shmget(KEY, SHMEM_SIZE, IPC_EXCL); // 这里无法成功,因为KEY已标记删除
 let shmaddr = shmat(shmid, null_mut(), 0);
 let ptr = shmaddr as *mut [u8; SHMEM_SIZE as usize];
 *ptr = [13; SHMEM_SIZE as usize];
}

目前我有两个不太理想的解决方案:

  • 选项1:守护进程监控方案
    启动一个后台守护进程,新进程需要向它发送PID完成注册。守护进程通过pidfd_open系统调用获取进程的文件描述符,使用poll监控进程状态,当监控的PID列表为空时删除共享内存。
    问题:方案较为脆弱,而且不想为了监控共享内存单独启动额外进程。
    附半成品实现代码:

    pub fn handle_unix_socket(
          &self,
          pollfd: &mut PollFd,
      ) -> Result<(Option<OwnedFd>, Option<u32>), std::io::Error> {
          let mut buffer = [0; 4];
          if pollfd
              .revents()
              .unwrap_or(PollFlags::empty())
              .contains(PollFlags::POLLIN)
          {
              if let Ok((mut stream, _)) = self.listener.accept() {
                  let _ = stream.read(&mut buffer)?;
                  let pid = u32::from_be_bytes(buffer);
                  let fd = unsafe { syscall2(syscalls::Sysno::pidfd_open, pid as usize, 0).unwrap() };
                  let fd = unsafe { OwnedFd::from_raw_fd(fd as i32) };
                  stream.write_all(&buffer)?;
                  Ok((Some(new_fd), Some(new_pid)))
              } else {
                  todo!()
              }
          } else {
              Ok((None, None))
          }
      }
    
      pub fn event_loop(&mut self) -> Result<(), std::io::Error> {
          loop {
              let mut pollfds = vec![PollFd::new(self.listener.as_fd(), PollFlags::POLLIN)];
              for fd in self.fds.iter() {
                  pollfds.push(PollFd::new(fd.as_fd(), PollFlags::POLLIN));
              }
              poll(&mut pollfds, PollTimeout::NONE)?;
              let (new_fd, new_pid) = self.handle_unix_socket(&mut pollfds[0])?;
              let remove_indicies = (1..pollfds.len())
                  .rev()
                  .filter(|i| {
                      if let Some(pflag) = pollfds[*i].revents() {
                          if pflag.contains(PollFlags::POLLIN) {
                              return true;
                          }
                      }
                      false
                  })
                  .collect::<Vec<_>>();
              for i in remove_indicies {
                  self.pids.remove(i - 1);
                  self.fds.remove(i - 1);
              }
              match (new_fd, new_pid) {
                  (Some(new_fd), Some(new_pid)) => {
                      self.fds.push(new_fd);
                      self.pids.push(new_pid);
                  }
                  _ => {}
              }
              if self.pids.is_empty() {
                  return Ok(());
              }
          }
      }
    
  • 选项2:eBPF跟踪方案
    用eBPF跟踪所有打开共享内存文件的进程,由eBPF程序维护一个PID列表,当列表为空时自动删除共享内存。
    问题:对eBPF经验不足,不确定方案是否可行;而且eBPF通常需要特权环境,希望避免这种依赖。

内容的提问来源于stack exchange,提问作者sesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:56:02