在Linux中,进程和内核都是通过页表PTE访问一个物理页面的,如果无法访问到正确的地址,将产生page fault(缺页异常)。

常见场景:

  1. 地址空间映射关系未建立
    1.1:malloc/mmap申请虚拟的地址空间并未分配实际物理页,首次访问触发缺页异常。
  2. 地址空间映射关系已建立
    2.1:要访问的页面已经被swapping到了磁盘,访问时触发缺页异常。
    2.2:fork子进程时,子进程共享父进程的地址空间,写是触发缺页异常(COW技术)。
    2.3:要访问的页面被KSM合并,写时触发缺页异常(COW技术)。
    2.4:兼容的ARM32体系架构模拟PTE_DIRTY PTE_YOUNG比特。
  3. 访问的地址空间不合法
    3.1:用户空间访问内核空间地址,触发缺页异常。
    3.2:内核空间访问用户空间地址,触发缺页异常。(不包括copy_to/from_user的情况)

直接放图,图中大致体现了cpu处于el0和el1等级下,从触发缺页异常到进入do_page_fault之前的流程处理。 
代码:arch/arm64/entry.S)

hy:el0、el1代表是用户空间还是内核空间发生的缺页异常。

缺页异常前的代码分析:

假设发生的是el0等级下的data abort,代码执行路径为:el0_sync->el0_da->do_mem_abort,el0_sync->el0_da。

主要是为调用do_mem_abort准备三个形参,函数声明如下。

void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)

addr:FAR存器中存放的出错地址。
esr: ESR寄存器中记录的MMU fault具体信息。
regs:异常发生时保存的寄存器信息pt_regs。

el0_da:mrs     x26, far_el1      //获取far寄存器中的异常地址enable_daifct_user_exitclear_address_tag x0, x26mov     x1, x25         //获取el0_sync中保存的esr寄存器内容mov     x2, sp          //获取sp指向的pt_regs指针bl      do_mem_abort      //带着三个形参跳入do_mem_abortb       ret_to_user/** Fall through to the Data abort case*/
el1_da:/** Data abort handling*/mrs x3, far_el1inherit_daif pstate=x23, tmp=x2clear_address_tag x0, x3mov x2, sp              // struct pt_regsbl do_mem_abortkernel_exit 1

do_mem_abort() :

do_mem_abort()可以看做错误处理的入口。linux内核为各种同步类型的MMU faults定义了相关的处理函数,并保存在名为fault_info[]的数组中。当发生错误时,do_mem_abort函数会根据ESR寄存器中的错误信息对应到fault_info[]中的处理函数。fault_info[]的定义如下:

static const struct fault_info fault_info[] = {{ do_bad,               SIGKILL, SI_KERNEL,     "ttbr address size fault"       },                                   { do_bad,               SIGKILL, SI_KERNEL,     "level 1 address size fault"    },                                   { do_bad,               SIGKILL, SI_KERNEL,     "level 2 address size fault"    },                                   { do_bad,               SIGKILL, SI_KERNEL,     "level 3 address size fault"    },                                   { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 0 translation fault"     },     el0下发生的translation错误                         { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },     el1下发生的translation错误                              { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },     el2下发生的translation错误                              { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },     el3下发生的translation错误                              { do_bad,               SIGKILL, SI_KERNEL,     "unknown 8"                     },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },                                   { do_bad,               SIGKILL, SI_KERNEL,     "unknown 12"                    },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },                                   { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      }, ......
}
/*此函数逻辑很简单,通过ESR寄存器的内容找到对应的fault_info并执行对应handler*/
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,      struct pt_regs *regs)
{const struct fault_info *inf = esr_to_fault_info(esr);  //根据esr的值查询fault_info[],其定义了所有的内存异常情况处理。if (!inf->fn(addr, esr, regs))                          //将形参addr和regs传给handler,并执行handlerreturn;                                         //若返回0,代表成功,并直接return。......
}

我们主要关注两个错误处理函数do_translation_fault()和do_page_fault(),

do_translation_fault()是处理页表pgd/pud/pmd/pte在转换过程中出现的错误,

do_page_fault()是处理访问PTE相关的错误。

do_translation_fault()最终仍调用do_page_fault(),所以接下来的代码分析围绕核心函数do_page_fault()。

do_page_fault :

do_page_fault这个函数相对复杂,其中有着大量条件判断,我梳理出下主要几个阶段并画了幅流程图,二者结合起来看会比较清晰。(就不贴冗长的代码了,读者可自行结合代码看)

主要阶段:

阶段1:判断缺页异常是否发生在内核线程或原子上下文中(中断也属于一种原子上下文),是的话执行do_kernel_fault尝试修复或报段错误。

阶段2:判断是否是内核态访问用户地址空间的情况,是的话判断是否是指定的三种情况,是则报段错误。

阶段3:进入_do_page_fault, 查找异常地址所在的vm_area_struct域,并走表(page table walk)查找address对应PGD PUD PMD,最终找到PTE。

阶段4:进入handle_pte_fault(),判断PTE为空的话,说明用户空间申请了虚拟地址后第一次访问,尚未映射物理页面。在根据页面类型分别执行do_anonymous_page或do_fault。

阶段5:PTE非空表示已经建立过映射。判断PTE的present位是否为真,非真说明页面被swapping到磁盘上,随即执行do_swap_page。

阶段6:判断PTE_PROT_NONE是否为真,若为真执行do_numa_page产生页面迁移。

阶段7:判断错误类型,若是写类型的错误,再判断PTE的读写权限。只读的话说明页面是写保护的,调用do_wp_page。

阶段8: 为了兼容ARM32,ARM32体系架构的Hardware PTE中不支持DIRTY YOUNG等bit位,所以通过软件上配合缺页异常进行模拟。

------------------------------------------------------------------其他文章的扩展-------------------------------------------------------------------

由于造成内核空间和用户空间的page fault的原因不尽相同,因此其处理流程也有所区别。

对于用户空间,需要区分多种情况,page fault的处理显得更为复杂。

首先,访问的内存地址必须是合法的,所谓「合法」,就是该地址一定是落在进程的某个VMA区间内。

假设现在一个进程的地址空间分布如下,那么address B是合法的(good area),address A就是非法的(bad area)。

地址落在进程的地址空间内,但对地址的访问权限不对(比如试图写入一个readonly的区域),也是非法的。访问了非法的地址,或者非法地访问了地址,就不是page fault那么简单了,将进一步上升到segmentation fault。

如果地址合法,权限也正确,那么还得分两种情况来讨论。

第一种情况是PTE不存在,这会出现在:

  • 对于anonymous page,用户空间使用malloc()进行内存申请时(对应底层的实现是mmap或者brk),内核并不会立刻为其分配物理内存,而只是为请求的进程的rbtree管理的vma信息中记录(添加或更改)诸如内存范围和标志之类的信息。

只有当内存被真正使用,触发page fault,才会真正分配物理页面和对应的页表项,即demand alloction,对应的函数实现是do_anonymous_page()。通过mmap映射建立的heap和stack等内存区域,在初始未使用时,也适用于这样的规则。

  • 对于page cache, 在发生内存回收后,部分text(code)段的页面会被discard,部分data段的页面会被writeback,之后再次访问这些页面,也将出现page fault。此时,需要从外部存储介质中,将页面内容调回内存,即demand paging,对应的函数实现是do_fault()

第二种情况是PTE存在,

但其中的"P(resent)"位为0,说明这是一个之前被swap out出去的anonymous page。现在PTE里存储的不是物理页面的编号PPN,而是外部swap area中slot的编号swp_entry_t,需要通过do_swap_page(),执行swap in操作将页面的内容拷贝回内存。

/* orig_pte是指发生page fault时的PTE */
if (!pte_present(vmf->orig_pte))return do_swap_page(vmf);

发生page fault时,如果目标页面驻留在外部存储器,那么需要开销较大的I/O操作,这种page fault被称为"major"的。而如果目标页面就在内存中(比如swap cache),只是缺少一个对该页面的引用而已,这种page fault不需要重新分配内存页面,代价较小,因此被称为"minor"的。

还是那个图书馆借书的例子,前台相当于内存,书库相当于磁盘,从前台直接取走就是"minor page fault",比如书到期了你还没有看完,可以在前台办完还书手续后马上再借(前提是这本书没有被其他读者预约),付出的代价就是多一次借书手续而已。

而如果你还了两个月再去借这本书,书已经被管理员上架了,你就需要自己去书架上按照类别寻找这本书,花费的时间自然较多,这就是"major page fault"。

https://zhuanlan.zhihu.com/p/66046257

----------------------------------------------------------------------------------------------------------------------------------------------------------

major fault与minor fault:

当我们进行性能分析时,某进程page faults发生的次数也是我们常常要观测的一个指标。但不论是perf、top再或者其他的工具关于page fault发生的次数的输出都会区分为major fault或minor fault两种,它们同为缺页异常,有什么不同呢?

