Linux 在/dev/shm子目录中创建共享内存时,shm_open()与EINVAL一起失败
我有一个GNU/Linux应用程序,它使用了许多共享内存对象。它可能在同一个系统上运行多次。为了保持整洁,我首先在Linux 在/dev/shm子目录中创建共享内存时,shm_open()与EINVAL一起失败,linux,shared-memory,glibc,Linux,Shared Memory,Glibc,我有一个GNU/Linux应用程序,它使用了许多共享内存对象。它可能在同一个系统上运行多次。为了保持整洁,我首先在/dev/shm中为每个共享内存对象集创建一个目录 问题是,在较新的GNU/Linux发行版上,我似乎不再能够在/dev/shm的子目录中创建这些 下面是一个简单的C程序,它演示了我所说的内容: /***************************************************************************** * shm_minimal.c
/dev/shm
中为每个共享内存对象集创建一个目录
问题是,在较新的GNU/Linux发行版上,我似乎不再能够在/dev/shm
的子目录中创建这些
下面是一个简单的C程序,它演示了我所说的内容:
/*****************************************************************************
* shm_minimal.c
*
* Test shm_open()
*
* Expect to create shared memory file in:
* /dev/shm/
* └── my_dir
* └── shm_name
*
* NOTE: Only visible on filesystem during execution. I try to be nice, and
* clean up after myself.
*
* Compile with:
* $ gcc -lrt shm_minimal.c -o shm_minimal
*
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, const char* argv[]) {
int shm_fd = -1;
char* shm_dir = "/dev/shm/my_dir";
char* shm_file = "/my_dir/shm_name"; /* does NOT work */
//char* shm_file = "/my_dir_shm_name"; /* works */
// Create directory in /dev/shm
mkdir(shm_dir, 0777);
// make shared memory segment
shm_fd = shm_open(shm_file, O_RDWR | O_CREAT, 0600);
if (-1 == shm_fd) {
switch (errno) {
case EINVAL:
/* Confirmed on:
* kernel v3.14, GNU libc v2.19 (ArchLinux)
* kernel v3.13, GNU libc v2.19 (Ubuntu 14.04 Beta 2)
*/
perror("FAIL - EINVAL");
return 1;
default:
printf("Some other problem not being tested\n");
return 2;
}
} else {
/* Confirmed on:
* kernel v3.8, GNU libc v2.17 (Mint 15)
* kernel v3.2, GNU libc v2.15 (Xubuntu 12.04 LTS)
* kernel v3.1, GNU libc v2.13 (Debian 6.0)
* kernel v2.6.32, GNU libc v2.12 (RHEL 6.4)
*/
printf("Success !!!\n");
}
// clean up
close(shm_fd);
shm_unlink(shm_file);
rmdir(shm_dir);
return 0;
}
/* vi: set ts=2 sw=2 ai expandtab:
*/
/*****************************************************************************
*shm_minimal.c
*
*测试shm_open()
*
*希望在以下位置创建共享内存文件:
*/dev/shm/
* └── 我的主任
* └── shm_名称
*
*注意:仅在执行期间在文件系统上可见。我试着和蔼可亲,而且
*我自己收拾一下。
*
*编译时使用:
*$gcc-轻轨最小值。c-o最小值
*
******************************************************************************/
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,const char*argv[]{
int shm_fd=-1;
char*shm_dir=“/dev/shm/my_dir”;
char*shm_file=“/my_dir/shm_name”/*不起作用*/
//char*shm\u file=“/my\u dir\u shm\u name”/*有效*/
//在/dev/shm中创建目录
mkdir(shm_dir,0777);
//创建共享内存段
shm_fd=shm_open(shm_文件,O_RDWR | O_CREAT,0600);
如果(-1==shm\u fd){
开关(错误号){
案例EINVAL:
/*确认日期:
*内核v3.14,GNU libc v2.19(ArchLinux)
*内核v3.13,GNU libc v2.19(Ubuntu 14.04 Beta 2)
*/
perror(“FAIL-EINVAL”);
返回1;
违约:
printf(“其他一些未测试的问题”);
返回2;
}
}否则{
/*确认日期:
*内核v3.8,GNU libc v2.17(Mint 15)
*内核v3.2,GNU libc v2.15(Xubuntu 12.04 LTS)
*内核v3.1,GNU libc v2.13(Debian 6.0)
*内核v2.6.32,GNU libc v2.12(RHEL 6.4)
*/
printf(“成功!!!\n”);
}
//清理
关闭(shm_fd);
shm_取消链接(shm_文件);
rmdir(shm_dir);
返回0;
}
/*vi:设置ts=2 sw=2 ai扩展选项卡:
*/
当我在一个相当新的发行版上运行这个程序时,对shm_open()
的调用返回-1
,并且errno
被设置为EINVAL
。但是,当我在稍旧的东西上运行时,它会像预期的那样在/dev/shm/my_dir
中创建共享内存对象
对于较大的应用程序,解决方案很简单。我可以使用公共前缀而不是目录
如果你能帮助我了解这种行为上的明显变化,那将非常有帮助。我怀疑其他人可能也在尝试做类似的事情。因此,问题源于GNU libc如何验证共享内存名。具体来说,共享内存对象现在必须位于
shmfs
装载点的根
这是在glibc中由于错误而更改的
具体而言,变化是:
if (name[0] == '\0' || namelen > NAME_MAX || strchr (name, '/') != NULL)
现在文件名中的任何位置都不允许使用
'/'
(不包括前导的'/'
)如果您有第三方工具被此shm_open更改破坏,一位出色的同事找到了一个解决方法:预加载一个库,该库覆盖shm_open调用并为下划线交换斜线。它对shm_unlink也有同样的作用,因此应用程序可以在需要时适当地释放共享内存
deslash_shm.cc:
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#include <string>
// function used in place of the standard shm_open() function
extern "C" int shm_open(const char *name, int oflag, mode_t mode)
{
// keep a function pointer to the real shm_open() function
static int (*real_open)(const char *, int, mode_t) = NULL;
// the first time in, ask the dynamic linker to find the real shm_open() function
if (!real_open) real_open = (int (*)(const char *, int, mode_t)) dlsym(RTLD_NEXT,"shm_open");
// take the name we were given and replace all slashes with underscores instead
std::string n = name;
std::replace(n.begin(), n.end(), '/', '_');
// call the real open function with the patched path name
return real_open(n.c_str(), oflag, mode);
}
// function used in place of the standard shm_unlink() function
extern "C" int shm_unlink(const char *name)
{
// keep a function pointer to the real shm_unlink() function
static int (*real_unlink)(const char *) = NULL;
// the first time in, ask the dynamic linker to find the real shm_unlink() function
if (!real_unlink) real_unlink = (int (*)(const char *)) dlsym(RTLD_NEXT, "shm_unlink");
// take the name we were given and replace all slashes with underscores instead
std::string n = name;
std::replace(n.begin(), n.end(), '/', '_');
// call the real unlink function with the patched path name
return real_unlink(n.c_str());
}
并在启动尝试在shm_open中使用非标准斜杠字符的进程之前预加载:
在bash中:
export LD_PRELOAD=/path/to/deslash_shm.so
在tcsh中:
setenv LD_PRELOAD /path/to/deslash_shm.so
POSIX表示,
shm_open
的参数中除第一个字符外的斜杠字符是“实现定义的”()。实际上,我会尝试(a)在strace
下运行,看看实际发生了什么;(b)使用不带斜杠的my_dir/my_name
,看看会发生什么。(尽管您处于实现定义的领域,但您可能只想创建自己的tmpfs并直接使用它。)@Nemo谢谢,这证实了我刚才在阅读glibc源代码和bug追踪器时发现的并不是POSIX违规行为。我猜新的“实现定义”是没有子目录的。它发生的方式很有趣。就在我即将放弃的时候,我找到了答案。@Nemo使用strace
是一个很好的建议。谢天谢地,这个大型应用程序是我正在编写的,所以我只需附加调试器就可以做得更好。关于领先的“/”
,现在我已经阅读了glibc代码,我可以说它不会有任何区别。他们要做的第一件事是删除所有的前导字符。实现在文件sysdeps/unix/sysv/linux/shm_open.c
中。谢谢。调查得不错(+1)。让glibc以最不有用的方式解释“实现定义”。。。这意味着如果你真的想要子目录结构,就要创建你自己的tmpfs。谢谢。这是保存在工具箱中的一小段方便的代码。
setenv LD_PRELOAD /path/to/deslash_shm.so