kernel crash 发生后的那些事(一)

本文以echo c > /proc/sysrq-trigger触发的Kernel crash为例,详细解析了Linux内核中处理空指针异常的过程,包括汇编指令的跳转、故障地址的获取以及异常处理函数的调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文根据 echo  c > /pro/sysrq-trigger 触发的 Kernel crash 为例,分析kernel crash 处理的过程。


当代码访问虚拟地址0时,会发生data abort,这是由MMU决定的,没有把virtual address 0 map 到任何有访问权限的物理地址。

当发生data abort时,PC 会跳转到W(b) vector_dabt + stubs_offset[arch/arm/kernel/entry-armv.S]。如果data abort 发生

在内核空间,会进而执行到__dabt_svc.

__dabt_svc

    .align    5
__dabt_svc:
    svc_entry
    mov    r2, sp
    dabt_helper

    @
    @ IRQs off again before pulling preserved data off the stack
    @
    disable_irq_notrace
    svc_exit r5                @ return from exception
 UNWIND(.fnend        )
ENDPROC(__dabt_svc)


__dabt_svc调用了宏dabt_helper

    .macro    dabt_helper

    @
    @ Call the processor-specific abort handler:
    @
    @  r2 - pt_regs
    @  r4 - aborted context pc
    @  r5 - aborted context psr
    @
    @ The abort handler must return the aborted address in r0, and
    @ the fault status register in r1.  r9 must be preserved.
    @

    bl    CPU_DABORT_HANDLER
    .endm

 CPU_DABORT_HANDLER的具体实现

dabt_helper调用了跳转指令bl    CPU_DABORT_HANDLER,标号CPU_DABORT_HANDLER有多个实现,可以根据  .config找到具体的实现。
在文件 arch/arm/include/asm/glue-df.h中找到:
#ifdef CONFIG_CPU_ABRT_EV7
# ifdef CPU_DABORT_HANDLER
#  define MULTI_DABORT 1
# else
#  define CPU_DABORT_HANDLER v7_early_abort
# endif
#endif
看上去挺特殊,为什么每行都以#开头,应该不是注释掉的意思吧。

到文件arch/arm/mm/abort-ev7.S:
    .align    5
ENTRY(v7_early_abort)
    /*
     * The effect of data aborts on on the exclusive access monitor are
     * UNPREDICTABLE. Do a CLREX to clear the state
     */
    clrex

    mrc    p15, 0, r1, c5, c0, 0        @ get FSR 【Fault Status Register (FSR)】
    mrc    p15, 0, r0, c6, c0, 0        @ get FAR 【Fault Address Register (FAR).】

    /*
     * V6 code adjusts the returned DFSR.
     * New designs should not need to patch up faults.
     */

    b    do_DataAbort
ENDPROC(v7_early_abort)
这里只关注 b    do_DataAbort,终于跳转到C 函数do_DataAbort

跳转到C函数 do_DataAbort

arch/arm/mm/fault.c
/*
 * Dispatch a data abort to the relevant handler.
 */
asmlinkage void __exception
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
    const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
    struct siginfo info;

    if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
        return;

    printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
        inf->name, fsr, addr);

    info.si_signo = inf->sig;
    info.si_errno = 0;
    info.si_code  = inf->code;
    info.si_addr  = (void __user *)addr;
    arm_notify_die("", regs, &info, fsr, 0);
}

请关注从汇编过来的入口参数:

do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
r0: mrc    p15, 0, r0, c6, c0, 0        @ get FAR 【Fault Address Register (FAR).】
r1: mrc    p15, 0, r1, c5, c0, 0        @ get FSR 【Fault Status Register (FSR)】
r2: @  r2 - pt_regs [r2是哪里]
这里涉及到CP15的几个寄存器:
The CP15 register c2 operations control the Translation Table Base (TTB).
The CP15 register c3 operations control the Domain Access Control (DAC) register.
The CP15 register c5 operations control the Fault Status Register (FSR).


根据fsr找到对应的处理函数

