首页 > 解决方案 > 在信号处理程序中遍历 TAILQ

问题描述

我有一个维护结构列表的应用程序,由 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 关闭时检测到列表正在被修改,有没有办法阻止信号处理程序,直到另一个线程完成更新列表?

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

标签: clinuxconcurrencylinked-listsigaction

解决方案


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

1.signalfd(2)

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

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

2.无锁链表

将您的链表更改为无锁实现,以便它是信号安全的。无锁链接列表和跳过列表描述了一种可能的方法。


推荐阅读