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

时间:2015-08-05 15:38:45

标签: multithreading operating-system signals

我尝试创建类似memcpy的函数,当在内存中作为未分配页面的一部分给出地址时,该函数将正常失败(即返回错误而不是segfaulting)。我认为正确的方法是安装sigsegv信号处理程序,并在处理程序中执行某些操作以使memcpy函数停止复制。

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

  • 信号处理程序是否可以在另一个线程中执行?
  • 如果段错误与任何memcpy操作无关,会发生什么?
  • 如何同时处理两个执行memcpy的线程?
  • 我错过了别的什么吗?我在找一些无法实现的东西吗?

1 个答案:

答案 0 :(得分:1)

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

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

来自man 2 sigaction

  

正常返回后,进程的行为是未定义的   SIGBUS,SIGFPE,SIGILL或SIGSEGV的信号捕获功能   不是由kill(),sigqueue()或raise()生成的信号。

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

这也在POSIX中指定。虽然它适用于Linux,但它并不是一种好的做法。

以下是您问题的具体答案:

  

信号处理程序是否可以在另一个线程中执行?

是的,确实如此。虽然在Linux和许多其他UNIX变体中,异常相关信号(SIGILLSIGFPE,但是信号被传递到任何未阻塞它的线程(但当然只传递给一个)。 SIGBUSSIGSEGV)通常会传递给导致异常的线程。但这不是必需的,因此为了获得最大的可移植性,您不应该依赖它。

您可以使用pthread_sigmask(2)阻止每个线程中的信号,但只有一个;这样你就可以确保每个信号始终传递到同一个线程。这使得单个线程专用于信号处理变得容易,这反过来允许您进行同步信号处理,因为线程可能使用sigwait(2)(注意多线程代码应该使用sigwait(2)而不是sigsuspend(2) {1}})直到信号被传递然后同步处理它。这是一种非常常见的模式。

  

如果段错误与任何memcpy操作无关,会发生什么?

好问题。信号被传递,并且没有(微不足道的)方法可以在memcpy(3)中将真正的段错误与段错误进行便携式区分。

如果您有一个线程负责处理每个信号,就像我上面提到的那样,您可以使用sigwaitinfo(2),然后检查一次si_addr siginfo_t的{​​{1}}字段sigwaitinfo(2)回。 si_addr字段是导致错误的内存位置,因此您可以将其与传递给memcpy(3)的内存地址进行比较。

但某些平台,尤其是Mac OS,并未实现sigwaitinfo(2)或其表兄sigtimedwait(2)

所以没有办法可以移植。

  

如何同时处理两个执行memcpy的线程?

我真的不明白这个问题,多线程memcpy(3)有什么特别之处?调用者有责任确保不会同时访问读取和写入的内存区域;如果你将重叠缓冲区传递给它,那么memcpy(3)就不是(也绝不是)线程安全的。

  

我错过了别的什么吗?我在找东西吗?   不可能实施?

如果你担心便携性,我会说这几乎是不可能的。即使你只关注Linux,也很难。如果这很容易做到,那么有人可能已经做过了。

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