相信我,你确实不想走那条路。出于多种原因,这是一罐蠕虫。正确的信号处理在单线程环境中已经很困难,但在多线程代码中却很困难。
首先,从异常条件导致的信号处理程序返回是未定义的行为 - 它在 Linux 中有效,但它仍然是未定义的行为,迟早会给您带来问题。
来自man 2 sigaction:
进程正常返回后的行为是不确定的
SIGBUS、SIGFPE、SIGILL 或 SIGSEGV 的信号捕获功能
不是由 kill()、sigqueue() 或 raise() 生成的信号。
(注意:这不会出现在 Linux 手册页上;but it's in SUSv2)
这也在 POSIX 中指定。虽然它在 Linux 中有效,但这不是一个好的做法。
下面是您问题的具体答案:
信号处理程序是否可以在另一个线程中执行?
是的,是的。一个信号被传递给任何没有阻塞它的线程(当然只传递给一个线程),尽管在 Linux 和许多其他 UNIX 变体中,异常相关的信号(SIGILL、SIGFPE、SIGBUS 和SIGSEGV) 通常会传递给导致异常的线程。不过这不是必需的,因此为了获得最大的可移植性,您不应依赖它。
您可以使用pthread_sigmask(2) 来阻止除一个以外的每个线程中的信号;这样你就可以确保每个信号总是被传递到同一个线程。这使得有一个专用于信号处理的线程很容易,这反过来又允许您进行同步信号处理,因为线程可能使用sigwait(2)(注意多线程代码应该使用sigwait(2)而不是sigsuspend(2))直到传递一个信号,然后同步处理它。这是一种很常见的模式。
如果段错误与任何 memcpy 操作无关,会发生什么?
好问题。信号已传递,并且没有(微不足道的)方法可以轻松区分真正的段错误和 memcpy(3) 中的段错误。
如果你有一个线程处理每个信号,就像我上面提到的那样,你可以使用sigwaitinfo(2),然后在sigwaitinfo(2) 返回时检查siginfo_t 的si_addr 字段。 si_addr 字段是导致故障的内存位置,因此您可以将其与传递给memcpy(3) 的内存地址进行比较。
但某些平台,尤其是 Mac OS,没有实现 sigwaitinfo(2) 或其近亲 sigtimedwait(2)。
所以没有办法做到便携。
如何处理同时执行 memcpy 的两个线程?
这个问题我不是很懂,多线程memcpy(3)有什么特别之处?调用者有责任确保正在读取和写入的内存区域不会被同时访问; memcpy(3) 不是(也从来不是)线程安全的,如果你将它传递给重叠的缓冲区。
我还缺少其他东西吗?我在寻找什么
无法实现?
如果您关心可移植性,我会说这几乎是不可能的。即使你只专注于 Linux,也很难。如果这很容易做到,那么这个时候可能已经有人做到了。
我认为您最好构建自己的分配器并强制用户代码依赖它。然后您可以存储状态并管理分配的内存,并轻松判断传递的缓冲区是否有效。