我可以在 Linux 上创建循环缓冲区吗? (当前代码段错误)

发布于 2025-01-16 12:09:07 字数 1331 浏览 3 评论 0原文

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

所以我尝试了使用 shm_openftruncatemmap 执行相同的操作。我过去曾多次使用 mmap 来处理内存和文件,但我从未将其与 shm_open 混合使用或使用 shm_open

我的代码在第二个 mmap 上失败,并出现段错误。我尝试直接在两个 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, "Start\n", 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, "Finish\n", 7); //doesn't print this
    return 0;
}

Inspired by this example for Windows. In short, they create a file handle (with CreateFileMapping) then create 2 different pointers to the same memory (MapViewOfFileEx or MapViewOfFile3)

So I tried to do the same thing with shm_open, ftruncate and mmap. I used mmap a few times in the past for memory and files but I never mixed it with shm_open or used shm_open.

My code fails on the second mmap with a segfault. I tried doing a syscall directly on both mmaps and it still segfaults :( How do I do this properly? The idea is I can do memcpy(p+len-10, src, 20) and have the first 10bytes of src be at the end of the memory and last 10 written to the start (hence circular)

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
int main()
{
    write(2, "Start\n", 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, "Finish\n", 7); //doesn't print this
    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暮色兮凉城 2025-01-23 12:09:08

Linux 通常从某个点开始选择地址空间进行映射,并随着每次保留而降低。因此,您的第二个 mmap 调用替换了以前的文件映射之一(可能是 libc.so),这导致 SIGSEGVSEGV_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 | 调用 mmap 来保留内存MAP_PRIVATE(并且没有文件)
  • mmapMAP_FIXED 一起使用,用您需要的内容覆盖该映射的部分

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

Linux usually selects address space for mappings starting from a certain point and goes lower with each reservation. So your 2nd mmap call replaces one of previous file mappings (likely libc.so), which leads to SIGSEGV with SEGV_ACCERR - invalid access permissions. You are overwriting executable section of libc.so (that is being executed right now) with non-executable data.

Use strace to check what is going on inside:

$ 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 +++

Compare addresses you are passing around with /proc/$pid/maps file and you will see what you are overwriting.

Your mistake was to assume MAP_FIXED can be used without reserving memory beforehand. To do this properly you need to:

  • Reserve memory by calling mmap with len * 2 size, PROT_NONE and MAP_ANONYMOUS | MAP_PRIVATE (and without file)
  • Use mmap with MAP_FIXED to overwrite portions of that mapping with the content you need

Additionally, you should prefer using memfd_create instead of shm_open on Linux to avoid shared memory files from staying around. Unlinking them with shm_unlink doesn't help if your program crashes. This also gives you a file that is private to your program instance.

驱逐舰岛风号 2025-01-23 12:09:08

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

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

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
int main()
{
    write(2, "Start\n", 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, "Finish\n", 7); //doesn't print this
    return 0;
}

You do not need to call mmap again to gen new pointer. (You even must not to do it.) Just increment it.

The pointer p2 will not point to the address just after the memory block allocated.

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
int main()
{
    write(2, "Start\n", 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, "Finish\n", 7); //doesn't print this
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文