• [No00003A]操作系统Operating Systems 内核级线程Kernel Threads内核级线程实现Create KernelThreads


    开始核心级线程

    内核级线程对多核的支持怎么样?

    和用户级相比,核心级线程有什么不同?

    ThreadCreate 是系统调用,内核管理TCB ,内核负责切换线程

    如何让切换成型? − − 内核栈,TCB

    • 用户栈是否还要用? 执行的代码仍然在用户态,还要进行函数调用
    • 一个栈到一套栈;两个栈到两套栈
    • TCB 关联内核栈,那用户栈怎么办?

    用户栈和内核栈之间的关联

    所有中断( 时钟、外设、INT指令) 都引起上述切换

    中断( 硬件) 又一次帮助了操作系统…

    仍然是那个A() ,B() ,C() ,D()…

    认真体会从内核返回( 中断返回) 时的样子…

    开始内核中的切换:switch_to

    switch_to: 仍然是通过TCB 找到内核栈指针;然后通过ret 切到 某个内核程序; 最后再用CS:PC 切到

    sys_read(){ 启动磁盘读; 将自己变成阻塞;找到next;switch_to(cur, next);}

    回答上面的问号??, ???, ????…

    ???: sys_read 函数的某个地方

    ??: interrupt 之前的某个地方

    ???: sys_xxx 函数中的某个地方

    最关键的地方来了: T 创建时如何填写?? , ????

    ?? 500 ,函数C()的开始地址

    ???? 一段能完成第二级返回的代码,一段包含iret 的代码…

    内核线程switch_to 的五段论

    ThreadCreate! 做成那个样子…

    用户级线程、核心级线程的对比

    操作系统Operating Systems内核级线程实现Create Kernel Threads

    核心级线程的两套栈,核心是内核栈…

    整个故事要从进入内核开始—— 某个中断开始…

    切换五段论中的中断入口和中断出口

    void sched_init(void)

    {set_system_gate(0x80,&system_call);}

    初始化时将各种中断处理设置好

    _system_call:

    push %ds..%fs

    pushl %edx...

    call sys_fork

    pushl %eax

    内核栈:

    movl _current,%eax

    cmpl $0,state(%eax)

    jne reschedule

    cmpl $0,counter(%eax)

    je reschedule

    ret_from_sys_call:

    reschedule:

    pushl $ret_from_sys_call

    jmp _schedule

    切换五段论中的schedule

    切换五段论中的switch_to

    Linux 0.11 用tss切换,但也可以用栈切换,因为tss 中的信息可以写到内核栈中

    另一个故事ThreadCreate 就顺了…

    从sys_fork 开始CreateThread

    _sys_fork:

    push %gs; pushl %esi

    ...

    pushl %eax

    call _copy_process

    addl $20,%esp

    ret

    int copy_process(int nr,long ebp,long edi,long esi,long gs,longnone,long ebx,long ecx,long edx, longfs,long es,long ds,long eip,longcs,long eflags,long esp,long ss)

    copy_process的细节:创建栈

    p=(struct task_struct *)get_free_page();// 申请内存空间

    p->tss.esp0 = PAGE_SIZE + (long) p;

    p->tss.ss0 = 0x10;// 创建内核栈

    p->tss.ss = ss & 0xffff;

    p->tss.esp = esp;// 创建用户栈(和父进程共用栈)

    申请内存空间;

    创建TCB;

    创建内核栈和用户栈;

    填写两个 stack;

    关联栈和TCB;

    copy_process 的细节:执行前准备

    p->tss.eip = eip;

    p->tss.cs = cs & 0xffff;// 将执行地址cs:eip 放在tss 中

    p->tss.eax = 0;

    p->tss.ecx = ecx;// 执行时的寄存器也放进去了

    p->tss.ldt = _LDT(nr);

    set_tss_desc(gdt+(nr<<1) + 仔细体会tss 将要承担的作用…

    FIRST_TSS_ENTRY, &(p->tss));

    set_ldt_desc(gdt+(nr<<1) +

    FIRST_LDT_ENTRY, &(p->ldt));// 内存跟着切换

    p->state = TASK_RUNNING;

    copy_process( ...,long eip,long cs,longe flags,long esp,long ss)

    ...

    填写两个stack;

    第三个故事: 如何执行我们想要的代码?

    int main(int argc, char * argv[])

    { while(1) { scanf("%s", cmd);

    if(!fork()) {exec(cmd);} wait(0); }

    ThreadCreate(*A) 中的A 必须体现? 用户输入hello 命令,exec(hello)

    fork() 何时返回0 ,何时不会? 首先要找到fork() 怎么返回?

    mov %eax, __NR_fork

    INT 0x80

    mov res,%eax 如何到这条指令?

    父进程用iret ,因为要从核心态到用户态;那么子进程呢? 仔细想一想…

    结构: 子进程进入A ,父进程等待…

    故事要从exec 这个系统调用开始

    if(!fork()) {exec(cmd);}

    _system_call:

    push %ds .. %fs

    pushl %edx..

    call sys_execve

    _sys_execve:

    lea EIP(%esp),%eax

    pushl %eax

    call _do_execve

    EIP = 0x1C

    终于可以让AA 执行了…

    int do_execve( * eip,...

    {     p += change_ldt(...;

      eip[0] = ex.a_entry;

      eip[3] = p; ...

    struct exec {

    unsigned long a_magic;

    unsigned a_entry; //口 入口 };

    eip[0] = esp + 0x1C; eip[3] = esp +0x1C+0x0C = esp + 0x28 ( 正好是SP)

    ex.a_entry 是可执行程序入口地址,产生可执行文件时写入…

    • 理解switch_to 对应的栈切换,将自己变成计算机
    • ThreadCreate的目的就是初始化这样一套栈
  • 相关阅读:
    客户端字符集的作用总结
    江苏诚迈科技笔试题2013
    oracle数据库存储过程中NO_DATA_FOUND不起作用?
    判断单链表是否有环
    使用Linux静态库
    Texas Instruments matrix-gui-2.0 hacking -- submenu.php
    Texas Instruments matrix-gui-2.0 hacking -- run_script.php
    Texas Instruments matrix-gui-2.0 hacking -- menubar.php
    Texas Instruments matrix-gui-2.0 hacking -- helper_functions.php
    Texas Instruments matrix-gui-2.0 hacking -- generate.php
  • 原文地址:https://www.cnblogs.com/Chary/p/No00003A.html
Copyright © 2020-2023  润新知