sigaction(7):siginfo_t 的 si_code 成员的语义

Bru*_*ger 5 signals segmentation-fault linux-kernel

我有一个长时间运行的程序(通过 daemon(3) 调用变成一个守护进程),它每隔一段时间就会在 Signal 11(分段违规)上退出。我说不清为什么。因此,我编写了一个 SIGSEGV 处理程序,使用sigaction()系统调用进行设置。我设置了处理程序函数,使它具有这个原型:void (*sa_sigaction)(int, siginfo_t *, void *)这意味着它获得一个指向siginfo_t结构的指针作为形式参数。

在神秘的 SIGSEGV 之际,si_code元素的siginfo_t值为 0x80,这意味着,根据 sigaction 手册页,“内核”发送了信号。这是在 Red Hat RHEL 系统上:Linux blahblah 2.6.18-308.20.1.el5 #1 SMP Tue Nov 6 04:38:29 EST 2012 x86_64 x86_64 x86_64 GNU/Linux

为什么内核会发送 SIGSEGV?这是来自著名的 OOM-Killer,还是存在获得 SIGSEGV 的其他原因?作为这个系统的普通用户,我看不到/var/log/message,而且系统管理员有点冷漠,可能是因为他们来自 Windows 背景。

故意生成的 SIGSEGV(取消引用 NULL 指针)不会获得si_code0x80的值,而是获得 0x1,这意味着“地址未映射到对象”。

Jon*_*ham 5

si_code = SI_KERNELwith的未记录语义si_errno = 0是,

  1. 特定于处理器的陷阱
  2. 内核段内存冲突(信号量访问除外)
  3. ELF 文件格式违规,以及
  4. 堆栈违规。

所有其他SIGSEGVs 都应si_errno设置为非零值。请继续阅读以了解详细信息。

当内核建立一个用户空间进程时,它为该进程定义了一个虚拟内存页表。当内核调度器运行进程时,它会根据进程的页表重新配置 CPU 的内存管理单元 (MMU)。

当用户空间进程尝试访问其页表之外的内存时,CPU MMU 会检测到此违规并生成异常。请注意,这发生在硬件级别。内核尚未涉及。

内核设置为处理 MMU 异常。它捕获由正在运行的进程尝试访问其页表之外的内存而引起的异常。然后内核调用do_page_fault()它向进程发送 SIGSEGV 信号。这就是为什么信号来自内核而不是来自进程本身或另一个进程的原因。

这当然是一个高度简化的解释。我见过的最好的简单解释是 William Gatliff 的美丽文章The Linux Kernel's Memory Management Unit API 中的“Page Faults”部分。

请注意,在没有 MMU 的 CPU(例如 Blackfin MPU)上,Linux 用户空间进程通常可以访问任何内存。即没有针对内存违规的 SIGSEGV 信号(仅针对堆栈溢出等陷阱)并且调试内存访问问题可能很棘手。

我第二个 jordanm 关于设置ulimit和检查核心文件的评论gdb。你可以做ulimit -c unlimited,如果你从一个shell中运行的过程中,或者使用libc中的命令行setrlimit系统调用包装(man setrlimit在你的程序)。您可以在 file 中设置核心文件的名称及其位置/proc/sys/kernel/core_pattern。请参阅 AP Lawrence 在控制核心文件 (Linux) 中对此的出色说明。要gdb在 corefile 上使用,请参阅Steve.org上的这个小教程

甲段违规与si_codeSEGV_MAPERR(为0x1)可能是一个空指针,不存在的存储器,诸如0xfffffc0000004000的访问,或mallocfree问题。堆损坏或进程超出其运行时限制 ( man getrlimit)malloc和双重释放或释放未分配地址的情况free。查看si_errno元素以获取更多线索。

由于用户空间进程访问超过TASK_SIZE限制的虚拟内存而发生的分段违规将导致分段违规,其si_code值为SI_KERNEL。换句话说,TASK_SIZE限制是允许任何进程访问的最高虚拟地址。这通常是 3GB,除非内核配置为支持高内存。超出TASK_SIZE限制的区域称为“内核段”。看看linux-2.6//arch/x86/mm/fault.c:__bad_area_nosemaphore(...)它在哪里调用force_sig_info_fault(...)

对于每种架构,还有许多特定的陷阱会导致 SISEGV 带有SI_KERNEL. 对于 x86,这些由linux-2.6//arch/x86/kernel/traps.c.

OOM 处理程序发送 SIGKILL,而不是 SIGSEGV,正如linux-2.6//mm/oom_kill.c:oom_kill_process(...)在大约第 498 行的函数中所见:

do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true);
Run Code Online (Sandbox Code Playgroud)

对于相关流程和第 503 行:

do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);
Run Code Online (Sandbox Code Playgroud)

对于作为 OOM 最接近原因的过程。

您可以通过查看wait从其父进程中被杀死的进程的状态以及可能通过查看dmesg或更好地配置内核日志并查看它来获取更多信息。