• linux 进程用户栈和内核栈


    进程的用户栈和内核栈

    进程是程序的一次执行过程。用剧本和演出来类比,程序相当于剧本,而进程则相当于剧本的一次演出,舞台、灯光则相当于进程的运行环境,贴切

    进程的堆栈

    每个进程都有自己的堆栈,内核在创建一个新的进程时,在创建进程控制块task_struct的同时,也为进程创建自己堆栈。一个进程 有2个堆栈,用户堆栈和系统堆栈;用户堆栈的空间指向用户地址空间,内核堆栈的空间指向内核地址空间。当进程在用户态运行时,CPU堆栈指针寄存器指向的 用户堆栈地址,使用用户堆栈,当进程运行在内核态时,CPU堆栈指针寄存器指向的是内核栈空间地址,使用的是内核栈;

    进程用户栈和内核栈之间的切换

    当进程由于中断或系统调用从用户态转换到内核态时,进程所使用的栈也要从用户栈切换到内核栈。系统调用实质就是通过指令产生中断,称为软中断。进程因为中断(软中断或硬件产生中断),使得CPU切换到特权工作模式(0最高为内核模式,3最低为用户模式),此时进程陷入内核态,进程进入内核态后,首先把用户态的堆栈地址保存在内核堆栈中,然后设置堆栈指针寄存器的地址为内核栈地址,这样就完成了用户栈向内核栈的切换。

    当进程从内核态切换到用户态时,最后把保存在内核栈中的用户栈地址恢复到CPU栈指针寄存器即可,这样就完成了内核栈向用户栈的切换。

    这里要理解一下内核堆栈。前面我们讲到,进程从用户态进入内核态时,需要在内核栈中保存用户栈的地址。那么进入内核态时,从哪里获得内核栈的栈指针呢?

    要解决这个问题,先要理解从用户态刚切换到内核态以后,进程的内核栈总是空的。这点很好理解,当进程在用户空间运行时,使用的是用户 栈;当进程在内核态运行时,内核栈中保存进程在内核态运行的相关信息,但是当进程完成了内核态的运行,重新回到用户态时,此时内核栈中保存的信息全部恢 复,也就是说,进程在内核态中的代码执行完成回到用户态时,内核栈是空的。

    理解了从用户态刚切换到内核态以后,进程的内核栈总是空的,那刚才这个问题就很好理解了,因为内核栈是空的,那当进程从用户态切换到内核态后,把内核栈的栈顶地址设置给CPU的栈指针寄存器就可以了。

    X86 Linux内核栈定义如下(可能现在的版本有所改变,但不妨碍我们对内核栈的理解):

    在/include/linux/sched.h中定义了如下一个联合结构:

    union task_union {

           struct task_struct task;

           unsigned long stack[2408];

    };

    从这个结构可以看出,内核栈占8kb的内存区。实际上,进程的task_struct结构所占的内存是由内核动态分配的,更确切地说,内核根本不给task_struct分配内存,而仅仅给内核栈分配8K的内存,并把其中的一部分给task_struct使用。

    这样内核栈的起始地址就是union task_union变量的地址+8K 字节的长度。例如:我们动态分配一个union task_union类型的变量如下:

    unsigned char *gtaskkernelstack

    gtaskkernelstack = kmalloc(sizeof(union task_union));

     

    那么该进程每次进入内核态时,内核栈的起始地址均为:(unsigned char *)gtaskkernelstack + 8096

     

    进程上下文

    进程切换现场称为进程上下文(context),包含了一个进程所具有的全部信息,一般包括:进程控制块(Process Control Block,PCB)、有关程序段和相应的数据集。

     

    进程控制块PCB(任务控制块)

    进程控制块是进程在内存中的静态存在方式,Linux内核中用task_struct表示一个进程(相当于进程的人事档案)。进程的静 态描述必须保证一个进程在获得CPU并重新进入运行态时,能够精确的接着上次运行的位置继续进行,相关的程序段,数据以及CPU现场信息必须保存。处理机 现场信息主要包括处理机内部寄存器和堆栈等基本数据。

    进程控制块一般可以分为进程描述信息、进程控制信息,进程相关的资源信息和CPU现场保护机构。

     

    进程的切换

    当一个进程的时间片到时,进程需要让出CPU给其他进程运行,内核需要进行进程切换。

    Linux 的进程切换是通过调用函数进程切换函数schedule来实现的。进程切换主要分为2个步骤:

    1. 调用switch_mm()函数进行进程页表的切换;

    2. 调用 switch_to() 函数进行 CPU寄存器切换;

     

    __switch_to定义在/arch/arm/kernel目录下的entry-armv.S 文件中,源码如下:

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

    ENTRY(__switch_to)

     UNWIND(.fnstart    )

     UNWIND(.cantunwind )

        add ip, r1, #TI_CPU_SAVE

        ldr r3, [r2, #TI_TP_VALUE]

        stmia   ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack

    #ifdef CONFIG_MMU

        ldr r6, [r2, #TI_CPU_DOMAIN]

    #endif

    #if __LINUX_ARM_ARCH__ >= 6

    #ifdef CONFIG_CPU_32v6K

        clrex

    #else

        strex   r5, r4, [ip]            @ Clear exclusive monitor

    #endif

    #endif

    #if defined(CONFIG_HAS_TLS_REG)

        mcr p15, 0, r3, c13, c0, 3      @ set TLS register

    #elif !defined(CONFIG_TLS_REG_EMUL)

        mov r4, #0xffff0fff

        str r3, [r4, #-15]          @ TLS val at 0xffff0ff0

    #endif

    #ifdef CONFIG_MMU

        mcr p15, 0, r6, c3, c0, 0       @ Set domain register

    #endif

        mov r5, r0

        add r4, r2, #TI_CPU_SAVE

        ldr r0, =thread_notify_head

        mov r1, #THREAD_NOTIFY_SWITCH

        bl atomic_notifier_call_chain

        mov r0, r5

        ldmia   r4, {r4 - sl, fp, sp, pc}   @ Load all regs saved previously

     UNWIND(.fnend      )

    ENDPROC(__switch_to)

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

    Switch_to的处理流程如下:

     

    1. 保存本进程的CPU寄存器(PC、R0 ~ R13)到本进程的栈中;

    2. 保存SP(本进程的栈基地址)到task->thread.save 中;

    3. 从新进程的task->thread.save恢复SP为新进程的栈基地址;

    4. 从新进程的栈中恢复新进程的CPU相关寄存器值,

    5. 新进程开始运行,完成任务切换。

     

    这里读者可能会问,在进行任务切换的时候,到底是在运行进程1还是运行进程2呢?进程切换的时候,已经进行页表切换,那页表切换之后,切换进程使用的是进程1还是进程2的页表呢?

     

    要回答这个问题,首先我们要明白由谁来完成进程切换?

     

    通过对操作系统的理解,毫无疑问,进程切换是由内核来完成的,也就是说,在进行进程切换时,CPU运行在内核模式,使用的是内核空间的内核代码,它既不属于进程1,也不属于进程2,当进程的时间片到时,内核提供服务来完成进程的切换。既不使用进程1的页表,也不使用进程2的页表,使用的内核映射页表。这样我们就很好理解上面的问题了。


  • 相关阅读:
    Changing Icon File Of Push Button At Runtime In Oracle Forms 6i
    Set Font Properties On Mouse Hover Of Push Button And Text Items At Run time In Oracle Forms
    Change An Item Property Using Set_Item_Property In Oracle Forms
    Calling / Running a report in Oracle forms 10g / 11g
    Change Or Set Report Object Property At Run Time In Oracle Forms Using Set_Report_Object_Property Command
    Refresh / Updating a form screen in Oracle D2k Forms 6i
    Know How And When To Use System.Message_Level To Control Messages In Oracle Forms
    Perform Cut Copy Paste Operations Using Cut_Region Copy_Region Paste_Region Commands In Oracle Forms
    CHECKBOX_CHECKED built-in in Oracle D2k Forms
    Limiting To Select Only 5 Check Boxes Out Of Ten In Oracle Forms
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786939.html
Copyright © 2020-2023  润新知