内核如何知道当前线程是什么?

16

有人能解释一下这段代码吗?它来自于这里,是从Linux内核中获取的。

/*
  * how to get the thread information struct from C
 */
 static inline struct thread_info *current_thread_info(void) __attribute_const__;

 static inline struct thread_info *current_thread_info(void)
 {
        register unsigned long sp asm ("sp");
        return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}

问题:

  1. __attribute_const__是什么?
  2. register unsigned long sp asm ("sp");这是做什么的?
  3. 为什么(struct thread_info *)(sp & ~(THREAD_SIZE - 1));会返回指向结构体的指针?

1
const - http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html,sp - 是一个堆栈指针寄存器。 - bobah
2个回答

15
  1. const属性表示返回的指针在整个程序运行期间将保持不变。实际上,这仅在一个线程的范围内是正确的,但我想不出编译器会尝试优化跨线程访问的情况。

  2. 使用registerasm(“sp”)可以将变量绑定到硬件寄存器sp(即当前堆栈指针)。这样,代码就不必以汇编语言直接访问该寄存器。

  3. THREAD_SIZE是一个常数,表示为线程堆栈分配的内存量。我认为它总是必须是2的幂次方,例如8千字节可能是一个典型的值。

    表达式~(THREAD_SIZE-1)则给出了一个掩码,以去除实际的堆栈地址。对于8 KB堆栈,它将是0xffffe000

    通过将堆栈指针值与位与,我们可以获得为堆栈分配的最低地址。在这种架构上,线程信息存储在那里。这只是一种设计决策,他们可以使用其他地方来存储信息。

    堆栈指针对于获取线程信息很有用,因为每个线程总是有自己的堆栈。


8
在Linux中,内核栈有固定的大小(THREAD_SIZE-2页或在x86上为8KB)。线程的struct thread_info被保存在堆栈内存块的底部。请记住,堆栈向下工作,因此堆栈指针最初指向内存块的末尾,随着数据推入堆栈,堆栈指针向内存块的底部移动。当然,其他CPU架构可能使用其他技术。
因此,如果您获取当前堆栈指针值,并掩码处理低阶位,则会获得使用当前堆栈的线程的struct thread_info指针。
该行代码:
register unsigned long sp asm ("sp");

告诉GCC将变量sp映射到CPU寄存器sp(对我来说,使用16位寄存器名称似乎很奇怪 - 这是来自实际的Linux源代码树吗?)。

__attribute_const__通常在GCC编译器下定义为__attribute__((__const__))(在Linux内核中有其他编译器吗?)。这告诉GCC函数没有副作用 - 实际上它比这更强大:函数仅使用参数并返回基于这些参数的值。这可能为编译器提供了一些优化机会 - 它可以假设没有全局变量被更改,甚至被读取(因此编译器可以自由地推迟更新它可能需要更新的内存,以进行“正常”函数调用)。


这是ARM,不是x86;这就是为什么它是sp而不是esp。否则你的分析是正确的。 - jpa
那时候是R13。你可以打错成R14或R15,然后就会有各种有趣的事情发生;-) - Steve Jessop
“sp”在ARM汇编中是“r13”的同义词。我想这行代码完全没有任何效果。实际上,整个函数可能会被编译成一个单周期的ARM指令。 - marko
@MichaelBurr 对于Linux内核来说,它还有其他的选择吗? 我相信至少有一个Linux发行版现在是使用Clang构建的。对于那些不关心开源政治的人来说,这将是一个越来越有吸引力的选择。 - marko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接