我可以安全地访问可能未分配的内存地址吗?
Can I safely access potentially unallocated memory addresses?
我正在尝试创建类似 memcpy 的函数,当给定属于未分配页面一部分的内存地址时,该函数将优雅地失败(即 return 错误而不是段错误)。我认为正确的方法是安装一个 sigsegv 信号处理程序,并在处理程序中做一些事情来使 memcpy 函数停止复制。
但我不确定在我的程序是多线程的情况下会发生什么:
- 信号处理程序是否可以在另一个线程中执行?
- 如果段错误与任何 memcpy 操作无关,会发生什么情况?
- 如何处理两个同时执行 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=、SIGBUS
和 SIGSEGV
) 通常传递给引起异常的线程。但这不是必需的,因此为了获得最大的可移植性,您不应该依赖它。
您可以使用 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_t
的 si_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,也很难。如果这是一件容易做的事,到这个时候可能已经有人做了。
我认为您最好构建自己的分配器并强制用户代码依赖它。然后您可以存储状态和管理分配的内存,并轻松判断传递的缓冲区是否有效。
我正在尝试创建类似 memcpy 的函数,当给定属于未分配页面一部分的内存地址时,该函数将优雅地失败(即 return 错误而不是段错误)。我认为正确的方法是安装一个 sigsegv 信号处理程序,并在处理程序中做一些事情来使 memcpy 函数停止复制。
但我不确定在我的程序是多线程的情况下会发生什么:
- 信号处理程序是否可以在另一个线程中执行?
- 如果段错误与任何 memcpy 操作无关,会发生什么情况?
- 如何处理两个同时执行 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=、SIGBUS
和 SIGSEGV
) 通常传递给引起异常的线程。但这不是必需的,因此为了获得最大的可移植性,您不应该依赖它。
您可以使用 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_t
的 si_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,也很难。如果这是一件容易做的事,到这个时候可能已经有人做了。
我认为您最好构建自己的分配器并强制用户代码依赖它。然后您可以存储状态和管理分配的内存,并轻松判断传递的缓冲区是否有效。