uclinux第一个内核线程的运行

来源:互联网 发布:mac apache 下载 编辑:程序博客网 时间:2024/04/29 00:22

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

  

本文适用于

ADSP-BF561

uclinux-2008r1.5-RC3(移植到vdsp5)

Visual DSP++ 5.0(update 5)

 

欢迎转载,但请保留作者信息

 

 

kernel_init是内核中创建的第一个内核线程,此时DSP处于中断15的状态。当内核第一次执行schedule的时候,这个线程将开始执行。

看看上下文切换的代码:

ENTRY(_resume)

     /*

      * Beware - when entering resume, prev (the current task) is

      * in r0, next (the new task) is in r1.

      */

     p0 = r0;

     p1 = r1;

     [--sp] = rets;

     [--sp] = fp;

     [--sp] = (r7:4, p5:3);

 

     /* save usp */

     p2 = usp;

     [p0+(TASK_THREAD+THREAD_USP)] = p2;

 

     /* save current kernel stack pointer */

     [p0+(TASK_THREAD+THREAD_KSP)] = sp;

 

     /* save program counter */

     r1.l = _new_old_task;

     r1.h = _new_old_task;

     [p0+(TASK_THREAD+THREAD_PC)] = r1;

 

     /* restore the kernel stack pointer */

     sp = [p1+(TASK_THREAD+THREAD_KSP)];

 

     /* restore user stack pointer */

     p0 = [p1+(TASK_THREAD+THREAD_USP)];

     usp = p0;

 

     /* restore pc */

     p0 = [p1+(TASK_THREAD+THREAD_PC)];

     jump (p0);

 

     /*

      * Following code actually lands up in a new (old) task.

      */

 

_new_old_task:

     (r7:4, p5:3) = [sp++];

     fp = [sp++];

     rets = [sp++];

 

     /*

      * When we come out of resume, r0 carries "old" task, becuase we are

      * in "new" task.

      */

     rts;

ENDPROC(_resume)

此时传递进来的prev是初始线程的task_struct,而next则是kernel_init这一内核线程的task_struct。此时新线程的PC指向的是_ret_from_fork这一汇编写的函数,因此DSP将跳转到这个地方执行,注意此时仍然处于中断15的状态,且SP已经切换到新线程的stack

ENTRY(_ret_from_fork)

     SP += -12;

     call _schedule_tail;

     SP += 12;

     r0 = [sp + PT_IPEND];

     cc = bittst(r0,1);

     if cc jump .Lin_kernel;

     RESTORE_CONTEXT

     rti;

.Lin_kernel:

     bitclr(r0,1);

     [sp + PT_IPEND] = r0;

     /* do a 'fake' RTI by jumping to [RETI]

      * to avoid clearing supervisor mode in child

      */

     r0 = [sp + PT_PC];

     [sp + PT_P0] = r0;

 

     RESTORE_ALL_SYS

     jump (p0);

ENDPROC(_ret_from_fork)

当跳转到这个入口时,SP已经切换到了新线程的stack,由于是内核线程,此时将跳转到.Lin_kernel执行。由于在kernel_init这一内核线程创建时,将struct pt_regs放在了栈的底部,因此在这里可以通过[sp + ???]这样的方式来访问结构体的成员。

     r0 = [sp + PT_PC];

     [sp + PT_P0] = r0;

这两行代码将线程创建时设置好的PC入口放在了struct pt_regs::p0这个成员中,它将指向kernel_thread_helper这个地址,当执行完

     RESTORE_ALL_SYS

这个宏时,所有寄存器的值都被恢复出来,最后就可以跳转到kernel_thread_helper开始执行了,看看kernel_thread_helper

/*

 * This gets run with P1 containing the

 * function to call, and R1 containing

 * the "args".  Note P0 is clobbered on the way here.

 */

void kernel_thread_helper(void);

__asm__(".section .text/n"

     ".align 4/n"

     "_kernel_thread_helper:/n/t"

     "/tsp += -12;/n/t"

     "/tr0 = r1;/n/t" "/tcall (p1);/n/t" "/tcall _do_exit;/n" ".previous;");

调用这段代码的时候,p1里面存放的是回调函数的地址,在这里就是kernel_init函数,而r1里面则放了给kernel_init的参数。

注意此时DSP仍然处于中断15的状态。

 

 

1       参考资料

uclinux内核线程的创建(2009-4-23)

fork_inituclinux内核的线程数量限制(2009-4-22)

uclinux内核的任务优先级及其load_weight(2009-4-22)

init_thread_union猜想(2009-1-17)

uclinux2.6(bf561)内核中的current_thread_info(2008/5/12)