Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15498601
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2007-07-19 15:12:40

浅析armlinux-sp的孵化流程,1号内核线程init的创建

文章来源:http://gliethttp.cublog.cn

接续上一篇《浅析armlinux-sp进程切换栈结构和切换函数__switch_to(),研究一下sp从内核系统启动到内核线程init启动的变化过程.
  当系统启动的时候,她运行在核心态,这时,系统中只有一个进程:初始化进程(init_task).象所有其它进程一样,初始化进程有一个堆栈、寄存器等表示的
机器状态(TSS).当系统中其它进程运行时,这些信息保存在初始化进程的task_struct数据结构中.在系统初始化结束时,初始化进程创建并启动一个核心线程(init),
然后自己进入空循环(idle).当系统中没有其它可以运行的进程时,调度程序会运行这个空闲进程.这个空闲进程的task_struct,:init_task_union,是唯一一个不是动态分配,而是
在内核连接时静态定义的结构,为了不至于混淆,该进程叫做init_task.
  空闲进程init_task的进程标识符pid是0,核心进程init的进程标识符pid是1.init是系统中第一个真正的进程,它执行一些系统初始化设置.
-----------------------------------------------------------------------
1.arch/arm/kernel/head-armv.S
//init_task的静态栈空间
...
        .type    __switch_data, %object
__switch_data:    .long    __mmap_switched
        .long    SYMBOL_NAME(__bss_start)
        .long    SYMBOL_NAME(_end)
        .long    SYMBOL_NAME(processor_id)
        .long    SYMBOL_NAME(__machine_arch_type)
        .long    SYMBOL_NAME(cr_alignment)
        .long    SYMBOL_NAME(init_task_union)+8192
...
        .align    5
__mmap_switched:
        adr    r3, __switch_data + 4
        ldmia    r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
                                             @ sp = stack pointer
        mov    fp, #0                        @ Clear BSS (and zero fp)
1:      cmp    r4, r5
        strcc    fp, [r4],#4
        bcc    1b
        str    r9, [r6]                      @ Save processor ID
        str    r1, [r7]                      @ Save machine type
#ifdef CONFIG_ALIGNMENT_TRAP
        orr    r0, r0, #2                    @ ...........A.
#endif
        bic    r2, r0, #2                    @ Clear 'A' bit
        stmia    r8, {r0, r2}                @ Save control register values
        b    SYMBOL_NAME(start_kernel)
...
-----------------------------------------------------------------------
2.反汇编__switch_data数据
c0008038 <__switch_data>:
c0008038:    c0008080     andgt    r8, r0, r0, lsl #1
c000803c:    c0120660     andgts    r0, r2, r0, ror #12
c0008040:    c01415c8     andgts    r1, r4, r8, asr #11
c0008044:    c01213b8     ldrgth    r1, [r2], -r8
c0008048:    c01213ac     andgts    r1, r2, ip, lsr #7
c000804c:    c0118c4c     andgts    r8, r1, ip, asr #24
c0008050:    c0118000     andgts    r8, r1, r0                    

//sp=c0118000,内核空间,启动时,暂时使用,后由init进程使用[gliethttp]
//arm入栈方式:sp先sp-4,之后把数据推入sp-4中
-----------------------------------------------------------------------
3.arch/arm/kernel/Init_task.c
union task_union init_task_union __attribute__((__section__(".init.task"))) =
        { INIT_TASK(init_task_union.task) };