major fault:

  1. user space address触发缺页异常时,若被访问的地址映射的物理页已经被swap到磁盘空间,需要从磁盘中将页面换入。
  2. user space address触发缺页异常时,若被访问的地址空间是被mmap映射到磁盘文件的话且page cache中还未缓存文件内容,需要通过磁盘IO将内容读入page cache。

minor fault:

  1. 当user space address触发缺页异常时,kernel可直接从buddy system中分配出内存用来满足该缺页异常即minor page fault

简单来说,major fault和minor fault的区别就是是否会触发读写磁盘的动作。

https://zhuanlan.zhihu.com/p/195580742?utm_source=wechat_timeline

作者:Yann Xu

-------------------------------------------------------------------------------------------------

中断异常的调用

linux kernel的异常向量表介绍

1、linux kernel - arch64的异常向量表-(irq,fiq,svc…)

armv8-arch64架构下,linux kernel的异常量表,再entry.S中:

/** Exception vectors.*/.align   11
ENTRY(vectors)kernel_ventry 1, sync_invalid         // Synchronous EL1tkernel_ventry    1, irq_invalid          // IRQ EL1tkernel_ventry    1, fiq_invalid          // FIQ EL1tkernel_ventry    1, error_invalid        // Error EL1tkernel_ventry  1, sync             // Synchronous EL1hkernel_ventry    1, irq              // IRQ EL1hkernel_ventry    1, fiq_invalid          // FIQ EL1hkernel_ventry    1, error_invalid        // Error EL1hkernel_ventry  0, sync             // Synchronous 64-bit EL0kernel_ventry  0, irq              // IRQ 64-bit EL0kernel_ventry  0, fiq_invalid          // FIQ 64-bit EL0kernel_ventry  0, error_invalid        // Error 64-bit EL0#ifdef CONFIG_COMPATkernel_ventry    0, sync_compat, 32      // Synchronous 32-bit EL0kernel_ventry  0, irq_compat, 32       // IRQ 32-bit EL0kernel_ventry  0, fiq_invalid_compat, 32   // FIQ 32-bit EL0kernel_ventry  0, error_invalid_compat, 32 // Error 32-bit EL0
#elsekernel_ventry  0, sync_invalid, 32     // Synchronous 32-bit EL0kernel_ventry  0, irq_invalid, 32      // IRQ 32-bit EL0kernel_ventry  0, fiq_invalid, 32      // FIQ 32-bit EL0kernel_ventry  0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

我们这里讲解如下四行:

kernel_ventry    1, irq              // IRQ EL1h
kernel_ventry   0, irq              // IRQ 64-bit EL0
kernel_ventry   1, sync             // Synchronous EL1h
kernel_ventry   0, sync             // Synchronous 64-bit EL0

kernel_ventry是宏,翻译后的函数名分别是:
el1_irq
el0_riq
el1_sync
el0_sync
对应的函数入口我们就找到了,也就是说,当触发irq异常、或svc异常时会跳转到这几个函数中。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Linux中断处理: 脉络分析

https://zhuanlan.zhihu.com/p/185851980?utm_source=wechat_session

概述:

中断在Linux系统中有着举足轻重的地位,本文在这里梳理一下中断从发生到处理再到退出的流程脉络。文章基于arm64的linux5.0的内核代码。

关于中断栈:

ARM64的linux内核在2015年之前中断没有自己独立的栈,都是借用线程的内核栈。公用一个栈的情况下,为了防止内核栈的溢出,内核栈只能强制使用16kB的,不能使用8kB的,这样就存在一个问题,当系统有大量线程的时候,16kB的内核栈对系统内存会是一个很大的浪费。 于是后来社区人员为ARM64添加了中断栈的feature,用来保存中断的上下文。

  1. 中断栈的创建:内核启动时中会去为每个cpu创建一个per cpu的中断栈:start_kernel->init_IRQ->init_irq_stacks
  2. 中断栈的使用:中断发生和退出的时候调用irq_stack_entry和irq_stack_exit来进入和退出中断栈。

中断处理流程:

各种体系架构的处理器都有自己异常处理机制,代码和体系架构息息相关,发生异常后程序会首先跳转至异常向量表。

arm64的异常向量表vectors中设置了各种异常的入口(位于arm/arm64/kernel/entry.S)。目前有效的异常入口有两个同步异常el0_sync,el1_sync和两个异步异常el0_irq,el1_irq,其他异常入口暂时都invalid。中断属于异步异常,所以本文重点关注el0_irq和el1_irq。

通过上图,我们可以看出中断的处理分为三个部分,保护现场,中断处理,恢复现场。其中el0_irq(hy:用户空间)和el1_irq(hy:内核空间)的具体实现略有不同,但处理流程大致是相同的。 接下来我们以el0_irq为例对上面三个步骤进行梳理。

  • 保存现场

异常向量表如下,其中kernel_ventry是个宏定义,会根据后面的参数将系统引入对应的异常处理函数中。本文的背景是el0下发生的中断,故进入el0_irq。(同理,cpu运行在el1等级时,系统会进入el1_irq)

ENTRY(vectors)kernel_ventry  1, sync_invalid     // Synchronous EL1t             kernel_ventry   1, irq_invalid      // IRQ EL1tkernel_ventry    1, fiq_invalid      // FIQ EL1tkernel_ventry    1, error_invalid    // Error EL1tkernel_ventry  1, sync             // Synchronous EL1h       //el1下的同步异常,例如指令执行异常、缺页中断等。kernel_ventry   1, irq              // IRQ EL1h               //el1下的异步异常,硬件中断。 1代表异常等级kernel_ventry 1, fiq_invalid      // FIQ EyL1h              kernel_ventry 1, error            // Error EL1hkernel_ventry  0, sync             // Synchronous 64-bit EL0  //el0下的同步异常,例如指令执行异常、缺页中断(跳转地址或者取地址)、系统调用等。   kernel_ventry 0, irq              // IRQ 64-bit EL0          //el0下的异步异常,硬件中断。0代表异常等级kernel_ventry 0, fiq_invalid      // FIQ 64-bit EL0kernel_ventry  0, error            // Error 64-bit EL0......
END(vectors)

kernel_entry 0中是保存现场(被中断进程的上下文)的行为,这里具体指保存硬件上下文,即寄存器数据。通过stp指令依次将寄存器压栈。

el0_sync:kernel_entry 0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!mrs  x25, esr_el1            // read the syndrome registerlsr    x24, x25, #ESR_ELx_EC_SHIFT // exception classcmp   x24, #ESR_ELx_EC_SVC64      // SVC in 64-bit stateb.eq  el0_svccmp  x24, #ESR_ELx_EC_DABT_LOW   // data abort in EL0b.eq    el0_dacmp   x24, #ESR_ELx_EC_IABT_LOW   // instruction abort in EL0b.eq el0_iacmp   x24, #ESR_ELx_EC_FP_ASIMD   // FP/ASIMD accessb.eq  el0_fpsimd_acccmp   x24, #ESR_ELx_EC_SVE        // SVE accessb.eq   el0_sve_acccmp  x24, #ESR_ELx_EC_FP_EXC64   // FP/ASIMD exceptionb.eq   el0_fpsimd_exccmp   x24, #ESR_ELx_EC_SYS64      // configurable trapccmp    x24, #ESR_ELx_EC_WFx, #4, neb.eq    el0_syscmp  x24, #ESR_ELx_EC_SP_ALIGN   // stack alignment exceptionb.eq    el0_spcmp   x24, #ESR_ELx_EC_PC_ALIGN   // pc alignment exceptionb.eq   el0_pccmp   x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL0b.eq el0_undefcmp    x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0b.ge   el0_dbgb    el0_inv
 .align  6
el0_irq:kernel_entry 0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
el0_irq_naked:gic_prio_irq_setup pmr=x20, tmp=x0enable_da_f#ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_off
#endifct_user_exit
#ifdef CONFIG_HARDEN_BRANCH_PREDICTORtbz    x22, #55, 1fbl  do_el0_irq_bp_hardening
1:
#endifirq_handler#ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_on
#endifb ret_to_user
ENDPROC(el0_irq)