fsr_info是全局变量,类型为
struct fsr_info {
    int    (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
    int    sig;
    int    code;
    const char *name;
};
通过当前的fsr作为index 得到对应的出来函数。

/* FSR definition 对本平台而言,# CONFIG_ARM_LPAE is not set*/
#ifdef CONFIG_ARM_LPAE
#include "fsr-3level.c"
#else
#include "fsr-2level.c"
#endif

我们要根据 “const struct fsr_info *inf = fsr_info + fsr_fs(fsr);”得到具体的函数,因为do_DataAbort被多次调用去加载物理页,
如果在该函数do_DataAbort中打印,看上去一直在打印好像开不了机。请注意,do_DataAbort(unsigned long addr,--)的第一个参数,就是r0
r0: mrc    p15, 0, r0, c6, c0, 0        @ get FAR 【Fault Address Register (FAR).】,存放的是出问题是访问的CPU访问的地址.
如果触发的是空指针crash, 则r0,也就是do_DataAbort的 addr参数为0.可以加个条件去打印。

do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
    const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
    struct siginfo info;
    /*threre so many callbacks*/
    if(!addr)
        printk(KERN_ALERT "do_DataAbort: NULL index:0x%x\n",fsr_fs(fsr));
    if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
        return;

}
输出:do_DataAbort: NULL index:0x5,这样可知对应的函数为do_translation_fault。
static struct fsr_info fsr_info[] = {
    /*
     * The following are the standard ARMv3 and ARMv4 aborts.  ARMv5
     * defines these to be "precise" aborts.
     */
    { do_bad,        SIGSEGV, 0,        "vector exception"           },
    { do_bad,        SIGBUS,     BUS_ADRALN,    "alignment exception"           },
    { do_bad,        SIGKILL, 0,        "terminal exception"           },
    { do_bad,        SIGBUS,     BUS_ADRALN,    "alignment exception"           },
    { do_bad,        SIGBUS,     0,        "external abort on linefetch"       },
    { do_translation_fault,    SIGSEGV, SEGV_MAPERR,    "section translation fault"       },
    { do_bad,        SIGBUS,     0,        "external abort on linefetch"       },
    { do_page_fault,    SIGSEGV, SEGV_MAPERR,    "page translation fault"       },
    { do_bad,        SIGBUS,     0,        "external abort on non-linefetch"  },
    { do_bad,        SIGSEGV, SEGV_ACCERR,    "section domain fault"           },
    { do_bad,        SIGBUS,     0,        "external abort on non-linefetch"  },
    { do_bad,        SIGSEGV, SEGV_ACCERR,    "page domain fault"           },
    { do_bad,        SIGBUS,     0,        "external abort on translation"       },
    { do_sect_fault,    SIGSEGV, SEGV_ACCERR,    "section permission fault"       },
    { do_bad,        SIGBUS,     0,        "external abort on translation"       },
    { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "page permission fault"           },
}

kernel mode空指针处理函数do_translation_fault



do_translation_fault(unsigned long addr, unsigned int fsr,
             struct pt_regs *regs)
{
    unsigned int index;
    pgd_t *pgd, *pgd_k;
    pud_t *pud, *pud_k;
    pmd_t *pmd, *pmd_k;

    if (addr < TASK_SIZE)
        return do_page_fault(addr, fsr, regs);
}

do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
    struct task_struct *tsk;
    struct mm_struct *mm;
    int fault, sig, code;
    int write = fsr & FSR_WRITE;
    unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
                (write ? FAULT_FLAG_WRITE : 0);

    /*
     * If we're in an interrupt or have no user
     * context, we must not take the fault..
     */
    if (in_atomic() || !mm){
        printk(KERN_ALERT "do_page_fault:mm_struct_1 0x%x\n", mm);
        goto no_context;
    }

    if (!down_read_trylock(&mm->mmap_sem)) {
        if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)){
            printk(KERN_ALERT "do_page_fault:mm_struct_2 0x%x\n", mm);
            goto no_context;
        }
        down_read(&mm->mmap_sem);
    }
    else{
        fault = __do_page_fault(mm, addr, fsr, flags, tsk);
        /*
         * If we are in kernel mode at this point, we
         * have no context to handle this fault with.
         */
        if (!user_mode(regs)){
            printk(KERN_ALERT "do_page_fault:mm_struct_3 0x%x\n", mm);
            goto no_context;
        }
    }
no_context:
    printk(KERN_ALERT "__do_kernel_fault: do_page_fault:index 0x%x\n", fsr_fs(fsr));
    __do_kernel_fault(mm, addr, fsr, regs);
    return 0;

}

如果是crash happened in kernel mode 最终会调到__do_kernel_fault。

对echo c > /proc/sysrq-trigger触发的空指针crash而言:所说的空指针虚拟地址为0, 0 < TASK_SIZE, 所以会调用到do_page_fault,找不到对应的VMA等信息,

且发生异常时在内核空间,所以 goto no_context.

最后调到 __do_kernel_fault。

