2012-08-14 103 views
4

现在我正在阅读Robert Love编写的“Linux Kernel Development 3d Edition”一书。在那里,他写了关于thread_info结构,其中包含指向task_struct结构的指针,据我所知,它位于进程内核堆栈的底部或顶部(取决于体系结构)。直到最近我还不熟悉Linux内核API,并且我不知道current()方法的存在。有从与所述书的摘录如何电流()方法的实际工作:了解从进程内核堆栈获取task_struct指针

在x86上,电流是通过掩蔽出栈 指针的13至少-显著比特以获得所述的thread_info structure.This计算是由 current_thread_info()函数完成的。此程序集显示如下: movl $ -8192,%eax andl%esp,%eax 这假定堆栈大小为8KB。启用4KB堆栈时,使用4096在 代替8192.

我的问题是:

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

对于谁会阅读本主题,我所提出的问题可能会导致作者不能正确理解内存分配和管理过程的结论。好吧,这可能是正确的,因为在我看来,我可以表示为堆栈分配的内存作为完整的位(或字节)。所有这些字节都可以通过一个特定的内存地址来访问,这个地址表示为一些十进制值堆栈的起源是最低的内存地址,堆栈的最高位是内存地址的最高值。但是,怎样才能得到指向位于堆栈末端的thread_info结构的指针,只需通过掩盖位于堆栈指针的ARBITRARY的13个最低有效位(如果我理解正确的话,我们可以屏蔽掉堆栈指针ADDRESS以十进制值表示)。

+0

提示:2为13 = 8192的幂(即栈的大小),十进制值-8192为十六进制0xFFFFE000(如2补足平台上的32位所表示的那样)。 – nos 2012-08-14 22:18:03

+0

你几次说“十进制值” - 你可能会更好地考虑二进制值,特别是试图了解掩蔽如何工作。 – mpe 2012-08-15 13:32:48

回答

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你需要得到一个thread_info终场前GET_THREAD_INFO从ASM-代码:

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 

...或与current_thread_info从C代码:

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,THREAD_SIZE定义为(PAGE_SIZE << THREAD_SIZE_ORDER)THREAD_SIZE_ORDER等于1,因此THREAD_SIZE结果为8192(2^13或1 < < 13)。

4

每个进程只获得8192字节的内核堆栈,并对齐到8192字节的边界,所以无论何时通过推送或弹出来改变堆栈指针,低13位是唯一改变的部分。 2 ** 13 == 8192。

0

但是现在,我们如何只能通过屏蔽掉任意位于堆栈指针的13最低显著位拿到指针位于堆栈的,也就是说,年底的thread_info结构

请注意,底部和限制(顶部)地址(假设位于底部的具有较高地址的自底向上堆栈)必须是堆栈大小的倍数。例如,如果堆栈大小为8192(= 2^13),则底部和限制地址都必须全部为13。最不重要的13位在某种意义上不是任意的,它提供了底部地址和限制地址之间的偏移量,它们都以13 0结束。因此,掩盖掉最不重要的13位,给出了限制地址的地址,这是thread_info结构所在的位置。

0

我的2位:请注意,'current'的实现是依赖于arch的。到目前为止,答案都集中在x86;在Linux操作系统上,各种获取thread_info以及task_struct的方法被其他arch使用。

例如,显然PPC使用一个寄存器(它是RISC,请记住,有大量的GPR)来存储当前值 - 实际上将其作为硬件环境的一部分!这将会非常快。

现代x86端口(我查找了4.1.0内核源代码)使用per-cpu数据以快速和无锁的方式实现电流。依此类推......