c-我可以在Linux上创建一个循环缓冲区吗?(当前代码segfault)



受此Windows示例的启发。简而言之,他们创建一个文件句柄(使用CreateFileMapping(,然后创建两个指向同一内存的不同指针(MapViewOfFileExMapViewOfFile3(

所以我试着用shm_openftruncatemmap做同样的事情。我过去曾多次使用mmap来处理内存和文件,但从未将其与shm_openshm_open混合使用。

我的代码在第二个mmap上失败,出现segfault。我尝试直接在两个mmap上执行系统调用,但它仍然存在以下错误:(我如何正确地执行此操作?想法是我可以执行memcpy(p+len-10, src, 20),并将src的前10个字节写在内存的末尾,将最后10个字节写入的开头(因此是循环的(

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
int main()
{
write(2, "Startn", 6); //prints this
int len = 1024*1024*2;
int fd = shm_open("example", O_RDWR | O_CREAT, 0777);
assert(fd > 0); //ok
int r1 = ftruncate(fd, len);
assert(r1 == 0); //ok
char*p = (char*)mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
assert((long long)p>0); //ok
//Segfaults on next line
char*p2 = (char*)mmap(p+len, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); //segfaults
write(2, "Finishn", 7); //doesn't print this
return 0;
}

Linux通常从某个点开始为映射选择地址空间,并随着每次保留而降低。因此,您的第二个mmap调用将替换以前的一个文件映射(可能是libc.so(,这将导致SIGSEGV具有SEGV_ACCERR——无效的访问权限。您正在用不可执行的数据覆盖libc.so的可执行部分(现在正在执行(。

使用strace检查内部发生了什么:

$ strace ./a.out 
...
openat(AT_FDCWD, "/dev/shm/example", O_RDWR|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0777) = 3
ftruncate(3, 2097152)                   = 0
mmap(NULL, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7f134c1bf000
mmap(0x7f134c3bf000, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x7f134c3bf000
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7f134c4ccc37} ---
+++ killed by SIGSEGV +++

将您传递的地址与/proc/$pid/maps文件进行比较,您将看到您正在覆盖的内容。

您的错误是认为MAP_FIXED可以在不预先保留内存的情况下使用。要正确地做到这一点,您需要:

  • 通过使用len * 2大小、PROT_NONEMAP_ANONYMOUS | MAP_PRIVATE调用mmap来保留内存(并且不带文件(
  • mmapMAP_FIXED结合使用,可以用所需的内容覆盖该映射的部分

此外,您应该更喜欢在Linux上使用memfd_create,而不是shm_open,以避免共享内存文件留下。如果您的程序崩溃,将它们与shm_unlink取消链接将没有帮助。这也为您提供了一个程序实例专用的文件。

您不需要再次调用mmap来生成新指针。(你甚至不能这样做。(只需增加它。

指针p2不会指向所分配的内存块之后的地址

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
int main()
{
write(2, "Startn", 6); //prints this
int len = 1024*1024*2;
int fd = shm_open("example", O_RDWR | O_CREAT, 0777);
assert(fd > 0); //ok
int r1 = ftruncate(fd, len);
assert(r1 == 0); //ok
char*p = (char*)mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
assert((long long)p>0); //ok
char*p2 = p+len;
write(2, "Finishn", 7); //doesn't print this
return 0;
}