小贴士:

  1. 压栈顺序是按照struct pt_regs内部成员的layout顺序来的。
  2. 仔细查看kernel_entry压栈流程,会发现进程task_struct的起始地址(即thread_info的地址)被放入了sp_el0中。没记错的话是5.0内核引入的改动
        .macro  kernel_entry, el, regsize = 64.if     \regsize == 32mov     w0, w0                          // zero upper 32 bits of x0.endifstp     x0, x1, [sp, #16 * 0]stp     x2, x3, [sp, #16 * 1]stp     x4, x5, [sp, #16 * 2]stp     x6, x7, [sp, #16 * 3]stp     x8, x9, [sp, #16 * 4]stp     x10, x11, [sp, #16 * 5]stp     x12, x13, [sp, #16 * 6]stp     x14, x15, [sp, #16 * 7]stp     x16, x17, [sp, #16 * 8]stp     x18, x19, [sp, #16 * 9]stp     x20, x21, [sp, #16 * 10]stp     x22, x23, [sp, #16 * 11]stp     x24, x25, [sp, #16 * 12]stp     x26, x27, [sp, #16 * 13]stp     x28, x29, [sp, #16 * 14].if     \el == 0 clear_gp_regsmrs     x21, sp_el0ldr_this_cpu    tsk, __entry_task, x20  // Ensure MDSCR_EL1.SS is clear,ldr     x19, [tsk, #TSK_TI_FLAGS]       // since we can unmask debugdisable_step_tsk x19, x20               // exceptions when scheduling........

其实,当异常发生时(进入vectors前)硬件还会有一坨硬件上的处理。如下图,armv8手册讲到的进入异常前的处理,只截图了一小部分,这里我们关心的是:

  1. 保存PSTATE到SPSR_ELx寄存器;
  2. 将PSTATE中的D A I F全部屏蔽;
  3. 保存PC寄存器的值到ELR_ELx寄存器。

  • 处理中断:

保存过现场后,即将跳入中断处理的lable,即irq_handler。这里面主要的三个动作,1.进入中断栈; 2.执行中断控制器的handle_arch_irq; 3.退出中断栈。

        .macro  irq_handlerldr_l   x1, handle_arch_irqmov     x0, spirq_stack_entry      //进入中断栈blr     x1       //执行handle_arch_irqirq_stack_exit       //退出中断栈.endm

前面我们已经知道了中断栈是在内核初始化时创建的,那中断控制器的handle_arch_irq又是指向哪里呢?其实是在内核启动过程中初始化中断控制器时,设置了具体的handler,这里假设我们使用中断控制器的型号是drivers/irqchip/irq-gic-v3.c,那么就是gic_init_bases->set_handle_irq将handle_arch_irq指针指向gic_handle_irq函数。代码如下:

int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{handle_arch_irq = handle_irq;return 0;
}static int __init gic_init_bases(void __iomem *dist_base,struct redist_region *rdist_regs,u32 nr_redist_regions,u64 redist_stride,struct fwnode_handle *handle)
{set_handle_irq(gic_handle_irq);
}

中断最终会通过gic_handle_irq进入handle_domain_irq (中断号小于16的是核间中断,不走这里),然后通过判断中断号去执行对应的中断处理程序。深入gic_handle_irq,其调用关系gic_handle_irq-->handle_domain_irq-->__handle_domain_irq

__handle_domain_irq即是中断处理的核心函数,其中主要包含三个关键部分:

  1. irq_enter()
  2. generic_handle_irq(irq)
  3. irq_exit()
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,bool lookup, struct pt_regs *regs)
{//将保存的pt_regs写入对应cup的irq栈,并返回老的pt,猜想可能和中断嵌套有关。struct pt_regs *old_regs = set_irq_regs(regs); unsigned int irq = hwirq;int ret = 0;irq_enter();generic_handle_irq(irq);irq_exit();set_irq_regs(old_regs);return ret;
}

其中generic_handle_irq(irq)函数通过中断号找到全局中断描述符数组irq_desc[NR_IRQS]中的一项,然后执行该irq号注册的action。

接下来,我们看下irq_enter()与irq_exit(),这两个接口代表着中断上下文的进入和退出。irq_enter()通过__irq_enter()会为preempt_count中的HARDIRQ_OFFSET域加1,表明当前已进入硬件中断上下文。同理irq_exit()退出硬件中断上下文时会对其减1,但除了对preempt_count抢占计数的操作,这里还会去判断是否有pending的软中断,有的话调用invoke_softirq()处理软中断,代码及注释如下:

void irq_exit(void)
{local_irq_disable();//对应irq_enter中的计时account_irq_exit_time(current);  //硬件中断计数减1                preempt_count_sub(HARDIRQ_OFFSET); /*如果不在(软、硬)中断上下文中,并且per cpu变量irq_stat[cpu].__softirq_pending有被置位,则处理软中断。提醒:raise_softirq()函数就是来置位变量irq_stat[cpu].__softirq_pending的。*/if (!in_interrupt() && local_softirq_pending())  //依次处理pending状态的软中断。中断下半部tasklet就是在这里被执行invoke_softirq();                                tick_irq_exit();rcu_irq_exit();
}

其中关于软中断的部分,后面会单独写一篇文章。

这里我们提一下本地中断的开启与关闭,当中断上来时,为了防止中断的嵌套,硬件会自动关闭本地中断,那本地中断什么时候打开呢?分两种情况:

  1. 退出中断上下文时若有待处理的软中断,在执行软中断前__do_softirq->local_irq_enable会打开本地中断,即软中可以被硬件中断的打断。
  2. 没有要处理的软中断,那么在中断完全退出时,会恢复被中断进程的寄存器上下文,系统状态寄存器一但被恢复,本地中断自然也就开了。(当然情况1也包括情况2)
  • 恢复现场

恢复现场,主要是通过ret_to_user函数(el1的中断不走这里)。处理相对简单,主要分三步:1.屏蔽PSTATE中的D A I F;2.检查在退出中断前有没有需要处理事情,如调度、信号处理等。3.将之前压栈的pt_regs弹出,恢复现场。

ret_to_user:disable_daif                  //D A I F分别为PSTAT中的四个异常屏蔽标志位,此处屏蔽这4中异常。ldr  x1, [tsk, #TSK_TI_FLAGS]  //获取thread_info中的flags变量的值   and  x2, x1, #_TIF_WORK_MASK   //_TIF_WORK_MASK是一些列flags的集合,其中包括NEED_RESCHED|SIGPENDING|NOTIFY_RESUME等值                                       cbnz    x2, work_pending      //判断并跳转。此处判断若有_TIF_WORK_MASK的中任何flag都去执行work_pending
finish_ret_to_user:enable_step_tsk x1, x2kernel_exit 0            //恢复pt_regs中的寄存器上下文
ENDPROC(ret_to_user)

小贴士 : D A I F分别代表什么?

------------------------------------------------------------------------------------------------

Linux内存管理之page fault处理

背景

  • Read the fucking source code! --By 鲁迅
  • A picture is worth a thousand words. --By 高尔基

说明:

  1. Kernel版本:4.14
  2. ARM64处理器,Contex-A53,双核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

上篇文章分析到malloc/mmap函数中,内核实现只是在进程的地址空间建立好了vma区域,并没有实际的虚拟地址到物理地址的映射操作。这部分就是在Page Fault异常错误处理中实现的。

Linux内核中的Page Fault异常处理很复杂,涉及的细节也很多,malloc/mmap的物理内存映射只是它的一个子集功能,下图大概涵盖了出现Page Fault的情况:

下边就开始来啃啃硬骨头吧。

2. Arm64处理

Page Fault的异常处理,依赖于体系结构,因此有必要来介绍一下Arm64的处理。
代码主要参考:arch/arm64/kernel/entry.S

Arm64在取指令或者访问数据时,需要把虚拟地址转换成物理地址,这个过程需要进行几种检查,在不满足的情况下都能造成异常:

  1. 地址的合法性,比如以39有效位地址为例,内核地址的高25位为全1,用户进程地址的高25位为全0;
  2. 地址的权限检查,这里边的权限位都位于页表条目中;

从上图中可以看到,最后都会调到do_mem_abort函数,这个函数比较简单,直接看代码,位于arch/arm64/mm/fault.c

/** Dispatch a data abort to the relevant handler.*/
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,struct pt_regs *regs)
{const struct fault_info *inf = esr_to_fault_info(esr);struct siginfo info;if (!inf->fn(addr, esr, regs))return;pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",inf->name, esr, addr);mem_abort_decode(esr);info.si_signo = inf->sig;info.si_errno = 0;info.si_code  = inf->code;info.si_addr  = (void __user *)addr;arm64_notify_die("", regs, &info, esr);
}

该函数中关键的处理:根据传进来的esr获取fault_info信息,从而去调用函数。struct fault_info用于错误状态下对应的处理方法,而内核中也定义了全局结构fault_info,存放了所有的情况。
主要的错误状态和处理函数对应如下:

static const struct fault_info fault_info[] = {{ do_bad,        SIGBUS,  0,     "ttbr address size fault" },{ do_bad,     SIGBUS,  0,     "level 1 address size fault"  },{ do_bad,     SIGBUS,  0,     "level 2 address size fault"  },{ do_bad,     SIGBUS,  0,     "level 3 address size fault"  },{ do_translation_fault,   SIGSEGV, SEGV_MAPERR,   "level 0 translation fault"   },{ do_translation_fault,   SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"   },{ do_translation_fault,   SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"   },{ do_translation_fault,   SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"   },{ do_bad,     SIGBUS,  0,     "unknown 8"           },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"   },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"   },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"   },{ do_bad,     SIGBUS,  0,     "unknown 12"          },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"    },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"    },{ do_page_fault,  SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"    },...
};

