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

现在我正在阅读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号?

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

Solutions Collecting From Web of "了解从进程内核堆栈获取task_struct指针"

内核堆栈顶部包含一个特殊的结构体 – 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_INFO的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 << 13)。

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

但是,怎样才能得到指向位于堆栈末端的thread_info结构的指针,只需要屏蔽掉位于堆栈指针位置的13个最低有效位

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

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

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

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