解析Linux内核获取当前进程指针的方法

一、内存数据表示:
推荐文章:《linux 进程管理
我们在教材或阅读中,经常需要直观的用图示来展示数据在内存中的分布,那么数据是如何在内存
 <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
中组织的呢?不同的机器有不同的表示法,我们以最常见的
Intel X86 系列计算机为例来说明这个问题。
 
 
如上图示内存示意图:内存低址在上。内存高址在下,内存单位为 16bit 。对于基于 intel i386 架构的计算机,系统采用小端字节序来存放数据,所谓小端字节序是指低序字节低地址,高序字节高地址 ( 内存地址增大方向 ) ,大端字节序反之,给定系统所用的字节序称为主机字节序; CPU 也以小端字节序形式读取数据,如上图所示,如果变量 num 16 位的 short 短整类型,则 CPU 从内存中读出的 num=0x1234 ;如果 num 32 位的 int 类型,则 CPU 从内存中读出的是 num=0x56781234, 其中 num 地址是 0x12345678 ,即 &num=0x12345678
 
二、 linux 内核获取进程任务结构的指针
 
明白了系统内存数据表示,我们现在来看看 linux 内核是如何获取当前进程的任务结构指针的,以下代码均参照 linux 内核 2.4.0 的源码。
 
include/asm-i386/ current.h
#ifndef _I386_CURRENT_H
#define _I386_CURRENT_H
 
struct task_struct;
 
static inline struct task_struct * get_current(void)
{
       struct task_struct *current;
       __asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL));
       return current;
 }
#define current get_current()
 
#endif /* !(_I386_CURRENT_H) */
 
 
每个进程都有一个 task_struct 任务结构,和一片用于系统空间堆栈的存储空间,他们在物理内存空间中也是联系在一起的,当给进程申请 task_struct 任务结构空间时,系统将连同系统的堆栈空间一起分配,如下图为某个进程切换时刻的内存图:
 
 
下面针对代码实现来分析一下系统如何通过一系列操作获取进程在内核中的任务结构指针的:
由于 linux 内核分配进程任务结构空间时,是以 8KB(2 个页面空间,即 2^1*4KB linux 对物理内存空间和虚拟内存空间管理时,均规定其页面单位的尺寸为 4KB) 为单位来分配的,所以内存应用地址是 8KB(2^13) 的整数倍,即指针地址的低 13 位全为 0 ,所以根据小端字节序,分配内存返回地址应该是指向 struct task_struct 结构,如图中的 0xc2342000 地址所指,至于为何采用代码中的做法而不是直接将此指针保存在全局变量中以供应用,内核是从其自身的效率方面来考虑的,我们在此只针对代码解释:
根据上图,此刻内存 esp 内容必定在 0xc2342000 0xc2344000 之间的一个数值,我们假设取 0xc2343ffe( 即堆栈压栈 EIP 、返回地址、内部数据等相关数据了,地址值要减小;只要符合 0xc2342xxx 0xc2343xxx 的地址指针都是正确的 ) ,来通过代码运算看是否 current 的指针是 0xc2342000
__asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL));
语句的意思是将 ESP 的内容与 8191UL 的反码按位进行与操作,之后再把结果赋值给 current ,其中 8191UL=8192-1=2^13-1, 计算过程如下:
                         
8192UL=2^13               0000  0000  0000  0000  0010  0000  0000  0000
8191UL                          0000  0000  0000  0000  0001  1111  1111  1111
~8191UL( 反码 )             1111  1111  1111  1111  1110  0000  0000  0000
0xc2343ffe                     1100  0010  0011  0100  0011  1111  1111  1110
andl 结果:                      1100  0010  0011  0100  0010  0000  0000  0000 
                                                                || ( 对照着看 )
                                          0xc     2         3           4      2       0         0       0
         
 
所以按位与操作之后的结果位 0xc2342000 ,正好是 struct task_struct 结构的地址指针 . 通过观察可知,只要符合 0xc2342xxx 0xc2343xxx 的地址指针经过相同的计算,都可以得到内核进程任务结构的指针。
 
另外,在进入中断或系统调用时所引用的宏操作 (include/asm-i386/ hw_irq.h):
#define GET_CURRENT /
       "movl %esp, %ebx/n/t" /
       "andl $-8192, %ebx/n/t"
  其原理与上述描述也是一致的。
 <script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值