从代码中可以看出:

  • 出现0/1/2/3级页表转换错误时,会调用do_translation_fault,实际中do_translation_fault最终也会调用到do_page_fault
  • 出现1/2/3级页表访问权限的时候,会调用do_page_fault
  • 其他的错误则调用do_bad,其中未列出来的部分还包括do_sea等操作函数;

do_translation_fault

do_page_fault

do_page_fault函数为页错误异常处理的核心函数,与体系结构相关,上图中的handle_mm_fault函数为通用函数,也就是不管哪种处理器结构,最终都会调用到该函数。

3. handle_mm_fault

handle_mm_fault用于处理用户空间的页错误异常:

  • 进程在用户模式下访问用户虚拟地址,触发页错误异常;
  • 进程在内核模式下访问用户虚拟地址,触发页错误异常;
    do_page_fault函数的流程图中也能看出来,当触发异常的虚拟地址属于某个vma,并且拥有触发页错误异常的权限时,会调用到handle_mm_fault函数,而handle_mm_fault函数的主要逻辑是通过__handle_mm_fault来实现的。

流程如下图:

3.1 do_fault

do_fault函数用于处理文件页异常,包括以下三种情况:

  1. 读文件页错误;
  2. 写私有文件页错误;
  3. 写共享文件页错误;

3.2 do_anonymous_page

匿名页的缺页异常处理调用本函数,在以下情况下会触发:

  1. malloc/mmap分配了进程地址空间区域,但是没有进行映射处理,在首次访问时触发;
  2. 用户栈不够的情况下,进行栈区的扩大处理;

3.3 do_swap_page

如果访问Swap页面出错(页面不在内存中),则从Swap cacheSwap文件中读取该页面。
由于在4.14内核版本中,do_swap_page调用的很多函数都是空函数,无法进一步的了解,大体的流程如下图:

3.4 do_wp_page

do_wp_page函数用于处理写时复制(copy on write),会在以下两种情况处理:

  1. 创建子进程时,父子进程会以只读方式共享私有的匿名页和文件页,当试图写的时候,触发页错误异常,从而复制物理页,并创建映射;
  2. 进程创建私有文件映射,读访问后触发异常,将文件页读入到page cache中,并以只读模式创建映射,之后发生写访问后,触发COW

关键的复制工作是由wp_page_copy完成的:

https://www.cnblogs.com/LoyenWang/p/12116570.html

------------------------------------------------------------------------------------------------

Oops打印的调用

Linux内存管理 (23)一个内存Oops解析

专题:Linux内存管理专题

关键词:DataAbort、fsr、pte、backtrace、stack。

 

在内存相关实际应用中,内存异常访问是一种常见的问题。

本文结合异常T32栈回溯、Oops打印以及代码,分析打印log,加深对Oops的理解,有助于快速定位问题解决问题。

1. 不同类型异常处理

当内存访问异常时,触发__dabt_svc异常向量处理,进入do_DataAbort进行处理。

从_dabt_svc到do_DataAbort流程,可以参考do_DataAbort。

从do_DataAbort开始,fsr_fs()根据fsr找到fsr_info中的处理函数。

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))------------------这里根据fsr从fsr_info中找打对应的操作函数。return;
...
}static inline int fsr_fs(unsigned int fsr)
{return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
}

fsr_info列出了全部的错误类型,主要包括四种类型:section translation fault、page translation fault、section permission fault、page permission fault。

2. Section Translation Fault

2.1 Section Translation Fault栈信息

下面是一个Section Translation Fault错误实例的T32栈:

fsr=0x805,即100000000101,所以经过fsr_fs()处理返回值为101。

所以inf->fn即为do_translation_fault。

static struct fsr_info fsr_info[] = {
...{ 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_DataAbort根据异常地址、fsr、pt_regs,来判断异常发生在内核还是用户空间,当前状态是用户模式还是非用户模式,fsr用于确定错误处理函数。

__dabt_svc->do_DataAbort->do_translation_fault->do_bad_area->__do_kernel_fault->die

2.2 入口函数do_translation_fault

Section Translation Fault类型的错误处理函数是do_translation_fault。

static int __kprobes
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)-------------------------------------TASK_SIZE是用户空间地址的顶部,所以do_page_fault是用户空间处理函数。return do_page_fault(addr, fsr, regs);if (user_mode(regs))--------------------------------------至此的地址都是内核空间,如果regs显式为用户空间。说明两者冲突,进入bad_area。goto bad_area;index = pgd_index(addr);pgd = cpu_get_pgd() + index;pgd_k = init_mm.pgd + index;if (pgd_none(*pgd_k))-------------------------------------pgd_none()返回0,所以不会进入bad_area。goto bad_area;if (!pgd_present(*pgd))set_pgd(pgd, *pgd_k);pud = pud_offset(pgd, addr);pud_k = pud_offset(pgd_k, addr);if (pud_none(*pud_k))-------------------------------------pud_none()同样返回0,不会进入bad_area。goto bad_area;if (!pud_present(*pud))set_pud(pud, *pud_k);pmd = pmd_offset(pud, addr);pmd_k = pmd_offset(pud_k, addr);#ifdef CONFIG_ARM_LPAE/** Only one hardware entry per PMD with LPAE.*/index = 0;
#else/** On ARM one Linux PGD entry contains two hardware entries (see page* tables layout in pgtable.h). We normally guarantee that we always* fill both L1 entries. But create_mapping() doesn't follow the rule.* It can create inidividual L1 entries, so here we have to call* pmd_none() check for the entry really corresponded to address, not* for the first of pair.*/index = (addr >> SECTION_SHIFT) & 1;
#endifif (pmd_none(pmd_k[index]))------------------------------如果此时pmd_k[index]为0,则为异常进入bad_area。goto bad_area;copy_pmd(pmd, pmd_k);return 0;bad_area:do_bad_area(addr, fsr, regs);return 0;
}

如果确实是异常,进入do_bad_area()进行处理。分为user_mode和非user_mode两种模式分别进行处理。

user_mode处理较简单,发送SIGSEGV信号即可。

void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{struct task_struct *tsk = current;struct mm_struct *mm = tsk->active_mm;/** If we are in kernel mode at this point, we* have no context to handle this fault with.*/if (user_mode(regs))__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);else__do_kernel_fault(mm, addr, fsr, regs);
}

其它模式交给__do_kernel_fault进行处理,调用流程和打印结果如下。

2.3 内核空间Section Translation Fault处理

__do_kernel_fault的主要工作是打印pte、pt_regs、栈等信息,帮助发现问题根源,核心函数是__die。

__do_kernel_fault->show_pte----------------------------------------------------1->die->__die->print_modules-------------------------------------------2->__show_regs---------------------------------------------3->dump_mem------------------------------------------------4->dump_backtrace------------------------------------------5->dump_instr----------------------------------------------6->panic-----------------------------------------------------7

下面是打印结果,结合代码和打印信息进行分析如下:

<1>[153780.197326] Unable to handle kernel paging request at virtual address d8660000------0. 错误概述

<1>[153780.204406] pgd = c287c000---------------------------------------------------------------------------1. show_pte,当前pgd地址0xc287c000

<1>[153780.207183] [d8660000] *pgd=00000000-----------------------------------------------------------异常地址0xd8660000和其对应的pgd表项内容0x00000000,问题就出在这里。

