Linux内核中current_thread_info函数的实现原理是怎样的?

摘要:Linux 3.2 current_thread_info 函数 前言 current_thread_info, 这个函数在内核中, 经常被用于访问当前CPU正在运行的任务, 那么它的底层是怎么实现的呢? 这是我阅读 LKD 遇到的第一个难
Linux 3.2 current_thread_info 函数 前言 current_thread_info, 这个函数在内核中, 经常被用于访问当前CPU正在运行的任务, 那么它的底层是怎么实现的呢? 这是我阅读 LKD 遇到的第一个难点, 也是我第一次体会到 "纸上得来终觉浅, 绝知此事要躬行" 的点. 关于 Linux 3.2 进程模型, 在 copy_process 中已有记载. 1.让我们来看看, LKD 对此是怎么写的 LKD对此的描述如下 对, 不就是获取RSP, 然后去掉13位吗? 这有什么难的, 那不是只需要 rsp & ~(8192-1) 不就好了吗? 带着这个思路, 我打开了 thread_info.h... 2.但是, Linux 3.2 的源代码呢? 然而, 在Linux 3.2中, 代码是这样写的 static inline struct thread_info *current_thread_info(void) { struct thread_info *ti; ti = (void *)(percpu_read_stable(kernel_stack) + KERNEL_STACK_OFFSET - THREAD_SIZE); return ti; } 相信不止是我有这样的感受吧: 这什么鬼? 这percpu又是什么鬼? 为什么还要加加减减的? 和我在书上看到的完全不一样啊! 别急, 我们先拆分一下这段代码, 让它更清晰易懂: static inline struct thread_info *current_thread_info(void) { void* kstack = (void *)percpu_read_stable(kernel_stack); struct thread_info *ti; ti = kstack + KERNEL_STACK_OFFSET - THREAD_SIZE; return ti; } 3. percpu 机制 3.1 percpu 含义 percpu, 顾名思义, 每个cpu. 众所周知, 现代的 CPU 其实就是一个大公司, 每个核心相当于每个牛马, 操作系统相当于主管. 那我们这些在玩黑公司: 打工的牛马, 也有自己的隐私, 也就是说, 一个牛马不能访问其他牛马独有的资料和文件, 保证数据安全. 同时, 公司也有一些数据是公共的, 每个人都可以访问. 对, cpu核心也是一样的, cpu核心也有属于自己的数据, 和每个核心都能访问到的公共数据. 那问题来了, cpu核心怎么知道哪些数据是自己的, 哪些数据是公共的呢? 这些数据存储在哪? 如何保证隔离? 3.2 x86_64的分段模式 在 x86 中, 段寄存器存储的是段选择子. 那你可能会想, x86_64 就是 x86 的扩展嘛. 那分段也总和x86一样吧. N O! x86_64的长模式, 可谓是差不多快把分段这玩意给废了, 主要用的是平坦模型+分页模式. 更具体的来说, x86_64强制CS, DS, ES三个段寄存器的值为0(当然, 还有一种情况不是0, 那就是 x86 兼容模式. 向下兼容这块没得说). FS GS 存储的值仍然是段选择子(当然, 允许是0), 但是在长模式下, 段选择子仅仅用于检查特权级, 它的基址字段是不起作用的. 那么, 在长模式下, CPU 怎么计算地址呢? 段寄存器是CS DS ES的情况下, 计算地址的时候直接忽略掉这些段寄存器. 然而 FS GS 寄存器的情况有些不同. 在 每个CPU核心 (注意每个, 下面要考) 中有个区域叫 MSR, 这个区域中有两个字段分别叫做 MSR_GS_BASE 和 MSR_FS_BASE, CPU 在计算基址的时候, 会加上这两个字段存储的值, 也就是说假设有如下代码 mov ecx, 0xC0000101 mov eax,0x10 mov edx,0x0 wrmsr ;以上是操作 MSR 寄存器的汇编代码, 将 MSR_GS_BASE 的值设置为 0x00000010. mov rax,qword gs:[0x1234] 那 CPU 会从 0x00001244 处获取数据. 3.3 percpu_read_stable 的含义 OK, 现在让我们看看这个函数. 这个函数的作用就是读取每个CPU独有的变量. 让我们看看 percpu_read_stable 宏展开时候的样子 ({ typeof(kernel_stack) pfo_ret__; switch (sizeof(kernel_stack)) {
阅读全文