2

I'm writing a program that for performance reasons uses shared memory (sockets and pipes as alternatives have been evaluated, and they are not fast enough for my task, generally speaking any IPC method that involves copies is too slow). In the shared memory region I am writing many structs of a fixed size. There is one program responsible for writing the structs into shared memory, and many clients that read from it. However, there is one member of each struct that clients need to write to (a reference count, which they will update atomically). All of the other members should be read only to the clients.

Because clients need to change that one member, they can't map the shared memory region as read only. But they shouldn't be tinkering with the other members either, and since these programs are written in C++, memory corruption is possible. Ideally, it should be as difficult as possible for one client to crash another. I'm only worried about buggy clients, not malicious ones, so imperfect solutions are allowed.

I can try to stop clients from overwriting by declaring the members in the header they use as const, but that won't prevent memory corruption (buffer overflows, bad casts, etc.) from overwriting. I can insert canaries, but then I have to constantly pay the cost of checking them.

Instead of storing the reference count member directly, I could store a pointer to the actual data in a separate mapped write only page, while keeping the structs in read only mapped pages. This will work, the OS will force my application to crash if I try to write to the pointed to data, but indirect storage can be undesirable when trying to write lock free algorithms, because needing to follow another level of indirection can change whether something can be done atomically.

Is there any way to mark smaller areas of memory such that writing them will cause your app to blow up? Some platforms have hardware watchpoints, and maybe I could activate one of those with inline assembly, but I'd be limited to only 4 at a time on 32-bit x86 and each one could only cover part of the struct because they're limited to 4 bytes. It'd also make my program painful to debug ;)

Edit: I found this rather eye popping paper, but unfortunately it requires using ECC memory and a modified Linux kernel.

4

3 回答 3

6

我认为不可能像在操作系统级别那样只读一些位。

我刚才想到的一件事是,您可以按照您的建议将引用计数放在不同的页面中。如果结构的大小相同,并且都位于顺序内存位置,则可以使用指针算法从结构指针中定位引用计数,而不是在结构中使用指针。这可能比为您的用例提供指针更好。

long *refCountersBase;//The start address of the ref counters page
MyStruct *structsBase;//The start address of your structures page

//get address to reference counter
long *getRefCounter(MyStruct *myStruct )
{
    size_t n = myStruct - structsBase;
    long *ref = refCountersBase + n;
    return ref;
}
于 2010-03-20T22:19:57.733 回答
1

您需要为从异常中恢复的 SIGSEGV 添加一个信号处理程序,但仅限于某些地址。起点可能是http://www.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html以及您操作系统的相应文档。

编辑:我相信您想要的是执行写入并在写入地址实际上正常的情况下返回,如果您想传播异常,则尾调用前一个异常处理程序(安装异常处理程序时获得的指针)。虽然我对这些事情没有经验。

于 2010-03-21T03:45:10.517 回答
1

我从未听说过以小于页面粒度强制只读,因此除非您可以将每个结构放在两页上,否则您可能会在这个方向上不走运。如果您可以负担每个结构的两页,则可以将引用计数放在其中一页上,而将另一页设为只读。

您可以编写 API 而不仅仅是使用标头。强制客户端使用 API 将消除大多数损坏问题。

将数据与引用计数一起保存而不是在不同的页面上将有助于数据的局部性,从而提高缓存性能。

您需要考虑阅读器可能有问题并且无法正确更新其引用计数。此外,作者可能无法完成更新。处理这些事情需要额外的检查。您可以将此类检查与 API 结合使用。可能值得尝试测量某种完整性检查的性能影响。它可能足够快以保持校验和,就像 adler32 一样简单。

于 2010-03-21T04:20:53.180 回答