<think>我们正在解决JupyterNotebook内核崩溃的问题。根据引用[1],我们看到个与证书验证相关的解决方案,但该方案是针对Python证书检查的,可能与内核崩溃问题无关。然而,内核崩溃可能由多种原因引起,例如依赖冲突、内存不足、损坏的安装等。考虑到用户的上个问题(未提供)以及当前需求,我们将提供些常见的解决内核崩溃的方法。常见解决方案:1.**重启内核和JupyterNotebook**:有时简单地重启内核可以解决临时问题。如果无效,尝试重启整个JupyterNotebook服务。2.**检查内核日志**:内核崩溃时通常会在控制台输出错误信息(启动JupyterNotebook的命令行窗口)。如果没有,可以查看Jupyter的日志文件(通常位于~/.jupyter/jupyter_notebook.log)或内核的日志(例如,对于Python内核,可能是~/.local/share/jupyter/kernels/<kernel_name>/kernel.log)。3.**更新软件**:确保JupyterNotebook、IPython内核以及相关库(如tornado、pyzmq等)是最新版本。使用以下命令更新:```bashpipinstall--upgradejupyteripykernel```4.**重新安装内核**:如果特定内核崩溃,尝试重新安装该内核。例如,对于Python内核,可以重新安装ipykernel:```bashpipuninstallipykernelpipinstallipykernelpython-mipykernelinstall--user```5.**检查依赖冲突**:使用`pipcheck`检查是否有不兼容的包版本。如果有冲突,根据提示进行修复。6.**清理缓存**:有时缓存可能导致问题,尝试清理Jupyter的缓存:```bashjupyternbconvert--clear-outputjupyternbextensionuninstalljupyterclean```7.**使用虚拟环境**:创建个新的虚拟环境,重新安装Jupyter和相关库,以隔离环境冲突问题。8.**检查内存使用**:如果处理的数据量过大,可能导致内核因内存不足而崩溃。尝试减少数据量或使用更高效的数据处理方式。9.**禁用扩展**:如果安装了Jupyter扩展(nbextensions),尝试禁用它们,因为某些扩展可能导致不稳定。10.**检查系统资源**:确保系统有足够的资源(内存、CPU)运行内核。11.**重新安装Jupyter**:如果以上方法都无效,考虑备份你的环境配置,然后完全卸载并重新安装JupyterNotebook。12.**使用调试工具**:如果问题持续,可以尝试使用调试工具,例如使用`%debug`魔术命令(如果内核还能短暂运行)或使用专业的调试器。注意:引用[1]中提到的证书验证问题通常与SSL连接有关,而不是内核崩溃的直接原因。但如果你在连接远程资源时遇到内核崩溃,可能与SSL验证有关,可以尝试在代码中临时禁用证书验证(仅用于测试):```pythonimportsslssl._create_default_https_context=ssl._create_unverified_context```但这不推荐作为长期解决方案,因为存在安全风险。由于内核崩溃的原因很多,建议从最简单的步骤开始排查(重启、更新等),逐步进行。§§相关问题§§1.如何查看JupyterNotebook的日志以诊断内核崩溃?2.如何为JupyterNotebook创建干净的虚拟环境?3.如何修复JupyterNotebook中因内存不足导致的问题?</think> 要解决 Jupyter Notebook Kernel 崩溃问题,以下是经过验证的解决方案,结合系统维护和配置调整: ### 、基础排查步骤 1. **重启内核** - 在 Notebook 中执行:Kernel → Restart Kernel - 终端重启服务:`jupyter notebook --generate-config` 生成新配置 2. **检查内核日志** 查看内核错误日志定位原因(路径示例): ```bash # Python 内核日志 cat ~/.local/share/jupyter/kernel/<kernel_name>/kernel.log ``` --- ### 二、核心解决方案 #### A. 依赖冲突修复 1. **更新关键包** ```bash pip install --upgrade ipykernel jupyter-client tornado pyzmq ``` 2. **重建内核** ```bash # 删除旧内核 jupyter kernelspec remove python3 # 重装新内核 python -m ipykernel install --user ``` #### B. 证书验证问题 (引用[1]的扩展方案) 当内核因 SSL 验证崩溃时: 1. **临时禁用证书检查** ```python import ssl ssl._create_default_https_context = ssl._create_unverified_context ``` 2. **永久配置** (需谨慎使用) ```bash sed -i 's/verify=platform_default/verify=disable/' /etc/python/cert-verification.cfg ``` #### C. 内存优化 1. 启用内存监控: ```python %load_ext memory_profiler %memit your_function() # 检测函数内存 ``` 2. 限制资源使用: ```bash # 启动时设置内存上限 jupyter notebook --NotebookApp.memory_limit=8192 # 单位MB ``` --- ### 三、进阶排查 | 问题类型 | 诊断命令 | 解决方案 | |-----------------------|-----------------------------|----------------------------| | 包版本冲突 | `pip list --format=freeze` | 创建新虚拟环境 | | 内核通信异常 | `jupyter troubleshoot` | 重置 `~/.jupyter` 配置目录 | | 系统资源不足 | `top -o %MEM` | 增加 swap 空间或升级硬件 | > **注意**:若崩溃发生在深度学习任务中,检查 CUDA/cuDNN 与框架版本兼容性(引用[2]的神经网络计算优化原则) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值