【问题标题】:How can I prevent (not react to) a segmentation fault?如何防止(不对)分段错误?
【发布时间】:2013-09-18 03:02:55
【问题描述】:

我不想处理分段错误。我或多或少地了解异常处理的工作原理。我宁愿一开始就没有错。我想要做的是调用一个函数或执行一个操作,该操作返回一个值,告诉我该特定内存位置/块是否可访问,而无需实际访问它并得到错误。

也就是说,我想要一个 C 函数在实际访问它之前探测 Linux 和/或 Mac OS X 中的地址。比如:

result = probe_memory(address,length)

结果在哪里

 0 = writable
 1 = read-only
-1 = nonexistent

或类似的东西。

在 Linux 和/或 Mac OS X 中有类似的东西吗?

【问题讨论】:

  • 想解析 TLB 吗?
  • 对于动态分配的内存(通过 malloc() 等分配),请考虑 Steve Maguire 在 Writing Solid Code 中描述的技术。 (另外请注意,有些人强烈反对书中所说的任何内容——您可以在 ACCU 网站以及其他地方找到此类评论。)
  • 在 Linux 上,您可能能够从 /proc/self/maps 中提取一些信息,但如果这不仅仅是出于好奇……如果您需要询问,您可能做错了什么地址是否有效。
  • @user157426:那么使用类似 valgrind 的东西怎么样?这实际上会捕获更难发现的错误——比如不会导致段错误的内存损坏。
  • 是的,这看起来像我正在寻找的东西。我要求一个例程,但一个包也可以。它似乎可用于 linux 和 mac,这正是我所需要的。我可以试一试。

标签: c linux macos segmentation-fault


【解决方案1】:

我相信或多或少像下面这样的东西应该可以工作:

int probe_memory(void *address, size_t length) {
    int result, fd[2];

    pipe(fd);  /* Remember to check for errors! */

    errno = 0;
    result = write(fd[1], address, length);

    if (result < 0 || (size_t)result != length || errno == EFAULT) {
        result = 0;
    } else {
        result = 1;
    }

    close(fd[0]);
    close(fd[1]);

    return result;
}

这是您问题的部分解决方案,因为此代码不检查页面保护。

基本思想是让操作系统通过调用writeaddress读取length字节。如果内存不可访问,它将返回EFAULT,而不会触发段错误。

Jonathan Leffler 在他的Stack Overflow Questions repository 中写了一个完整的实现。

【讨论】:

  • 我真的需要页面保护部分。我应该更多地强调这一点。它出现的原因是因为我有一个接收字符串作为参数的函数,并且可以对其进行修改。但是一个调用者传递了一个固定字符串(char canned[] = "hard-coded string";),我遇到了一个段错误。如果我知道它是只读的,我可以分配一个字符串、复制和修改副本。
  • 为什么不让调用者提供一个可写的字节缓冲区作为参数呢?如果速度如此重要,以至于您不愿意总是自己分配可写副本,那么为什么要花时间去探查字符串呢?
  • 有趣的想法;它很昂贵,需要一些调整,但它确实有效。调整是在错误检测中;那应该是:errno = 0; result = write(fd[1], address, length); if (result &lt; 0 || (size_t)result != length || errno == EFAULT) result = 0; else result = 1;。这将问题推给了内核。对于实际代码,您必须保持管道打开(一次调用 pipe() 和两次调用 close() 非常昂贵),但您还必须担心管道缓冲区的长度(Mac 上为 8 KiB OS X)和length 中的值(超过 8 KiB 会阻塞您的程序)。
  • 感谢@Jonathan 找出正确的错误检测,添加到帖子中。
  • 您可以在我的SOQ(堆栈溢出问题)存储库中的 GitHub 上找到基于此答案中的想法的工作代码作为文件 memprobe.cmemprobe.hsrc/so-1886-3184 子-目录。
【解决方案2】:

您可以使用mincore(2),并且可以顺序读取并解析/proc/self/maps,请参阅proc(5)

请注意,Linux 并非旨在快速提供此类映射信息。众所周知,模拟应用程序分页(如 GNU/Hurd 外部寻呼机...)很慢。 (例如,通过某些低级的、特定于处理器的 SIGSEGV 处理程序)。

如果您的唯一目的是在 SIGSEGV 上提供回溯信息,您可以使用来自 GCC 源代码的 Ian Taylor 的 libbacktrace

正如一些cmets所说,也许你想要valgrind

【讨论】:

    【解决方案3】:

    您忘记了您的响应列表 - 可写但操作系统正在使用/某些关键的东西/实际上是内存映射自毁等。

    您最好的办法是尝试编写防弹代码,其中一部分是您写入拥有的位置。

    分配、检查、复制、修改可能最终会变得更快、更可靠、更便携和更易于维护,而不是试图将操作系统臂扭曲为提供所需的信息,而不是尝试自己检查它.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多