我可以安全地访问可能未分配的内存地址吗?

Can I safely access potentially unallocated memory addresses?

我正在尝试创建类似 memcpy 的函数,当给定属于未分配页面一部分的内存地址时,该函数将优雅地失败(即 return 错误而不是段错误)。我认为正确的方法是安装一个 sigsegv 信号处理程序,并在处理程序中做一些事情来使 memcpy 函数停止复制。

但我不确定在我的程序是多线程的情况下会发生什么:

相信我,你不想走那条路。出于多种原因,这是一罐蠕虫。正确的信号处理在单线程环境中已经很困难,但在多线程代码中更是如此。

首先,从由异常条件引起的信号处理程序返回是未定义的行为 - 它在 Linux 中有效,但它仍然是未定义的行为,迟早会给你带来问题.

来自 man 2 sigaction:

The behaviour of a process is undefined after it returns normally from a signal-catching function for a SIGBUS, SIGFPE, SIGILL or SIGSEGV signal that was not generated by kill(), sigqueue() or raise().

(注意:这不会出现在 Linux 联机帮助页上;but it's in SUSv2

这在POSIX中也有规定。虽然它在 Linux 中有效,但这不是好的做法。

下面具体回答您的问题:

Is it possible for the signal handler to execute in another thread?

是的,是的。一个信号被传递给任何不阻塞它的线程(但当然只传递给一个线程),尽管在 Linux 和许多其他 UNIX 变体中,与异常相关的信号(SIGILL,[= 12=、SIGBUSSIGSEGV) 通常传递给引起异常的线程。但这不是必需的,因此为了获得最大的可移植性,您不应该依赖它。

您可以使用 pthread_sigmask(2) 来阻止除一个线程之外的每个线程中的信号;这样你就可以确保每个信号总是传递到同一个线程。这使得拥有一个专用于信号处理的线程变得容易,这反过来又允许您进行同步信号处理,因为线程可能使用 sigwait(2) (注意多线程代码应该使用 sigwait(2) 而不是 sigsuspend(2)) 直到一个信号传递过来,然后同步处理。这是一个很常见的模式。

What happens if a segfault isn't related to any memcpy operation?

好问题。信号已发送,并且没有(简单的)方法可以方便地将真正的段错误与 memcpy(3).

中的段错误区分开来

如果你有一个线程处理每个信号,就像我上面提到的,你可以使用 sigwaitinfo(2),然后检查 siginfo_tsi_addr 字段一次 sigwaitinfo(2) 返回。 si_addr 字段是导致错误的内存位置,因此您可以将其与传递给 memcpy(3).

的内存地址进行比较

但是一些平台,最著名的是 Mac OS,没有实现 sigwaitinfo(2) 或其堂兄弟 sigtimedwait(2)

所以没办法移植。

How does one handle two threads executing memcpy concurrently?

这道题我不是很懂,多线程有什么特别的memcpy(3)?调用者有责任确保不会同时访问正在读取和写入的内存区域; memcpy(3) 如果您传递重叠缓冲区,它不是(也从来不是)线程安全的。

Am I missing something else? Am I looking for something that's impossible to implement?

如果您关心便携性,我会说这几乎是不可能的。即使你只专注于Linux,也很难。如果这是一件容易做的事,到这个时候可能已经有人做了。

我认为您最好构建自己的分配器并强制用户代码依赖它。然后您可以存储状态和管理分配的内存,并轻松判断传递的缓冲区是否有效。