<0>[153780.210845] Internal error: Oops: 805 [#1] ARM--------------------------------- ----------------0. die

<4>[153780.215362] Modules linked in:------------------------------------------------------------------------2. print_modules

<4>[153780.218475] CPU: 0    Not tainted  (3.4.110 #2)---------------------------------------------------3. __show_regs

<4>[153780.223083] PC is at __mutex_lock_slowpath+0x34/0xb8

<4>[153780.228118] LR is at dpm_prepare+0x58/0x1d0

<4>[153780.232360] pc : [<c04ad5bc>]    lr : [<c01a27a8>]    psr: 80000013

<4>[153780.232391] sp : c2d01e58  ip : 00000000  fp : c2cc6800

<4>[153780.243988] r10: c0690bfc  r9 : c0690c04  r8 : c3682c68

<4>[153780.249298] r7 : c3682c64  r6 : c2c2c000  r5 : c3682c30  r4 : c3682c64

<4>[153780.255889] r3 : d8660000  r2 : c2d01e5c  r1 : 00000000  r0 : c3682c64

<4>[153780.262512] Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel---Nzcv大写表示置位;IRQ/FIQ都打开;处于SVC_32模式;架构是ARM;处于内核中。

<4>[153780.269866] Control: 10c5383d  Table: 2287c059  DAC: 00000015

<4>[153780.275695] -----------------------------------------------------------------------------------------------------下面大段show_extra_register_data打印pt_regs前后128字节十六进制值

<4>[153780.275695] PC: 0xc04ad53c:

<4>[153780.280120] d53c  1afffffb e3510001 0afffff6 eaffffb9 e92d4008 e5b03004 e1530000 0a000001

<4>[153780.288360] d55c  e5930008 ebee2a57 e8bd8008 e3a03001 e1901f9f e180cf93 e33c0000 1afffffb

<4>[153780.296630] d57c  e3510000 012fff1e eafffff0 e92d41f0 e24dd010 e1a0200d e1a04000 e3c23d7f

<4>[153780.304870] d59c  e3c3303f e593600c e5903008 e28d2004 e2808004 e5802008 e58d8004 e58d3008

<4>[153780.313110] d5bc  e5832000 e58d600c e3e05000 e1903f9f e1802f95 e3320000 1afffffb e3530001

<4>[153780.321350] d5dc  0a00000e e1903f9f e1802f95 e3320000 1afffffb e3530001 0a000008 e3a07002

<4>[153780.329620] d5fc  e5867000 eb000433 e1943f9f e1842f95 e3320000 1afffffb e3530001 1afffff7

<4>[153780.337860] d61c  e99d000c e5823004 e5832000 e5943004 e1580003 03a03000 05843000 e28dd010

<4>[153780.346099]

<4>[153780.346130] LR: 0xc01a2728:

<4>[153780.350524] 2728  e5812090 e587308c eaffffd2 c0690bd8 c06e4e9c c067f0e8 c0690bf4 c01a1d0c

<4>[153780.358795] 2748  c059e114 c06e4ea0 e92d4ff8 e59f81b8 e1a00008 e288a024 eb0c2bb6 e288902c

<4>[153780.367034] 2768  ea000003 e37b000b 1a00005e e1a00005 ebffda19 e5984024 e154000a 0a000054

<4>[153780.375274] 2788  e2445054 e2447020 e1a00005 ebffda09 e59f0174 eb0c2b71 e1a00007 eb0c2ba5

<4>[153780.383544] 27a8  e5543004 e2131001 0a000002 e5941014 e2911000 13a01001 e59420a4 e5d43018

<4>[153780.391784] 27c8  e3520000 e7c03011 e5c43018 0a000038 e5926000 e3560000 0a000027 e1a00005

<4>[153780.400024] 27e8  e12fff36 e1a0b000 e1a01006 e1a0200b e59f0118 ebfff983 e1a00007 eb0c2b57

<4>[153780.408264] 2808  e59f0104 eb0c2b8b e35b0000 1affffd4 e5943000 e5542004 e1540003 e3822004

<4>[153780.416534]

<4>[153780.416534] SP: 0xc2d01dd8:

<4>[153780.420959] 1dd8  c06be940 c067ccb8 0000000a c2d01df8 c00190b0 c00193f4 60000013 0000000a

<4>[153780.429199] 1df8  c04ad5bc 80000013 ffffffff c2d01e44 c3682c68 c0008cd8 c3682c64 00000000

<4>[153780.437438] 1e18  c2d01e5c d8660000 c3682c64 c3682c30 c2c2c000 c3682c64 c3682c68 c0690c04

<4>[153780.445709] 1e38  c0690bfc c2cc6800 00000000 c2d01e58 c01a27a8 c04ad5bc 80000013 ffffffff

<4>[153780.453948] 1e58  00000010 c3682c68 d8660000 c07b2f78 c3682c84 c3682c30 00000000 c3682c64

<4>[153780.462188] 1e78  c0690bd8 c01a27a8 00000000 00000002 00000000 00000003 000d6508 00000000

<4>[153780.470458] 1e98  c06d01c8 c2d00000 c2cc6800 c01a292c c06d0748 c004092c 00000003 c04b4340

<4>[153780.478698] 1eb8  00000000 000d6508 00000000 c0040d94 c06d0834 00000000 c06e6f4c c06e8f68

<4>[153780.486938]

<4>[153780.486938] FP: 0xc2cc6780:

<4>[153780.491363] 6780  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.499633] 67a0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.507873] 67c0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.516113] 67e0  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.524353] 6800  c06d01c8 c2cba600 00000000 ffffffff 00000001 00000000 00000000 00000000

<4>[153780.532623] 6820  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.540863] 6840  00000000 00000000 00000000 00000001 00000001 c2cc6854 c2cc6854 c2cc6800

<4>[153780.549102] 6860  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

<4>[153780.557373]

<4>[153780.557373] R0: 0xc3682be4:

<4>[153780.561798] 2be4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.570037] 2c04  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 00000000 00000000

<4>[153780.578277] 2c24  00000000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.586547] 2c44  d8660000 d8660000 d8660000 d8660000 d8660001 d8660000 d8660000 d8660000

<4>[153780.594787] 2c64  ffffffff d8660000 c2d01e5c d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.603027] 2c84  d8660000 c0690bfc d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.611297] 2ca4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.619537] 2cc4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.627777]

<4>[153780.627777] R2: 0xc2d01ddc:

<4>[153780.632202] 1ddc  c067ccb8 0000000a c2d01df8 c00190b0 c00193f4 60000013 0000000a c04ad5bc

<4>[153780.640472] 1dfc  80000013 ffffffff c2d01e44 c3682c68 c0008cd8 c3682c64 00000000 c2d01e5c

<4>[153780.648712] 1e1c  d8660000 c3682c64 c3682c30 c2c2c000 c3682c64 c3682c68 c0690c04 c0690bfc

<4>[153780.656951] 1e3c  c2cc6800 00000000 c2d01e58 c01a27a8 c04ad5bc 80000013 ffffffff 00000010

<4>[153780.665191] 1e5c  c3682c68 d8660000 c07b2f78 c3682c84 c3682c30 00000000 c3682c64 c0690bd8

<4>[153780.673461] 1e7c  c01a27a8 00000000 00000002 00000000 00000003 000d6508 00000000 c06d01c8

<4>[153780.681701] 1e9c  c2d00000 c2cc6800 c01a292c c06d0748 c004092c 00000003 c04b4340 00000000

<4>[153780.689941] 1ebc  000d6508 00000000 c0040d94 c06d0834 00000000 c06e6f4c c06e8f68 c06e6f4c

<4>[153780.698211]

<4>[153780.698211] R3: 0xd865ff80:

<4>[153780.702636] ff80  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.710876] ffa0  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.719116] ffc0  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.727386] ffe0  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.735626] 0000  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.743865] 0020  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.752136] 0040  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.760375] 0060  ******** ******** ******** ******** ******** ******** ******** ********

<4>[153780.768615]

<4>[153780.768615] R4: 0xc3682be4:

<4>[153780.773040] 2be4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.781280] 2c04  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 00000000 00000000

<4>[153780.789550] 2c24  00000000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.797790] 2c44  d8660000 d8660000 d8660000 d8660000 d8660001 d8660000 d8660000 d8660000

<4>[153780.806030] 2c64  ffffffff d8660000 c2d01e5c d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.814300] 2c84  d8660000 c0690bfc d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.822540] 2ca4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.830780] 2cc4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.839050]

<4>[153780.839050] R5: 0xc3682bb0:

<4>[153780.843475] 2bb0  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.851715] 2bd0  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.859954] 2bf0  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.868225] 2c10  d8660000 d8660000 d8660000 00000000 00000000 00000000 d8660000 d8660000

<4>[153780.876464] 2c30  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.884704] 2c50  d8660000 d8660001 d8660000 d8660000 d8660000 ffffffff d8660000 c2d01e5c

<4>[153780.892944] 2c70  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 c0690bfc d8660000

<4>[153780.901214] 2c90  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.909454]

<4>[153780.909454] R6: 0xc2c2bf80:

<4>[153780.913879] bf80  f0000188 000181a4 00000000 00000000 00000000 00000000 c04bb340 c06aafbc

<4>[153780.922119] bfa0  c2c2bf00 c2c2b380 00000000 c3708000 00000000 00000000 00000001 00000000

<4>[153780.930389] bfc0  00000000 c2c2bfc4 c2c2bfc4 66756208 666e695f 72a5006f 7ae75aad 5aa55aa5

<4>[153780.938629] bfe0  5aa55ac5 52a75aa0 4a255aa5 5aa45aa5 5aa55aa5 1aa54aa5 08a55ae5 42a552ad

<4>[153780.946868] c000  00000000 c2d00000 00000002 04208040 00000000 00000001 00000064 00000064

<4>[153780.955139] c020  00000064 00000000 c04b4078 00000000 00015ab9 0000bd04 00000001 00000000

<4>[153780.963378] c040  00000000 c2c2c044 c2c2c044 00000001 be05bcc5 00008bdc 02e98615 00000000

