5

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

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

我的问题是:

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

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

4

4 回答 4

4

每个进程只获得 8192 字节的内核堆栈,与 8192 字节的边界对齐,因此每当堆栈指针被压入或弹出更改时,低 13 位是唯一更改的部分。2**13==8192。

于 2012-08-14T22:13:47.580 回答
3

内核栈在顶部包含一个特殊的结构——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 }

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

于 2012-08-16T23:00:10.117 回答
1

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

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

于 2015-09-17T15:33:15.917 回答
0

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

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

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

于 2015-11-10T07:35:36.403 回答