zoukankan      html  css  js  c++  java
  • current与rq->curr浅析

    基于linux-5.4

    背景

    在内核中检查当前运行任务时有时候会使用rq->curr,而其他时候内核又使用current来作为当前进程使用,究竟二者是什么关系,又什么区别呢? 带着好奇心我扒开内核代码简单进行了分析

    1 rq->curr浅析

    rq->curr表示的是rq这个就绪队列上当前正在运行任务的task_struct结构指针,它在调度函数__schedule()中进行更新。

    • 在主调度函数__schedule()中,内核先选好下一个将要运行的任务next,然后将任务next更新到rq->curr指针;在__schedule()函数中稍后就会从当前任务(prev)切换到新选择的任务next运行。
    __schedule()
    
      RCU_INIT_POINTER(rq->curr, next);

    由于运行队列runqueue是percpu的,因而在内核中可通过(&per_cpu(runqueues, (cpu)))方式(实际上内核有提供专门的宏cpu_rq(cpu)来完成这个动作)获取到目标cpu的rq,然后通过rq->curr就可获取到目标cpu上正在运行的任务。

     2 current宏

    current在内核中通常以宏的形式存在,其实现方式依赖于具体的架构和内核版本,有些实现是从堆栈中获取当前运行任务的task_struct结构指针,有些则从寄存器中获取。

    • current的实现

    拿 arm64架构 + linux-5.4版本 为背景举例, current实际上是从sp_el0这个寄存器中读取当前任务的task_struct结构指针,这个宏的具体实现如下:

    /* current实际上是get_current()函数 */
    #define current get_current()
    
    /* get_current()函数实际上是通过mrs指令从sp_el0寄存器取出当前任务的task_struct指针 */
    /*
     * We don't use read_sysreg() as we want the compiler to cache the value where
     * possible.
     */
    static __always_inline struct task_struct *get_current(void)
    {
            unsigned long sp_el0;
    
            asm ("mrs %0, sp_el0" : "=r" (sp_el0));
    
            return (struct task_struct *)sp_el0;
    }
    •  sp_el0值的来历

    这个sp_el0存放当前任务的task_struct,它是怎么来的呢?

    内核在发生任务切换动作前会将切换的next任务写入percpu变量__entry_task,这样正在cpu上运行的任务的task_struct保存在percpu变量__entry_task中;
    同时,由于内核中会频繁使用当前任务"current",而通过读取percpu变量来获取当前任务还是有一定开销;为此,正在运行的任务(也就是current)从用户态陷入到内核态时,会将这个percpu变量存放到sp_el0寄存器(该寄存器是用户态堆栈指针,在内核态暂时没有使用)中,这样每次获取当前任务时,可直接使用sp_el0来获取。

    • 每次任务切换时更新__entry_task为当前任务
    __switch_to()-->entry_task_switch(next)
    static void entry_task_switch(struct task_struct *next)
    {
        __this_cpu_write(__entry_task, next);
    }
    • 任务从用户态进入到内核态时将__entry_task取出到sp_el0中
        .macro  kernel_entry, el, regsize = 64
        ......
        //取percpu变量__entry_task到tsk
        ldr_this_cpu    tsk, __entry_task, x20
        ......
        .if     el == 0
        msr     sp_el0, tsk        //如果异常发生在用户态,则将tsk取到sp_el0
        .endif

    对于sp_el0是如何/何时存放当前任务的,实际上就是在当前任务从用户态进入内核态时动态从percpu变量__entry_task获取的;而这个percpu变量__entry_task则是在前一个任务调用__switch_to()切换到当前任务切换运行时设置的。 

  • 相关阅读:
    [linux] 使用markdown写文档
    [c/c++] C数据结构: 链表 Linked List
    Apache 2 : starting apache
    GNU/CPIO 学习小结
    lsof 命令小结
    在Linux中扩展磁盘容量(1)
    在linux中扩展磁盘容量(2)LVM
    RHEL6.0 QEMU/KVM 建立新的虚拟机之配置Birdged Networking
    NFS server down机或重启导致的NFS系统错误
    Build linux kernel Module
  • 原文地址:https://www.cnblogs.com/liuhailong0112/p/14921228.html
Copyright © 2011-2022 走看看