<4>[153780.971618] c060  adc99ea7 00000105 01f7d876 00000000 00000000 00000000 00000000 00000000

<4>[153780.979888]

<4>[153780.979888] R7: 0xc3682be4:

<4>[153780.984313] 2be4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153780.992553] 2c04  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 00000000 00000000

<4>[153781.000793] 2c24  00000000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.009063] 2c44  d8660000 d8660000 d8660000 d8660000 d8660001 d8660000 d8660000 d8660000

<4>[153781.017303] 2c64  ffffffff d8660000 c2d01e5c d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.025543] 2c84  d8660000 c0690bfc d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.033782] 2ca4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.042053] 2cc4  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.050292]

<4>[153781.050292] R8: 0xc3682be8:

<4>[153781.054718] 2be8  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.062957] 2c08  d8660000 d8660000 d8660000 d8660000 d8660000 00000000 00000000 00000000

<4>[153781.071228] 2c28  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.079467] 2c48  d8660000 d8660000 d8660000 d8660001 d8660000 d8660000 d8660000 ffffffff

<4>[153781.087707] 2c68  d8660000 c2d01e5c d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.095977] 2c88  c0690bfc d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.104217] 2ca8  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.112457] 2cc8  d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000 d8660000

<4>[153781.120727]

<4>[153781.120727] R9: 0xc0690b84:

<4>[153781.125152] 0b84  c019fcac 00000000 c05b339c 00000124 c019fc00 00000000 c05b33b0 00000124

<4>[153781.133392] 0ba4  c019fb54 00000000 c05b33d0 000001a4 c019f8b0 c019fb00 00000000 c0690bc0

<4>[153781.141632] 0bc4  c0690bc0 00000000 00000001 c0690bd0 c0690bd0 00000001 c0690bdc c0690bdc

<4>[153781.149902] 0be4  c0690be4 c0690be4 c0690bec c0690bec c0690bf4 c0690bf4 c3682c84 c283fb04

<4>[153781.158142] 0c04  c06907c4 c36b4454 c2ae03d4 c3411e44 c0690c14 c0690c14 00000000 c0690c20

<4>[153781.166381] 0c24  c0690c20 00000005 00000100 c0690c30 c0690c30 c01a5354 c05b31c8 00000000

<4>[153781.174621] 0c44  c0690cb8 00000000 00000000 c3413ac0 c01a5b3c 00000000 00000000 c01a5ae4

<4>[153781.182891] 0c64  00000000 00000000 00000000 00000000 00000000 c348bf00 0000003c c05b5d00

<4>[153781.191131]

<4>[153781.191131] R10: 0xc0690b7c:

<4>[153781.195648] 0b7c  c05b3388 00000124 c019fcac 00000000 c05b339c 00000124 c019fc00 00000000

<4>[153781.203887] 0b9c  c05b33b0 00000124 c019fb54 00000000 c05b33d0 000001a4 c019f8b0 c019fb00

<4>[153781.212158] 0bbc  00000000 c0690bc0 c0690bc0 00000000 00000001 c0690bd0 c0690bd0 00000001

<4>[153781.220397] 0bdc  c0690bdc c0690bdc c0690be4 c0690be4 c0690bec c0690bec c0690bf4 c0690bf4

<4>[153781.228637] 0bfc  c3682c84 c283fb04 c06907c4 c36b4454 c2ae03d4 c3411e44 c0690c14 c0690c14

<4>[153781.236877] 0c1c  00000000 c0690c20 c0690c20 00000005 00000100 c0690c30 c0690c30 c01a5354

<4>[153781.245147] 0c3c  c05b31c8 00000000 c0690cb8 00000000 00000000 c3413ac0 c01a5b3c 00000000

<4>[153781.253387] 0c5c  00000000 c01a5ae4 00000000 00000000 00000000 00000000 00000000 c348bf00

<0>[153781.261627] Process suspend (pid: 755, stack limit = 0xc2d00268)--------------线程名是suspend,pid是755,栈的最底部是0xc2d00268,也即sp的指针不能小于此值。

<0>[153781.267730] Stack: (0xc2d01e58 to 0xc2d02000)----------------------------------------------------------------------------------4. dump_mem,有前面可知栈的底部,8K对齐则是栈的顶部。

<0>[153781.272155] 1e40:                                                       00000010 c3682c68--------------------------------------------------从栈的底部开始dump,直到栈的顶部。

<0>[153781.280395] 1e60: d8660000 c07b2f78 c3682c84 c3682c30 00000000 c3682c64 c0690bd8 c01a27a8

<0>[153781.288635] 1e80: 00000000 00000002 00000000 00000003 000d6508 00000000 c06d01c8 c2d00000

<0>[153781.296905] 1ea0: c2cc6800 c01a292c c06d0748 c004092c 00000003 c04b4340 00000000 000d6508

<0>[153781.305145] 1ec0: 00000000 c0040d94 c06d0834 00000000 c06e6f4c c06e8f68 c06e6f4c c0690c1c

<0>[153781.313385] 1ee0: 000d6508 c06e8f68 c06e6f4c c0690c1c 000d6508 c01a5390 c067eaf0 00000000

<0>[153781.321655] 1f00: c2d01f9c c04ae1e0 00000000 c2c2c000 c067eaf0 386f67b6 1432efb3 00000000

<0>[153781.329895] 1f20: c2d01f7c c003a910 895c6980 00000000 7bb36301 00000000 00000000 895c6980

<0>[153781.338134] 1f40: 00000000 c2c2c000 c0690c2c c2cc79c0 00000000 c2cc6800 00000000 c002b7cc

<0>[153781.346405] 1f60: 00000064 c2c2c000 c067eaf0 c2cc79c0 c2cc79d4 c2d00000 c2cc79d4 00000001

<0>[153781.354644] 1f80: c06d01c8 00000002 c2cc6800 c002ba10 c06d01c4 c2cc79c0 c2cba600 c002bb28

<0>[153781.362884] 1fa0: c002ba20 c06d01c4 00000000 c341fefc c2cba600 c002ba20 00000013 00000000

<0>[153781.371124] 1fc0: 00000000 00000000 00000000 c0030144 00000000 00000000 c2cba600 00000000

<0>[153781.379394] 1fe0: c2d01fe0 c2d01fe0 c341fefc c00300c0 c0009a20 c0009a20 00000000 00000000

<4>[153781.387664] [<c04ad5bc>] (__mutex_lock_slowpath+0x34/0xb8) from [<c01a27a8>] (dpm_prepare+0x58/0x1d0)----5. dump_backtrace

<4>[153781.396942] [<c01a27a8>] (dpm_prepare+0x58/0x1d0) from [<c01a292c>] (dpm_suspend_start+0xc/0x60)

<4>[153781.405792] [<c01a292c>] (dpm_suspend_start+0xc/0x60) from [<c004092c>] (suspend_devices_and_enter+0x58/0x258)

<4>[153781.415863] [<c004092c>] (suspend_devices_and_enter+0x58/0x258) from [<c0040d94>] (pm_suspend+0x268/0x2b0)

<4>[153781.425598] [<c0040d94>] (pm_suspend+0x268/0x2b0) from [<c01a5390>] (suspend+0x3c/0xfc)--------------------dump_backtrace_entry负责打印每条信息,从右到左调用关系

<4>[153781.433654] [<c01a5390>] (suspend+0x3c/0xfc) from [<c002b7cc>] (process_one_work+0x138/0x358)

<4>[153781.442260] [<c002b7cc>] (process_one_work+0x138/0x358) from [<c002ba10>] (process_scheduled_works+0x24/0x34)

<4>[153781.452239] [<c002ba10>] (process_scheduled_works+0x24/0x34) from [<c002bb28>] (rescuer_thread+0x108/0x19c)

<4>[153781.462066] [<c002bb28>] (rescuer_thread+0x108/0x19c) from [<c0030144>] (kthread+0x84/0x90)

<4>[153781.470489] [<c0030144>] (kthread+0x84/0x90) from [<c0009a20>] (kernel_thread_exit+0x0/0x8)

<0>[153781.478881] Code: e2808004 e5802008 e58d8004 e58d3008 (e5832000) --------------------------------------------------6. dump_instr

<4>[153781.485168] ---[ end trace 352bcf684b277880 ]---------------------------------------------------------------------------------------oops_exit打印信息

<0>[153781.489746] Kernel panic - not syncing: Fatal exception---------------------------------------------------------------------------7. panic,

__do_kernel_fault主要打印pte页表内容,然后将工作交给die进行处理。

show_pte对pgd、pud、pmd、pte各项进行了检查。

