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

PHP shmop跨用户无法删除共享内存段的权限问题咨询

PHP shmop跨用户无法删除共享内存段的权限问题咨询

我目前有个轻量数据共享的场景,为了避免用Memcached/Redis或者数据库(数据量从160字节到10KB不等,那些方案太重了),选择了PHP的shmop系列函数实现共享内存,但跨用户操作时遇到了权限问题,想请教大家。

场景背景

我有三个不同系统用户的进程需要共享数据:

  • 服务进程用用户A执行bash脚本,调用PHP CLI处理
  • 网站进程用用户B运行,需要读取CLI进程的事件数据
  • 还有我手动用用户C运行的脚本

我实现了基础的增删查逻辑:每次修改数据时先删除旧段再创建新段,还给部分条目加了TTL。目前跨用户读写共享内存都正常,但跨用户删除共享内存段的时候遇到了权限卡点——哪怕创建段时用了0777权限,其他用户还是没法调用shmop_delete删除别人创建的段。

报错信息与测试代码

我最初的删除逻辑是这样的:

@$id = shmop_open($this->m_dataCacheId, "a", 0, 0);
if(false !== $id){
    shmop_delete($id);
    @shmop_close($id);
}

运行后直接抛出警告:

Warning: shmop_delete(): Can't mark segment for deletion (are you the owner?)

之后再尝试打开这个段:

$id = shmop_open($this->m_dataCacheId, "a", 0, 0);

又会报:

Warning: shmop_open(): Unable to attach or create shared memory segment "Invalid argument"

创建共享内存段的代码我是这么写的(段ID用ftok生成,跨用户调用时ID是一致的):

$this->m_dataCacheId = ftok(__FILE__, "0");
$id = shmop_open($this->m_dataCacheId, "c", 0777, strlen(serialize($data)));

为了复现问题,我写了完整的测试脚本:

<?php
$m_dataCacheId = ftok(__FILE__, "0");

// 写入测试
$demoData = "Hi";
set_cache($demoData);

// 读取测试
$demoData = get_cache();
if(false !== $demoData){
    echo("Mem data: " . $demoData . PHP_EOL);
}else{
    echo("No data stored." . PHP_EOL);
}

function set_cache($data) {
    global $m_dataCacheId;
    echo("set_cache id: " . $m_dataCacheId . PHP_EOL);
    
    // 先尝试删除旧缓存
    @$id = shmop_open($m_dataCacheId, "a", 0777, 0);
    if(false !== $id){
        shmop_delete($id);
        @shmop_close($id);
    }
    
    $id = shmop_open($m_dataCacheId, "c", 0777, strlen(serialize($data)));
    if ($id) {
        $shmopStatus = shmop_write($id, serialize($data), 0);
        @shmop_close($id);
        return $shmopStatus;
    }
    return false;
}

function get_cache() {
    global $m_dataCacheId;
    echo("get_cache id: " . $m_dataCacheId . PHP_EOL);
    
    @$id = shmop_open($m_dataCacheId, "a", 0777, 0);
    if($id){
        $data = unserialize(shmop_read($id, 0, shmop_size($id)));
        @shmop_close($id);
        if($data){
            return $data;
        }
    }
    return false;
}

测试步骤:

  1. 用普通用户运行脚本,创建共享内存段
  2. 用sudo(root用户)再次运行脚本,此时root尝试删除普通用户创建的段,触发权限报错
  3. 切回普通用户运行,会遇到shmop_open的「Invalid argument」错误

排查结果与疑问

后来我用C语言写了简单的共享内存测试程序,发现了同样的问题:root创建的段普通用户删不了,普通用户创建的段root能删。查了Linux的shmctl手册后发现,这其实是Linux系统的原生行为:

IPC_RMID Mark the segment to be destroyed. The caller must be the owner or creator of the segment, or be privileged.

也就是说,shmop_delete底层调用的是shmctl(IPC_RMID),Linux内核要求必须是段的创建者/所有者,或者特权用户才能标记段为删除状态——哪怕给段设了0777权限也没用,因为这个权限控制的是读写,不是删除操作。

目前我临时的解决办法是分两个共享内存段:一个给普通用户用,一个给特权用户用,但这毕竟是权宜之计。现在想请教两个问题:

  1. 这种跨用户无法删除shmop段的情况,确实是正常的系统行为,而不是PHP shmop的bug吗?
  2. 我当时把问题标了php标签,但现在发现本质是Linux IPC的权限问题,要不要修改问题的标签?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:52:57