【问题标题】:Why do system calls return EFAULT instead of sending a segfault?为什么系统调用返回 EFAULT 而不是发送段错误?
【发布时间】:2012-03-25 04:42:56
【问题描述】:

需要明确的是,这是一个设计问题,而不是一个实现问题

我想知道 POSIX 为何如此行事的原因。 POSIX 系统调用在给定无效内存位置时返回 EFAULT 而不是使用户空间程序崩溃(通过发送 sigsegv),这使得它们的行为与用户空间函数不一致。

为什么?这不只是隐藏内存错误吗?这是一个历史错误还是有充分的理由?

【问题讨论】:

  • 对我来说,sigsegv/sigbus 也更有意义。现在我正在玩 2 个自定义系统调用,它们也应该有(更慢的)用户空间模拟。我不明白为什么实际的系统调用和仿真在传递无效缓冲区时应该表现不同。甚至 POSIX 似乎也认为用户不必关心系统函数是真正的系统调用还是用户空间函数。我的相关问题:stackoverflow.com/questions/44239545/…
  • 这是strace ./a.out 非常适合调试不检查系统调用错误的玩具程序的原因之一。

标签: c segmentation-fault posix standards signals


【解决方案1】:

好吧,你想要发生什么。系统调用是对系统的请求。如果你问:“去慕尼黑的渡轮什么时候出发?”您希望程序崩溃,还是使用 errno = ENOHARBOR 获得 return = -1 ?如果您要求系统将您的汽车放入您的手提包中,您希望销毁您的手提包,还是返回 -1 并将 errno 设置为 EBAGTOOSMALL ?

有一个技术细节:在系统调用之前或之后,在进入/离开系统调用时,必须转换(复制)到/来自用户/系统的参数。主要是出于安全原因,系统非常不愿意写入用户空间。 (Linux 对此有一个 copy_to_user_space 函数(反之亦然),它会在执行实际复制之前检查凭据


为什么?这不只是隐藏内存错误吗?`

相反。它允许您的程序处理错误(在这种情况下是不可能的),并优雅地终止。但是程序必须检查系统调用的返回值并检查errno。对于 SIGSEGVE,您的程序几乎没有什么可做的,因此将 EINVAL 映射到 SIGSEGVE 是个坏主意。

系统调用被设计为总是返回(或无限期阻塞......),无论它们成功还是失败。

技术方面可能是{segmentation faults, buserror, floating point exception, ...}(通常)是由硬件中断产生的。

【讨论】:

  • 我已经说过在这个问题中什么更有意义 - 向应用发送段错误信号。
  • 啊,也许我误解了你的问题。有一个技术问题,copyfromuser()、copytouser()(在linux的情况下)是从内核模式执行的,所以检查必须由内核“手动”执行,没有SEGVE 可能,内核可以为您执行此复制。因此,正式地说,它不是分段违规(如果从用户空间执行,它)另外:从用户进程的角度来看,系统调用只是一个具有返回值的函数。信号应该代表一些异步事件。
  • 这是一个糟糕的答案。这可能适用于任何函数,系统调用与否。
  • errno==ENOBADANSWER
【解决方案2】:

因为系统调用是由内核执行的,而不是由用户程序执行的——当系统调用发生时,用户进程会暂停并等待内核完成。

当然,内核本身是不允许seg fault的,所以它必须手动检查用户进程给它的所有地址区域。如果其中一项检查失败,系统调用将失败并显示EFAULT。所以在这种情况下,实际上并没有发生分段错误——内核显式检查以确保所有地址都有效,从而避免了分段错误。因此,没有信号发送是有道理的。

此外,如果发送了一个信号,内核将无法将有意义的程序计数器附加到该信号上,当系统调用执行时,用户进程实际上并没有执行跑步。这意味着用户进程无法产生体面的诊断、重新启动失败的指令等。

总结:主要是历史性的,但推理有实际逻辑。就像 EINTR 一样,这并没有减少处理它的烦恼。

【讨论】:

  • 信号上“附加一个有意义的程序计数器”是什么意思?你的意思是它不知道在用户的信号处理程序执行后从哪里恢复?那不就在系统调用之后吗?
  • 是的,忽略关于恢复的那一点——再想一想,这实际上并不相关。 (因为如果内核伪造 seg 错误,它很容易伪造寄存器状态。)我认为这里的主要问题是:内核没有向您发送 seg 错误,因为 实际上没有发生 seg 错误 .
  • 我怀疑这是因为他们不想做工作来让它工作,而这种方式更容易。
猜你喜欢
  • 2019-04-22
  • 2016-05-23
  • 1970-01-01
  • 2020-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 2012-02-26
相关资源
最近更新 更多