在信号处理程序中运行 TAILQ

Walking a TAILQ in a signal handler

我有一个维护结构列表的应用程序,这些结构由 TAILQ 链接在一起。每个结构都有一个脏位,并指向内存中一些专用的特殊页面。我需要知道是否有人写入这些页面中的任何一个,所以我 mprotect 将它们写到 PROT_READ,然后安装一个信号处理程序以在检测到 SEGV 时关闭。

当处理程序被调用时,我检索地址并遍历我的列表以查看 segv 是否出现在我的任何页面上,如果出现,我将页面标记为脏页面,mprotect可写。所以它看起来像这样:

typedef struct _record_t {
     void        * start_addr;
     int           dirty;
     TAILQ_ENTRY(_record_t)
                   tailq_entry;
} record_t;

segv_handler(int sig, siginfo_t *si, void *p1) {
     void       * addr = si->si_addr;
     void       * addr_page = ROUND_DOWN(addr, page_size);
     record_t     rec;
     TAILQ_FOREACH(rec, g_reclist, tailq_entry) {
         if (rec->start_addr == addr_page) {
             rec->dirty = 1;
             mprotect(addr_btm, page_size, PROT_READ|PROT_WRITE);
             return;
         }
     }
     // otherwise call default page handler...
}

我担心能否在信号处理程序中安全地遍历列表。如果一个线程正在修改列表,而另一个线程生成 SEGV,我担心会出现一些未定义的行为。

如果我能在线程修改列表时查看它的地狱,并且我知道它不会生成 SEGV,那么有没有安全的方法在信号处理程序中遍历此列表?也就是说,如果我在 SEGV 关闭时检测到列表正在被修改,是否有任何方法可以阻止信号处理程序直到另一个线程完成更新列表?

最后,最后一个问题 -- 在某些情况下,可能需要修改信号处理程序中的列表(添加或删除条目)。有安全的方法吗?

我看到 2 种可能的解决方案:

1。 signalfd(2)

使用 signalfd(2) 并从主线程读取 signalfd 文件描述符。

此解决方案将允许您显式检查和处理 SEGV 信号,以便您可以确保链表不会同时被修改。

2。 Lock-free链表

将您的链接列表更改为 lock-free 实现,使其成为 signal-safe。 Lock-Free Linked Lists and Skip Lists 描述了一种可能的方法。