【问题标题】:Understanding the getting of task_struct pointer from process kernel stack理解从进程内核栈获取task_struct指针
【发布时间】:2020-10-24 00:25:28
【问题描述】:

现在我正在阅读 Robert Love 的《Linux Kernel Development 3d Edition》一书。在那里,他写了包含指向 task_struct 结构的指针的 thread_info 结构,据我所知,它位于进程内核堆栈的底部或顶部(取决于架构)。直到最近我才熟悉 Linux 内核 API,也不知道 current() 方法的存在。书中有一段关于 current() 方法实际工作原理的摘录:

在 x86 上,通过屏蔽堆栈的 13 个最低有效位来计算电流 获取 thread_info 结构的指针。这是由 current_thread_info() 函数。程序集如下所示: movl $-8192, %eax andl %esp, %eax 这里假设堆栈大小为 8KB。当启用 4KB 堆栈时,4096 用于 代替 8192。

我的问题是:

  1. 据我所知,如果我们将十进制值表示为一组位,那么该组中只有一个最低有效位,不是吗?
  2. 什么是神奇的数字 13?

对于那些将阅读本主题的人来说,我提出的问题可能会导致作者没有正确理解内存分配和管理过程的结论。好的,这可能是正确的,因为在我看来,我可以将分配给堆栈的内存表示为充满位(或字节)的功能区。所有这些字节都可以由一个特定的内存地址访问,表示为一些十进制值。栈的起点是最低内存地址,栈尾是内存地址的最高值。但是如何,我们如何才能获得指向位于堆栈末尾的 thread_info 结构的指针,仅通过屏蔽位于任意位置的堆栈指针的 13 个最低有效位(如果我理解正确,我们屏蔽了堆栈的位指针 ADDRESS 表示为十进制值)。

【问题讨论】:

  • 提示:2 的 13 次方 = 8192(即堆栈的大小),十进制值 -8192 为十六进制 0xFFFFE000(在 2 补码平台上以 32 位表示。)
  • 您多次说“十进制值” - 您可能最好考虑二进制值,尤其是尝试了解掩码的工作原理。

标签: linux linux-kernel


【解决方案1】:

每个进程仅获得 8192 字节的内核堆栈,与 8192 字节的边界对齐,因此无论何时堆栈指针被 push 或 pop 改变,低 13 位是唯一改变的部分。 2**13==8192.

【讨论】:

    【解决方案2】:

    内核栈在顶部包含一个特殊的结构——thread_info

     26 struct thread_info {
     27         struct task_struct      *task;          /* main task structure */
     28         struct exec_domain      *exec_domain;   /* execution domain */
     29         __u32                   flags;          /* low level flags */
     30         __u32                   status;         /* thread synchronous flags */
     31         __u32                   cpu;            /* current CPU */
     32         int                     preempt_count;  /* 0 => preemptable,
     33                                                    <0 => BUG */
     34         mm_segment_t            addr_limit;
     35         struct restart_block    restart_block;
     36         void __user             *sysenter_return;
     37 #ifdef CONFIG_X86_32
     38         unsigned long           previous_esp;   /* ESP of the previous stack in
     39                                                    case of nested (IRQ) stacks
     40                                                 */
     41         __u8                    supervisor_stack[0];
     42 #endif
     43         unsigned int            sig_on_uaccess_error:1;
     44         unsigned int            uaccess_err:1;  /* uaccess failed */
     45 };
    

    因此,要获取 task_struct,您需要从 ASM 代码中获取带有 GET_THREAD_INFOthread_info 指针:

    183 /* how to get the thread information struct from ASM */
    184 #define GET_THREAD_INFO(reg)     \
    185         movl $-THREAD_SIZE, reg; \
    186         andl %esp, reg
    

    ...或使用 C 代码中的 current_thread_info

    174 /* how to get the thread information struct from C */
    175 static inline struct thread_info *current_thread_info(void)
    176 {
    177         return (struct thread_info *)
    178                 (current_stack_pointer & ~(THREAD_SIZE - 1));
    179 }
    

    请注意,THREAD_SIZE 定义为 (PAGE_SIZE &lt;&lt; THREAD_SIZE_ORDER)THREAD_SIZE_ORDER 对于 x86_32 和 x86_64 都等于 1,因此 THREAD_SIZE 结果为 8192(2^13 或 1

    【讨论】:

      【解决方案3】:

      但是如何,我们如何仅通过屏蔽位于任意位置的堆栈指针的 13 个最低有效位来获得指向位于堆栈末尾的 thread_info 结构的指针

      请注意,底部和限制(顶部)地址(假设自下而上的堆栈,较高的地址位于底部)必须是堆栈大小的倍数。例如,如果堆栈大小为 8192 (=2^13),则 13 个最低有效位对于底部地址和限制地址都必须全部为 0。最低有效的 13 位在某种意义上不是任意的,它给出了底部地址和限制地址之间的偏移量,两者都以 13 个 0 结尾。因此,屏蔽掉最不重要的 13 位可以得到限制地址的地址,也就是 thread_info 结构所在的位置。

      【讨论】:

        【解决方案4】:

        我的 2 位:请注意,“当前”的实现是依赖于架构的。到目前为止的答案集中在 x86 上; Linux OS 上的其他拱门采用了各种获取 thread_info 和 task_struct 的方法。

        例如,显然 PPC 使用一个寄存器(它是 RISC,记住,有很多 GPR)来存储 current 的值 - 实际上将其渲染为硬件上下文的一部分!这将非常快。

        现代 x86 端口(我查找了 4.1.0 内核源代码)使用 per-cpu 数据以快速且无锁的方式实现 current。等等……

        【讨论】:

          猜你喜欢
          • 2016-02-03
          • 1970-01-01
          • 2014-02-17
          • 2012-01-17
          • 2017-04-18
          • 2010-09-25
          • 2021-12-26
          • 2020-03-22
          • 2013-02-01
          相关资源
          最近更新 更多