-----------------------
#ifndef INIT_TASK_SIZE
# define INIT_TASK_SIZE    2048*sizeof(long)
#endif
union task_union {                                         //很简单就是告诉编译器占用8k固定空间,供内核使用
    struct task_struct task;                               //task_struct在低地址、stack处在高地址
    unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
-----------------------
在arch/arm/vmlinux-armv.lds.in
...
. = ALIGN(8192);                                           //8k对齐
    .data : {
        /*
         * first, the init task union, aligned
         * to an 8192 byte boundary.
         */

        *(.init.task)                                      //8k对齐[gliethttp 2007-07-18]
        /*
         * then the cacheline aligned data
         */

        . = ALIGN(32);
        *(.data.cacheline_aligned)
        /*
         * and the usual data section
         */

        *(.data)
        CONSTRUCTORS
        _edata = .;
    }
...
-----------------------------------------------------------------------
4.反汇编init_task_union,以下数值是由INIT_TASK(init_task_union.task)静态编译生成
c0116000 <init_task_union>:
    ...
c0116010:    c0118ee0     andgts    r8, r1, r0, ror #29
    ...
c011601c:    ffffffff     swinv    0x00ffffff
c0116020:    0000000a     andeq    r0, r0, sl
    ...
c0116034:    ffffffff     swinv    0x00ffffff
c0116038:    ffffffff     swinv    0x00ffffff
c011603c:    c011603c     andgts    r6, r1, ip, lsr r0
c0116040:    c011603c     andgts    r6, r1, ip, lsr r0
c0116044:    00000000     andeq    r0, r0, r0
c0116048:    c0116000     andgts    r6, r1, r0
c011604c:    c0116000     andgts    r6, r1, r0
c0116050:    c01189ac     andgts    r8, r1, ip, lsr #19
    ...
c0116094:    c0116000     andgts    r6, r1, r0
c0116098:    c0116000     andgts    r6, r1, r0
    ...
c01160a8:    c01160a8     andgts    r6, r1, r8, lsr #1
c01160ac:    c01160a8     andgts    r6, r1, r8, lsr #1
    ...
c01160bc:    c01160bc     ldrgth    r6, [r1], -ip
c01160c0:    c01160bc     ldrgth    r6, [r1], -ip
    ...
c01160f4:    c0025b1c     andgt    r5, r2, ip, lsl fp
    ...
c01161d4:    fffffeff     swinv    0x00fffeff
c01161d8:    00000000     andeq    r0, r0, r0
c01161dc:    ffffffff     swinv    0x00ffffff
c01161e0:    00000000     andeq    r0, r0, r0
c01161e4:    c0119d08     andgts    r9, r1, r8, lsl #26
c01161e8:    ffffffff     swinv    0x00ffffff
c01161ec:    ffffffff     swinv    0x00ffffff
c01161f0:    ffffffff     swinv    0x00ffffff
c01161f4:    ffffffff     swinv    0x00ffffff
c01161f8:    ffffffff     swinv    0x00ffffff
c01161fc:    ffffffff     swinv    0x00ffffff
c0116200:    00800000     addeq    r0, r0, r0
c0116204:    ffffffff     swinv    0x00ffffff
c0116208:    00000000     andeq    r0, r0, r0
c011620c:    ffffffff     swinv    0x00ffffff
c0116210:    ffffffff     swinv    0x00ffffff
c0116214:    ffffffff     swinv    0x00ffffff
    ...
c0116220:    00000400     andeq    r0, r0, r0, lsl #8
c0116224:    00000400     andeq    r0, r0, r0, lsl #8
c0116228:    ffffffff     swinv    0x00ffffff
c011622c:    ffffffff     swinv    0x00ffffff
c0116230:    ffffffff     swinv    0x00ffffff
c0116234:    ffffffff     swinv    0x00ffffff
c0116238:    ffffffff     swinv    0x00ffffff
c011623c:    ffffffff     swinv    0x00ffffff
c0116240:    77730000     ldrvcb    r0, [r3, -r0]!
c0116244:    65707061     ldrvsb    r7, [r0, -#97]!
c0116248:    00000072     andeq    r0, r0, r2, ror r0
    ...
c011626c:    00000001     andeq    r0, r0, r1
    ...
c0116320:    0000001d     andeq    r0, r0, sp, lsl r0
c0116324:    c01182e0     andgts    r8, r1, r0, ror #5
c0116328:    c0118304     andgts    r8, r1, r4, lsl #6
    ...
c0116334:    c01184a4     andgts    r8, r1, r4, lsr #9
    ...
c0116344:    c0116340     andgts    r6, r1, r0, asr #6
    ...
c0118000 <runqueue_lock>:
-----------------------------------------------------------------------
综上可知start_kernel函数中使用的sp值[栈顶]为内核空间的c0118000[我的at91rm9200板sdram起始地址为0x20000000,head-armv.S前4M虚拟内存映射之后,物理内存20118000对应内核虚拟内存c0118000],
看看init线程的创建
init/Main.c->rest_init()->kernel/Fork.c->kernel_thread()->arch/arm/kernel/Process.c->arch_kernel_thread()->arch/arm/kernel/Sys_arm.c->sys_clone();
static void rest_init(void)
{
    kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);//创建核心进程init
    unlock_kernel();
    current->need_resched = 1;
     cpu_idle();
}
long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
    struct task_struct *task = current;
    unsigned old_task_dumpable;
    long ret;
    /* lock out any potential ptracer */
    task_lock(task);
    if (task->ptrace) {
        task_unlock(task);
        return -EPERM;
    }
    old_task_dumpable = task->task_dumpable;
    task->task_dumpable = 0;
    task_unlock(task);
    ret = arch_kernel_thread(fn, arg, flags);                

//arch_kernel_thread唯一调用处,和μC/OS-II的OSStart()函数性质一样.
    /* never reached in child process, only in parent */
    current->task_dumpable = old_task_dumpable;
    return ret;
}
pid_t arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
    pid_t __ret;