/** Oops.  The kernel tried to access some page that wasn't present.*/
static void
__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,struct pt_regs *regs)
{/** Are we prepared to handle this kernel fault?*/if (fixup_exception(regs))return;/** No handler, we'll have to terminate things with extreme prejudice.*/bust_spinlocks(1);pr_alert("Unable to handle kernel %s at virtual address %08lx\n",(addr < PAGE_SIZE) ? "NULL pointer dereference" :"paging request", addr);-------------------------------------------用户空间地址显示"NULL pointer dereference",内核空间地址显示"paging request"。show_pte(mm, addr);-----------------------------------------------------打印页表项内容die("Oops", regs, fsr);-------------------------------------------------Oops die打印,包括modules、pt_regs、stack、backtrace、mem等信息。bust_spinlocks(0);do_exit(SIGKILL);
}/** This is useful to dump out the page tables associated with* 'addr' in mm 'mm'.*/
void show_pte(struct mm_struct *mm, unsigned long addr)
{pgd_t *pgd;if (!mm)----------------------------------------------------------------如果当前mm为NULL,表示当前进程为内核线程,mm对应init_mm。mm = &init_mm;pr_alert("pgd = %p\n", mm->pgd);----------------------------------------打印pgd地址pgd = pgd_offset(mm, addr);pr_alert("[%08lx] *pgd=%08llx",addr, (long long)pgd_val(*pgd));--------------------------------打印问题地址和其地址对应的pgd值,注意这里的pgd已经根据地址进行了偏移。do {pud_t *pud;pmd_t *pmd;pte_t *pte;if (pgd_none(*pgd))break;if (pgd_bad(*pgd)) {pr_cont("(bad)");break;}pud = pud_offset(pgd, addr);if (PTRS_PER_PUD != 1)pr_cont(", *pud=%08llx", (long long)pud_val(*pud));if (pud_none(*pud))break;if (pud_bad(*pud)) {pr_cont("(bad)");break;}
---------------------------------------------------------------------对于Linux二级页表映射,上面的判断都可以跳过。pmd = pmd_offset(pud, addr);if (PTRS_PER_PMD != 1)pr_cont(", *pmd=%08llx", (long long)pmd_val(*pmd));if (pmd_none(*pmd))------------------------------------------对于Linux二级页表映射,pmd=pud=pgd,所以*pmd=*pgd。因为实例中*pgd=0x0000,所以此处break。break;if (pmd_bad(*pmd)) {-----------------------------------------pmd值第2bit必须清零,#define pmd_bad(pmd) (pmd_val(pmd) & 2)pr_cont("(bad)");break;}/* We must not map this if we have highmem enabled */if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT)))break;pte = pte_offset_map(pmd, addr);pr_cont(", *pte=%08llx", (long long)pte_val(*pte));
#ifndef CONFIG_ARM_LPAEpr_cont(", *ppte=%08llx",(long long)pte_val(pte[PTE_HWTABLE_PTRS]));
#endifpte_unmap(pte);} while(0);pr_cont("\n");
}

die将主要工交给__die()打印信息,然后调用panic()执行halt或重启之类的操作。

void die(const char *str, struct pt_regs *regs, int err)
{struct thread_info *thread = current_thread_info();int ret;enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE;oops_enter();raw_spin_lock_irq(&die_lock);console_verbose();bust_spinlocks(1);if (!user_mode(regs))bug_type = report_bug(regs->ARM_pc, regs);if (bug_type != BUG_TRAP_TYPE_NONE)str = "Oops - BUG";ret = __die(str, err, thread, regs);if (regs && kexec_should_crash(thread->task))crash_kexec(regs);---------------------------------------加载并运行调试内核bust_spinlocks(0);add_taint(TAINT_DIE);raw_spin_unlock_irq(&die_lock);oops_exit();------------------------------------------------打印"...end trace...",表示Oops结束,进入panic阶段。if (in_interrupt())panic("Fatal exception in interrupt");if (panic_on_oops)panic("Fatal exception");if (ret != NOTIFY_STOP)do_exit(SIGSEGV);
}

__die输出module信息、ARM寄存器、dump栈、回溯栈等信息。

__show_regs将pt_regs的寄存器打印,并将前后128字节dump出来。

dump_mem将stack二进制dump出来。

dump_backtrace回溯栈并打印出对应符号表信息。

static int __die(const char *str, int err, struct pt_regs *regs)
{struct task_struct *tsk = current;static int die_counter;int ret;pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP S_ISA "\n",str, err, ++die_counter);/* trap and error numbers are mostly meaningless on ARM */ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV);if (ret == NOTIFY_STOP)return 1;print_modules();__show_regs(regs);pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk));-------end_of_stack是栈的底部。if (!user_mode(regs) || in_interrupt()) {dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp,THREAD_SIZE + (unsigned long)task_stack_page(tsk));---------------dump的范围是当前sp指针到栈的顶部,顶部可以通过task->stack获取,大小固定。sp指向底部。dump_backtrace(regs, tsk);dump_instr(KERN_EMERG, regs);}return 0;
}void print_modules(void)
{struct module *mod;char buf[8];printk(KERN_DEFAULT "Modules linked in:");/* Most callers should already have preempt disabled, but make sure */preempt_disable();list_for_each_entry_rcu(mod, &modules, list) {if (mod->state == MODULE_STATE_UNFORMED)continue;pr_cont(" %s%s", mod->name, module_flags(mod, buf));}preempt_enable();if (last_unloaded_module[0])pr_cont(" [last unloaded: %s]", last_unloaded_module);pr_cont("\n");
}void __show_regs(struct pt_regs *regs)
{unsigned long flags;char buf[64];show_regs_print_info(KERN_DEFAULT);print_symbol("PC is at %s\n", instruction_pointer(regs));----------PC指针指向的函数以及偏移print_symbol("LR is at %s\n", regs->ARM_lr);-----------------------LR指向的函数以及偏移printk("pc : [<%08lx>]    lr : [<%08lx>]    psr: %08lx\n"----------打印pt_regs各寄存器值。"sp : %08lx  ip : %08lx  fp : %08lx\n",regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr,regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);printk("r10: %08lx  r9 : %08lx  r8 : %08lx\n",regs->ARM_r10, regs->ARM_r9,regs->ARM_r8);printk("r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",regs->ARM_r7, regs->ARM_r6,regs->ARM_r5, regs->ARM_r4);printk("r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",regs->ARM_r3, regs->ARM_r2,regs->ARM_r1, regs->ARM_r0);flags = regs->ARM_cpsr;------------------------------------------cpsr的NZCV标志位buf[0] = flags & PSR_N_BIT ? 'N' : 'n';buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';buf[2] = flags & PSR_C_BIT ? 'C' : 'c';buf[3] = flags & PSR_V_BIT ? 'V' : 'v';buf[4] = '\0';#ifndef CONFIG_CPU_V7Mprintk("Flags: %s  IRQs o%s  FIQs o%s  Mode %s  ISA %s  Segment %s\n",buf, interrupts_enabled(regs) ? "n" : "ff",fast_interrupts_enabled(regs) ? "n" : "ff",processor_modes[processor_mode(regs)],isa_modes[isa_mode(regs)],get_fs() == get_ds() ? "kernel" : "user");
#elseprintk("xPSR: %08lx\n", regs->ARM_cpsr);
#endif#ifdef CONFIG_CPU_CP15{unsigned int ctrl;buf[0] = '\0';
#ifdef CONFIG_CPU_CP15_MMU{unsigned int transbase, dac;asm("mrc p15, 0, %0, c2, c0\n\t""mrc p15, 0, %1, c3, c0\n": "=r" (transbase), "=r" (dac));snprintf(buf, sizeof(buf), "  Table: %08x  DAC: %08x",transbase, dac);}
#endifasm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl));printk("Control: %08x%s\n", ctrl, buf);-----------------------输出MMU相关信息}
#endif

show_extra_register_data(regs, 128);------------------------------打印pt_regs寄存器地址的前后128字节十六进制

}/** Dump out the contents of some memory nicely...*/
static void dump_mem(const char *lvl, const char *str, unsigned long bottom,unsigned long top)
{unsigned long first;mm_segment_t fs;int i;/** We need to switch to kernel mode so that we can use __get_user* to safely read from kernel space.  Note that we now dump the* code first, just in case the backtrace kills us.*/fs = get_fs();set_fs(KERNEL_DS);printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top);for (first = bottom & ~31; first < top; first += 32) {unsigned long p;char str[sizeof(" 12345678") * 8 + 1];memset(str, ' ', sizeof(str));str[sizeof(str) - 1] = '\0';for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {if (p >= bottom && p < top) {unsigned long val;if (__get_user(val, (unsigned long *)p) == 0)sprintf(str + i * 9, " %08lx", val);elsesprintf(str + i * 9, " ????????");}}printk("%s%04lx:%s\n", lvl, first & 0xffff, str);}set_fs(fs);
}static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{unwind_backtrace(regs, tsk);
}

