重要通知

问题完全解决了not个,而且是一个打字错误导致的not个.仅仅因为有人说他们不能重现它(就他们本地的测试设置而言,他们可能是对的),这not就意味着对我来说不存在问题.我可以完美地重现这个问题,但我仍在寻找解决方案或解释.

如果需要更多的信息,请告诉我需要what.

游行示威

Here是一个演示这个问题的视频短片.


原问题

我正在使用POSIX共享内存在我的应用程序的多个实例之间共享一些公共状态.POSIX共享内存是通过shm_open()函数创建的,它通过ftruncate()扩展到所需的大小,然后通过mmap()函数映射到内存中.我还通过sem_open()函数创建了一个信号量,用于同步对共享内存的访问并确保first实例执行初始化.

到目前为止,这似乎运行得很好.我还看到我的共享内存对象显示在/dev/shm/以下,拥有适当的所有者和权限,这一切都与预期不谋而合.但是,在Linux(Ubuntu 20.04LTS)上,我看到了大约103种行为:重启后,共享内存对象(以及信号量)仍然存在.这怎么可能呢?毕竟,就我所知,共享内存对象是volatile,即/dev/shm/文件系统是完全驻留在RAM中的104,因此not可以在重启后存活.更糟糕的是,在重新启动后,我的共享内存对象的owner已经从原来的用户更改为"根",这显然是出于no个原因!这意味着如果我再次启动我的应用程序after a reboot,则shm_open() fails的权限被拒绝!因此,必须有"某种东西"在系统停机时将共享内存保存到磁盘上,并在系统重新启动时恢复共享内存.但为什么神秘的所有者发生了变化,让这部《持久化》共享内存inaccessible成为现实呢?

注:我已经在MacOS X和FreeBSD上判断了这一点,但这两种操作系统似乎都没有这种行为.

我在Linux上测试了multiple times,在重新 bootstrap 之前,ls -al /dev/shm显示了我的共享内存(和信号量)的原始所有者,并且在重新 bootstrap 之后,它将"根"显示为所有者.应用程序确实在两次ls -al /dev/shm次调用之间运行了not次.让一切恢复正常的唯一方法是在每次重启后转到sudo rm -f /dev/shm/*.但这不可能是"解决方案",对吧?在Linux上处理这个问题的"标准"方法是什么?


我的代码如下所示:

static unsigned char *initialize(const int create_shmem)
{
    void *buffer = NULL;

    int fd = shm_open(OBJECT_NAME, create_shmem ? O_CREAT | O_EXCL | O_RDWR : O_RDONLY, 0644);
    if (fd < 0) {
        if ((!create_shmem) && (errno == ENOENT)) {
            return initialize(1);
        } else {
            return NULL;
        }
    }

    if (create_shmem) {
        if (ftruncate(fd, BUFFER_SIZE) != 0) {
            goto failure;
        }
    }

    buffer = mmap(NULL, BUFFER_SIZE, create_shmem ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0U);
    if (buffer == MAP_FAILED) {
        goto failure;
    }

    /* ... */
}

我知道我可以在重启之前通过shm_unlink()函数explicitly删除共享内存,这可能会避免这个问题.但这真的是not件可靠的事!如果一旦应用程序由于任何原因(例如崩溃)而错过了适当的清理,然后系统被重新 bootstrap ,那么我们最终得到的是一个可以再访问no的共享内存对象.


有趣的是,我发现systemd login manager有一个配置选项100-"Controls whether System V and POSIX IPC objects belonging to the user shall be removed when the user fully logs out".然而,这确实not似乎影响了属于"根"用户的共享存储器对象("Note that IPC objects of the root user and other system users are excluded from the effect of this setting").此外,这也解释了为什么在重启时,现有共享存储器对象的所有权从先前的所有者被转移到"根"用户,而不是仅仅删除共享存储器.删除是可以的,因为这样我们就能够以普通用户的身份重新创建它.似乎有no个类似的选项可以将"根"拥有的对象也删除.

Source


Linux是否提供了一种"启动计数器",在每次系统开机或重新启动时都会递增?如果是这样的话,我只需在我的对象名后面加上that个计数器,就可以解决"旧"对象(名称)被"根"用户阻止的问题.


我现在已经创建了一个系统服务单元,它在系统关闭时运行,并调用一个脚本,该脚本将103目录中的所有内容.从我的日志(log)中,我可以看到(稍后)我的"删除"脚本实际上是在系统关闭时运行的.我可以看到它实际上从/dev/shm目录中删除了现有的共享内存.尽管如此,当我在重新启动后判断/dev/shm时,共享内存又回来了,现在归"根"用户所有!!

我只能假设这种情况的发生是因为当系统重新启动时,"无论"是将/dev/shm的内容保存到磁盘还是before我的删除脚本都有机会运行……

推荐答案

正如comment所示,在Linux上,我们可以使用/proc/sys/kernel/random/boot_id来获取在每次重新启动时都会更改的唯一ID,但只要系统运行,该ID就会保持不变.这是为我的POSIX共享内存生成唯一名称的一种很好的方法,这样不同的进程可以就same的名称达成一致,但该名称在重新启动后仍然会更改.因此,我们可以避免与不再可访问的"旧的"共享记忆的名称冲突.

C++相关问答推荐

在Windows上构建无聊的SSL x64

在struct中调用函数,但struct在void中 *

在函数中使用复合文字来初始化C语言中的变量

我编译了一个新的c程序,并收到以下错误

GCC引发不明确的诊断消息

特定闪存扇区的内存别名

SDL 2.0-从数组渲染纹理

为什么我从CSV文件中进行排序和搜索的代码没有显示数据的所有结果?

如何只获取字符串的第一个单词,然后将其与c中的另一个单词进行比较?

MacOS下C++的无阻塞键盘阅读

为什么我在C代码中得到一个不完整的类型?

使用Open62541向OPCUA服务器发送读请求时内存泄漏

为什么我的旧式&q;函数在传递浮点数时会打印2?

哪个首选包含第三个库S头文件?#INCLUDE;文件名或#INCLUDE<;文件名&>?

按字典顺序打印具有给定字符的所有可能字符串

将不同类型的指针传递给函数(C)

我的代码可以与一个编译器一起使用,但不能与其他编译器一起使用

C 语言中 CORDIC 对数的问题

C11 嵌套泛型

当循环变量在溢出时未定义时,可以进行哪些优化?