重要通知
问题完全解决了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个类似的选项可以将"根"拥有的对象也删除.
Linux是否提供了一种"启动计数器",在每次系统开机或重新启动时都会递增?如果是这样的话,我只需在我的对象名后面加上that个计数器,就可以解决"旧"对象(名称)被"根"用户阻止的问题.
我现在已经创建了一个系统服务单元,它在系统关闭时运行,并调用一个脚本,该脚本将103目录中的所有内容.从我的日志(log)中,我可以看到(稍后)我的"删除"脚本实际上是在系统关闭时运行的.我可以看到它实际上从/dev/shm
目录中删除了现有的共享内存.尽管如此,当我在重新启动后判断/dev/shm
时,共享内存又回来了,现在归"根"用户所有!!
我只能假设这种情况的发生是因为当系统重新启动时,"无论"是将/dev/shm
的内容保存到磁盘还是before我的删除脚本都有机会运行……