void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
  {
      #ifdef CONFIG_KALLSYMS
          printk("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from);
      #else
          printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
      #endif

 

if (in_exception_text(where))
          dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
  }

static void dump_instr(const char *lvl, struct pt_regs *regs)
{unsigned long addr = instruction_pointer(regs);const int thumb = thumb_mode(regs);const int width = thumb ? 4 : 8;mm_segment_t fs;char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;int i;/** We need to switch to kernel mode so that we can use __get_user* to safely read from kernel space.  Note that we now dump the* code first, just in case the backtrace kills us.*/fs = get_fs();set_fs(KERNEL_DS);for (i = -4; i < 1 + !!thumb; i++) {unsigned int val, bad;if (thumb)bad = __get_user(val, &((u16 *)addr)[i]);elsebad = __get_user(val, &((u32 *)addr)[i]);if (!bad)p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",width, val);else {p += sprintf(p, "bad PC value");break;}}printk("%sCode: %s\n", lvl, str);set_fs(fs);
}

panic()首先打印一条信息"Kernel panic...",然后执行一些清理操作。

最后执行panic_blink提示,执行重启操作。

void panic(const char *fmt, ...)
{static DEFINE_SPINLOCK(panic_lock);static char buf[1024];va_list args;long i, i_next = 0;int state = 0;/** Disable local interrupts. This will prevent panic_smp_self_stop* from deadlocking the first cpu that invokes the panic, since* there is nothing to prevent an interrupt handler (that runs* after the panic_lock is acquired) from invoking panic again.*/local_irq_disable();/** It's possible to come here directly from a panic-assertion and* not have preempt disabled. Some functions called from here want* preempt to be disabled. No point enabling it later though...** Only one CPU is allowed to execute the panic code from here. For* multiple parallel invocations of panic, all other CPUs either* stop themself or will wait until they are stopped by the 1st CPU* with smp_send_stop().*/if (!spin_trylock(&panic_lock))panic_smp_self_stop();console_verbose();bust_spinlocks(1);va_start(args, fmt);vsnprintf(buf, sizeof(buf), fmt, args);va_end(args);printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);----------------------panic()的最后最后一条消息
#ifdef CONFIG_DEBUG_BUGVERBOSE/** Avoid nested stack-dumping if a panic occurs during oops processing*/if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)dump_stack();
#endif/** If we have crashed and we have a crash kernel loaded let it handle* everything else.* Do we want to call this before we try to display a message?*/crash_kexec(NULL);-------------------------------------------------------------定义CONFIG_KEXEC的情况下,加载调试内核镜像,然后执行。/** Note smp_send_stop is the usual smp shutdown function, which* unfortunately means it may not be hardened to work in a panic* situation.*/smp_send_stop();---------------------------------------------------------------关闭SMP其它核。kmsg_dump(KMSG_DUMP_PANIC);----------------------------------------------------执行dump_list上的dumper。atomic_notifier_call_chain(&panic_notifier_list, 0, buf);----------------------执行panic_notifier_list链表上的notifier。bust_spinlocks(0);if (!panic_blink)panic_blink = no_blink;if (panic_timeout > 0) {-------------------------------------------------------如果panic_timeout不为0情况下,会在若干秒过后重启。/** Delay timeout seconds before rebooting the machine.* We can't use the "normal" timers since we just panicked.*/printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {touch_nmi_watchdog();if (i >= i_next) {i += panic_blink(state ^= 1);i_next = i + 3600 / PANIC_BLINK_SPD;}mdelay(PANIC_TIMER_STEP);}}if (panic_timeout != 0) {/** This will not be a clean reboot, with everything* shutting down.  But if there is a chance of* rebooting the system it will be rebooted.*/emergency_restart();--------------------------------------------------------执行重启操作。}
...
}

3. Section Permission Fault

4. Page Permission Fault

https://www.cnblogs.com/arnoldlu/p/8672139.html

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Kernel Exception 问题分析

https://www.jianshu.com/p/82d7b4c6bc9a

缺页异常(Page Faults) 和 Kernel Oops打印调用流程相关推荐

  1. 模拟请求分页管理中地址转换和缺页中断处理_Linux内存管理:缺页异常(一)

    缺页异常: 缺页异常(Page Faults)属于ARM V8处理器的异常类型中的同步异常.当MMU走表时可能会产生若干种类型的MMU faults(有同步的也有异步的),其中的同步异常,即这里将要讨 ...

  2. 6.S081-6缺页异常 - lazy allocation - Page Fault

    6.S081-6缺页异常Page Fault 这一节课,可以帮我们完成2个实验: 题目要求链接:Lab: xv6 lazy page allocation 对应做法链接:6.S081 Lab4 Laz ...

  3. linux内存管理(九)-缺页异常分析

    缺页异常被触发通常有两种情况 a.程序设计的不当导致访问了非法的地址 b.访问的地址是合法的,但是该地址还未分配物理页框 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB ...

  4. 图解|什么是缺页错误Page Fault

    1.号外号外 各位老铁,大家好! 上周大白有事停更1次,最近在想如何让大家在10分钟中有所收获,于是准备搞一个"什么是xxx"系列,写一些精悍的知识点. 先抛一道阿里面试题给大家热 ...

  5. 趣味图解 | 什么是缺页错误 Page Fault?

    来源 | 后端技术指南针 号外号外 各位老铁,大家好! 最近在想如何让大家在10分钟中有所收获,写一些精悍的知识点. 先抛一道阿里面试题给大家热热身,引出今天的主角-缺页异常Page Fault. 谈 ...

  6. 什么是缺页错误 Page Fault?

    谈谈对缺页异常Page Fault的理解. 话不多说,集合上车. 术语约定 VA:Virtual Address 虚拟地址 PA:Physical Address 物理地址 MMU:Memory Ma ...

  7. 页错误 Page Fault /缺页异常 详解

    ​​​​​目录 ​​​​​​ 1. 第一部分:如果你看得懂 1.1 页错误定义 1.2 页错误的处理 2. 第二部分:如果你看不懂上面的,请看这里 2.1. 举例子(背景) 2.1.1 进程及页映射 ...

  8. 一文透彻了解缺页异常

    缺页异常处理流程图解 首先明确下什么是缺页异常,CPU通过地址总线可以访问连接在地址总线上的所有外设,包括物理内存.IO设备等等,但从CPU发出的访问地址并非是这些外设在地址总线上的物理地址,而是一个 ...

  9. OK6410A 开发板 (八) 63 linux-5.11 OK6410A linux内核空间常见的异常情景及分析 kernel exception

    异常情景的level1 异常情景的level1 是最底层 show 当前状况的 api 其他具体的异常情景会根据 自身情景 调用 这些api oops打印Unable to ... 到 ---[ en ...

  10. linux缺页异常,操作系统缺页异常

    缺页异常,页缺失 Page fault,指的是硬错误.硬中断.分页错误.寻页缺失.缺页中断.页故障等)指的是当软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由中央处理 ...

最新文章

  1. UVa11038 - How Many O's?(统计0的个数)
  2. 使用 Oracle Datapump API 实现数据导出
  3. git学习4:分支管理
  4. CountDownLatch应用实战
  5. 铁血规则:事件预订与取消预订
  6. w8服务器dns修改,Win8.1系统的DNS地址如何修改?修改win8.1系统DNS地址图文教程
  7. python tkinter中的锚点(anchor)问题
  8. .net core webapi 导出excel(两种方式EPPLUS、NPOI),返回下载地址或文件流
  9. 升级Spring Boot内嵌Tomcat版本
  10. 产品设计:《现代设计史》
  11. python爬取煎蛋图片(2018-7-30有效)
  12. 输入一个字符串,对字符中的各个英文字符,数字,空格进行统计。 按照统计个数由多到少输出统计结果,如果统计的个数相同,则按照ASII码由小到大排序输出
  13. 逻辑思维能力测试题归纳总结之计算题
  14. 【drag】HTML5 drag API
  15. 计算机硬件和软件之间的区别
  16. 工科生安装Ubuntu18.04后干的事情
  17. 更自然的解决字典数组插入nil而导致crash
  18. 【工具】XP超级终端的设置与使用
  19. 记录安装Ubuntu Server 17.04, 安装hwinfo 失败的解决办法
  20. 江苏省计算机一级考试操作题,2017上半年计算机一级考试操作题

热门文章

  1. python代码整体加密,python 代码加密
  2. gbdt 算法比随机森林容易_数据挖掘面试准备(1)|常见算法(logistic回归,随机森林,GBDT和xgboost)...
  3. 配置LVS + Keepalived高可用负载均衡集群之图文教程
  4. windows10安装nodeJs及环境配置
  5. Linux升级python版本
  6. 集合Collections,List
  7. UVA 1252 十五 Twenty Questions
  8. Windows 無法查詢{7B849a69-220F-......} 的 DllName 登錄項目
  9. 【本人秃顶程序员】SpringMVC工作原理详解
  10. H5禁用长按选取,原生拷贝功能