//r0 = fn = init;
//r1 = arg = null;
//r2 = flags = 0x00010e00 = CLONE_FS | CLONE_FILES | CLONE_SIGNAL;[2007-07-18 gliethttp]
    __asm__ __volatile__(
    "orr    r0, %1, %2    @ kernel_thread sys_clone    \n\
    mov    r1, #0                                      \n\
    "
__syscall(clone)"                                 \n\//出发软中断swi,执行sys_clone系统调用
    movs    %0, r0        @ if we are the child        \n\
    bne    1f                                          \n\
    mov    fp, #0         @ ensure that fp is zero     \n\
    mov    r0, %4                                      \n\
    mov    lr, pc                                      \n\
    mov    pc, %3                                      \n\
    b    sys_exit                                      \n\
1:    "

        : "=&r" (__ret)
        : "Ir" (flags), "I" (CLONE_VM), "r" (fn), "r" (arg)
    : "r0", "r1", "lr");
    return __ret;
}
-----------------------
arch/arm/kernel/entry-common.S
sys_clone_wapper:
        add    r2, sp, #S_OFF[S_OFF=8 gliethttp]           //调整r2指针,r2=sp+8,指向pt_regs结构的开始
        b    SYMBOL_NAME(sys_clone)                         //对于init线程的创建,该sp处在init_task_union空间
-----------------------
arch/arm/kernel/entry-common.S
...
.align    5
ENTRY(vector_swi)
    save_user_regs
    zero_fp
    get_scno

//ldr r7, [lr,-#4]将中断语句[如:swi 0x0090000b,对应的机器码:0xef90000b]转储到r7中,此时r7=0xef90000b[gliethttp]
    arm710_bug_check scno, ip
#ifdef CONFIG_ALIGNMENT_TRAP
    ldr    ip, __cr_alignment
    ldr    ip, [ip]
    mcr    p15, 0, ip, c1, c0        @ update control register
#endif
    enable_irq ip
    str    r4, [sp, #-S_OFF]!

//将r4存放到[sp-8]地址处,同时sp=sp-8[2007-07-19 gliethttp]这样出现了8字节的sp空洞
    get_current_task tsk
    ldr    ip, [tsk, #TSK_PTRACE]         @ check for syscall tracing
    bic    scno, scno, #0xff000000        @ mask off SWI op-code
    eor    scno, scno, #OS_NUMBER << 20   @ check OS number
    adr    tbl, sys_call_table            @ load syscall table pointer
    tst    ip, #PT_TRACESYS               @ are we tracing syscalls?
    bne    __sys_trace

    adrsvc    al, lr, ret_fast_syscall    @ return address
    cmp    scno, #NR_syscalls             @ check upper syscall limit
    ldrcc    pc, [tbl, scno, lsl #2]

//无sp入栈操作的跳转到swi处理函数[gliethttp 2007-07-18]
-----------------------
arch/arm/kernel/entry-header.S
    .macro    save_user_regs
    sub    sp, sp, #S_FRAME_SIZE        

//sizeof(struct pt_regs)=18*4=72=S_FRAME_SIZE
    stmia    sp, {r0 - r12}           

//顺序存储r0~r12到sp,sp+1,...,sp+12,sp值不变
    add    r8, sp, #S_PC

//r8=sp+#S_PC=sp+60
    stmdb    r8, {sp, lr}^                                 //按顺序分别将lr,sp存入r8-4和r8-8地址中
    mrs    r8, spsr                                        //spsr暂存到r8
    str    lr, [sp, #S_PC]                                 //将lr存入sp+#S_PC
    str    r8, [sp, #S_PSR]                                //将spsr存入sp+#S_PSR
    str    r0, [sp, #S_OLD_R0]                             //将r0存入sp+#S_OLD_R0
    .endm
-----------------------
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
{
//r0 = clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
//r1 = newsp = 0
//r2 = 通过上面分析r2是指向存储到sp栈中的pt_regs结构的栈指针起始值[2007-07-18 gliethttp]
    if (!newsp)//对于init进程的创建,sp栈和regs处在init_task_union空间
        newsp = regs->ARM_sp;                              

//即:regs->uregs[13](2007-07-18 gliethttp)
    return do_fork(clone_flags, newsp, regs, 0);
}
-----------------------------------------------------------------------
include/asm-arm/proc-armv/Ptrace.h定义了regs结构
struct pt_regs {
    long uregs[18];
};

#define ARM_cpsr    uregs[16]
#define ARM_pc        uregs[15]
#define ARM_lr        uregs[14]
#define ARM_sp        uregs[13]
#define ARM_ip        uregs[12]
#define ARM_fp        uregs[11]
#define ARM_r10        uregs[10]
#define ARM_r9        uregs[9]
#define ARM_r8        uregs[8]
#define ARM_r7        uregs[7]
#define ARM_r6        uregs[6]
#define ARM_r5        uregs[5]
#define ARM_r4        uregs[4]
#define ARM_r3        uregs[3]
#define ARM_r2        uregs[2]
#define ARM_r1        uregs[1]
#define ARM_r0        uregs[0]
#define ARM_ORIG_r0    uregs[17]
未完待续

阅读(1445) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~