了解从进程内核堆栈获取task_struct指针

时间:2012-08-14 22:03:28

标签: linux linux-kernel

现在我正在阅读Robert Love撰写的“Linux内核开发3D版”一书。在那里他写了关于thread_info结构,其中包含指向task_struct结构的指针,据我所知,它位于进程内核堆栈的底部或顶部(取决于体系结构)。直到最近我才熟悉Linux内核API,并且我不知道current()方法的存在。本书的摘录与current()方法的实际工作方式有关:

  

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

我的问题是:

  1. 据我所知,如果我们将十进制值表示为一组位,那么集合中只有一个最低有效位,不是吗?
  2. 什么是神奇的数字13?
  3. 对于将阅读此主题的人,我所提出的问题可以得出结论,即作者不能正确理解内存分配和管理的过程。好吧,这可能是正确的,因为在我看来,我可以将为堆栈分配的内存表示为带满位(或字节)。所有这些字节都可以通过特定的存储器地址访问,表示为一些十进制值。堆栈的原点是最低的内存地址,堆栈的fin是内存地址的最高值。但是,我们如何才能获得指向位于堆栈末端的thread_info结构的指针,只需通过屏蔽13个位于堆栈指针的ARBITRARY的最低有效位(如果我理解正确的话,我们屏蔽掉了堆栈指针ADDRESS表示为十进制值。)

4 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分: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代码中获取thread_info GET_THREAD_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,THREAD_SIZE定义为(PAGE_SIZE << THREAD_SIZE_ORDER)THREAD_SIZE_ORDER等于1,因此THREAD_SIZE结果为8192(2 ^ 13或1 <&lt; 13)

答案 2 :(得分:0)

  

但是,我们如何才能通过屏蔽出位于堆栈指针的ARBITRARY的13个最低有效位来获取指向位于堆栈末端的thread_info结构的指针

请注意,底部和极限(顶部)地址(假设自下而上的堆栈,地址位于底部)必须是堆栈大小的倍数。例如,如果堆栈大小为8192(= 2 ^ 13),则底部和限制地址的13个最低有效位必须全为0。从某种意义上说,最低有效13位是非仲裁的,它给出了底部地址和极限地址之间的偏移量,它们都以13 0结尾。因此,屏蔽最不重要的13位会给出限制地址的地址,这是thread_info结构所在的位置。

答案 3 :(得分:0)

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

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

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