在linux 内核中有一宏 : current (获取当前正在运行的进程)
linux 2.4.0
点击(此处)折叠或打开
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()
union task_union {
struct task_struct task;
unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
对于linux 2.6之前的内核采用的模式是:进程的PCB和进程的系统空间一块分配。对于linux2.4 来讲给进程分配系统空间和PCB用到的结构体是 union task_union .
如图: esp ------------------------------ 高地址
| 系统 |
| 空间 |
| |
| |
| -----------------------------|
| task_struct |
PCB |------------------------------ 低地址
进程的创建是在内核态的,在内核态需要用到栈和PCB ,为了方便管理将PCB和系统空间一块分配了两个连续的页面(8k),其中PCB大约为1K,进程的系统空间大约是7K。但是每次切换到内核态进程的系统栈都是空的。对于 struct task_union 分配空间都是8k的整数倍,因此在每个struct task_union首地址的后13位都是0 。因此一个进程的PCB的地址和该进程的系统的 esp 的地址除低13位都相等-------------->sp 可以移动的范围实在以PCB 为基址的条件下,以8K为跨度的偏移。
因此可以将esp 和 0xffffffe0(地址为32位)的值进行相与,得到struct task_struct 的地址。
因为对于系统的地址范围不缺定,因此内核使用: (~8191UL)
8191------>1 1111 1111 1111 B linux 2.6.0
点击(此处)折叠或打开
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 cpu;
mm_segment_t addr_limit;
__s32 preempt_count;
struct restart_block restart_block;
};
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~0x1fff);
}
static inline struct task_struct * get_current(void)
{
return current_thread_info()->task;
}
#define current get_current() 从linux 2.6 开始之后内核代码有了很大的改版,因为进程的PCB中要存储的东西又增加了很多,由于进程的系统空加不能动态变化,因此是的进程的系统空间足够使用,将PCB中的一部分信息进行提取(thread_info)取代2.4内核的PCB,使得系统空间和thread_info 一块分配。
如图: esp ------------------------------ 高地址
| 系统 |
| 空间 |
| |
| |
| -----------------------------|
| thread_info |------------> -------------------------
|------------------------------ 低地址 | PCB |
| |
--------------------------
同2.4内核一样先获得thread_info 的地址。(加粗下划线)然后根据thread_info 的结构体获得进程的pid的struct task_struct 地址。
注:0x1fff = 8191