您的位置 首页 被动

ARM Linux反常处理之data abort二

上文提到dataabort的正常处理过程中,最终会调用do_DataAbort函数,下面分析一下该函数的处理过程。do_DataAbortasmlinkagevoid__exc…

上文说到data abort的正常处理进程中,终究会调用do_DataAbort函数,下面剖析一下该函数的处理进程。

do_DataAbort

asmlinkage void __exception do_DataAbort(

unsigned long addr,//导致反常的内存地址

unsigned int fsr,//反常发生时CP15中的寄存器值,见前文

struct pt_regs *regs)//反常发生前的寄存器值列表

{

const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);

if (!inf->fn(addr, fsr, regs))

return;

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);

}

处理data abort时,首要依据fsr的值得到发生abort的原因,然后依据此原因从一个大局数组fsr_info中得到处理此种abort的struct fsr_info结构,然后调用结构中的fn函数处理。假如fn函数为空,或许函数回来不为0,则调用arm_notify_die函数。

arm_notify_die

首要看一下比较简略的景象,即fsr_info中fn未界说,此刻调用arm_notify_die处理:

void arm_notify_die(const char *str, struct pt_regs *regs,

struct siginfo *info, unsigned long err, unsigned long trap)

{

if (user_mode(regs)) {

//。。。

force_sig_info(info->si_signo, info, current);

} else {

die(str, regs, err);

}

}

该函数首要运用user_mode判别abort时是归于用户形式仍是内核形式,判别办法是看cpsr寄存器中的形式位。依照arm的界说,形式位为0代表用户形式。

l假如是用户形式,那么强制发送一个信号给导致abort的使命(留意这儿的使命或许是一个线程)。详细哪个信号被发送由struct fsr_info结构体中界说的值决议,一般来说,是一个能使进程中止的信号,比方SIGSEGV等等(SIGSEGV之类的信号即便被发给一个线程,也会中止整个进程,详细可看get_signal_to_deliver函数)。

l假如是内核形式,那么调用die函数,这是kernel处理OOPS的规范函数。

fsr_info

fsr_info数组界说在fault.c中,关于每一种或许导致data abort的原因,都有一个fsr_info结构与之对应。

static struct fsr_info fsr_info[] = {

{ do_bad,SIGSEGV, 0,”vector exception”},

//。。。

{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”},

{ do_bad,SIGBUS,0,”unknown 16″},

//。。。

{ do_bad,SIGBUS,0,”unknown 30″},

{ do_bad,SIGBUS,0,”unknown 31″}

};

fsr_info对大多数abort都调用do_bad函数处理,do_bad函数简略回来1,这样就能够持续履行上面说到的arm_notify_die。

fsr_info对以下四种特别abort将作独自处理:

l”section translation fault”do_translation_fault
段转化过错,即找不到二级页表

l”page translation fault”do_page_fault
页表过错,即线性地址无效,没有对应的物理地址

l”section permission fault”do_sect_fault
段权限过错,即二级页表权限过错

l”page permission fault”do_page_fault
页权限过错

段权限过错do_sect_fault

do_sect_fault函数直接调用do_bad_area作处理,并回来0,所以不会再经过arm_notify_die。do_bad_area中,判别是否归于用户形式。假如是用户形式,调用__do_user_fault函数;不然调用__do_kernel_fault函数。

void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)

if (user_mode(regs))

__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);

else

__do_kernel_fault(mm, addr, fsr, regs);

__do_user_fault中,会发送信号给当时线程。

__do_kernel_fault则比较复杂:

l调用fixup_exception进行修正操作,fixup的详细细节可在内核文档exception.txt中找到,它可用于处理get_user之类函数传入的地址参数无效的状况。

l假如不能修正,调用die函数处理oops。

l假如没有进程上下文,内核会在上一步的oops中panic。所以到这儿必定有一个进程与之相关,所以调用do_exit(SIGKILL)函数退出进程,SIGKILL会被设置在task_struct的exit_code域。

段表过错do_translation_fault

do_translation_fault函数中,会首要判别引起abort的地址是否处于用户空间。

l假如是用户空间地址,调用do_page_fault,转入和页表过错、页权限过错相同的处理流程。

l假如是内核空间地址,会判别该地址对应的二级页表指针是否在init_mm中。假如在init_mm里边,那么该二级页表指针到当时进程的一级页表;不然,调用do_bad_area处理(或许会调用到fixup)。

对段表过错的处理逻辑的个人了解如下(不确保彻底精确):
Linux发生段表过错,除了fixup之外还有两种原因:一个是用户空间映射的线性地址出现反常,另一个是内核中调用vmalloc分配的线性地址出现反常。对用户空间地址的反常处理很简单了解。关于内核地址,从vmalloc的完成代码中能够看到,它分配的线性空间的映射联系都会保存到大局变量init_mm中,所以,任何vmalloc生成的线性空间的二级页表都应该在init_mm中找到。(init_mm是内核的mm_struct,办理整个内核的内存映射)。

从这儿也能够看出,对vmalloc的地址拜访或许会发生两次反常:第一次是段表过错,生成二级页表;第2次是页表过错,分配真实的物理页面到线性空间。

关于init_mm的细节能够参阅http://my.chinaunix.net/space.php?uid=25471613&do=blog&id=323374,大约意思是内核页表改动时只改动init进程的内核页表init_mm,其它进程经过缺页反常从init_mm更新自己保护的内核页表。

页表过错do_page_fault

页权限过错do_page_fault

do_page_fault完成了真实的物理页面分配工作,别的栈扩展、mmap的支撑等也都在这儿。关于物理页面的分配,会调用到do_anonymous_page->。。。-> __rmqueue,__rmqueue中完成了物理页面分配的同伴算法。

假如当时没有满意物理页面供内存分配,即分配失利:

l内核形式下的abort会调用__do_kernel_fault,这与段权限过错中的处理相同。

l用户形式下,会调用do_group_exit退出该使命所属的进程。

用户程序请求内存空间时,假如库函数自身的内存池不能满意分配,会调用brk体系调用向体系请求扩展堆空间。但此刻扩展的仅仅线性空间,直到真实运用到那块线性空间时,体系才会经过data abort分配物理页面。用户空间的malloc函数回来不为NULL只能阐明得到了线性空间的资源,但物理内存或许并没有映射上去,所以真实物理内存分配失利时,进程仍是会以资源缺乏为由,直接退出。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/ziliao/beidong/277213.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部