current宏及Linux进程栈的底层实现

1. current宏的实现

#ifndef __ASSEMBLY__struct task_struct;//用于在编译时候声明一个perCPU变量,该变量被放在一个特殊的段中,原型为DECLARE_PER_CPU(type,name),主要作用是为处理器创建一个type类型,名为name的变量。DECLARE_PER_CPU(struct task_struct *, current_task);static __always_inline struct task_struct *get_current(void){return percpu_read_stable(current_task);}#define current get_current()
#endif /* __ASSEMBLY__ */
  • 继续跟踪percpu_read_stable()个宏函数:位\linux-2.6.32.63\arch\x86\include\asm\percpu.h查得定义:
#define percpu_read_stable(var)	percpu_from_op("mov", per_cpu__##var, "p" (&per_cpu__##var))
  • 继续跟进percpu_from_op()这个函数:
/*
percpu_from_op宏中根据不同的sizeof(var)选择不同的分支,执行不同的流程,因为这里是x86体系,所以sizeof(current_task)的值为4
在每个分支中使用了一条的內联汇编代码,其中__percpu_arg(1)为%%fs:%P1(X86)或者%%gs:%P1(X86_64),将上述代码整理后,current获取代码如下:
1. x86: asm(movl "%%fs:%P1","%0" : "=r" (pfo_ret__) :"p" (&(var)) 
2. x86_64: asm(movl "%%gs:%P1","%0" : "=r" (pfo_ret__) :"p" (&(var)) 
*/
#define percpu_from_op(op, var, constraint)		\
({                            					\typeof(var) ret__;                			\switch (sizeof(var)) {                		\case 1:                        				\asm(op "b "__percpu_arg(1)",%0"        	\: "=q" (ret__)            			\: constraint);            			\break;                    				\case 2:                        				\asm(op "w "__percpu_arg(1)",%0"        	\: "=r" (ret__)            			\: constraint);            			\break;                    				\case 4:                        				\asm(op "l "__percpu_arg(1)",%0"        	\: "=r" (ret__)            			\: constraint);            			\break;                    				\case 8:                        				\asm(op "q "__percpu_arg(1)",%0"        	\: "=r" (ret__)            			\: constraint);            			\break;                    				\default: __bad_percpu_size();            	\}                        					\ret__;                        				\
})
  • 将fs(或者gs)段中P1偏移处的值传送给pfo_ret__变量。

  • 继续跟进per_cpu__kernel_stack的定义。(linux-2.6.32.63\arch\x86\kernel\cpu\common.c)

/*
The following four percpu variables are hot.  Align current_task to
cacheline size such that all four fall in the same cacheline.
*/
DEFINE_PER_CPU(struct task_struct *, current_task) ____cacheline_aligned = &init_task;
EXPORT_PER_CPU_SYMBOL(current_task);DEFINE_PER_CPU(unsigned long, kernel_stack) = (unsigned long)&init_thread_union - KERNEL_STACK_OFFSET + THREAD_SIZE;
EXPORT_PER_CPU_SYMBOL(kernel_stack);DEFINE_PER_CPU(char *, irq_stack_ptr) = init_per_cpu_var(irq_stack_union.irq_stack) + IRQ_STACK_SIZE - 64;DEFINE_PER_CPU(unsigned int, irq_count) = -1;
  • 继续看进程内核栈初始化的关键代码:

DEFINE_PER_CPU(unsigned long, kernel_stack) = (unsigned long)&init_thread_union - KERNEL_STACK_OFFSET + THREAD_SIZE;

//linux-2.6.32.63\arch\x86\kernel\init_task.c
/** Initial task structure.** All other task structs will be allocated on slabs in fork.c*/
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);/** Initial thread structure.** We need to make sure that this is THREAD_SIZE aligned due to the* way process stacks are handled. This is done by having a special* "init_task" linker map entry..*/
union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) 
};
  • \linux-2.6.32.63\include\linux\init_task.h
/**  INIT_TASK is used to set up the first task table, touch at* your own risk!. Base=0, limit=0x1fffff (=2MB)*/
#define INIT_TASK(tsk)    \
{                                    \.state        = 0,                        \.stack        = &init_thread_info,                \.usage        = ATOMIC_INIT(2),                \.flags        = PF_KTHREAD,                    \.lock_depth    = -1,                        \.prio        = MAX_PRIO-20,                    \.static_prio    = MAX_PRIO-20,                    \.normal_prio    = MAX_PRIO-20,                    \.policy        = SCHED_NORMAL,                    \.cpus_allowed    = CPU_MASK_ALL,                    \.mm        = NULL,                        \.active_mm    = &init_mm,                    \.se        = {                        \.group_node     = LIST_HEAD_INIT(tsk.se.group_node),    \},                                \.rt        = {                        \.run_list    = LIST_HEAD_INIT(tsk.rt.run_list),    \.time_slice    = HZ,                     \.nr_cpus_allowed = NR_CPUS,                \},                                \.tasks        = LIST_HEAD_INIT(tsk.tasks),            \.pushable_tasks = PLIST_NODE_INIT(tsk.pushable_tasks, MAX_PRIO), \.ptraced    = LIST_HEAD_INIT(tsk.ptraced),            \.ptrace_entry    = LIST_HEAD_INIT(tsk.ptrace_entry),        \.real_parent    = &tsk,                        \.parent        = &tsk,                        \.children    = LIST_HEAD_INIT(tsk.children),            \.sibling    = LIST_HEAD_INIT(tsk.sibling),            \.group_leader    = &tsk,                        \.real_cred    = &init_cred,                    \.cred        = &init_cred,                    \.cred_guard_mutex =                        \__MUTEX_INITIALIZER(tsk.cred_guard_mutex),        \.comm        = "swapper",                    \.thread        = INIT_THREAD,                    \.fs        = &init_fs,                    \.files        = &init_files,                    \.signal        = &init_signals,                \.sighand    = &init_sighand,                \.nsproxy    = &init_nsproxy,                \.pending    = {                        \.list = LIST_HEAD_INIT(tsk.pending.list),        \.signal = {{0}}},                    \.blocked    = {{0}},                    \.alloc_lock    = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),        \.journal_info    = NULL,                        \.cpu_timers    = INIT_CPU_TIMERS(tsk.cpu_timers),        \.fs_excl    = ATOMIC_INIT(0),                \.pi_lock    = __SPIN_LOCK_UNLOCKED(tsk.pi_lock),        \.timer_slack_ns = 50000, /* 50 usec default slack */        \.pids = {                            \[PIDTYPE_PID]  = INIT_PID_LINK(PIDTYPE_PID),        \[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID),        \[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),        \},                                \.dirties = INIT_PROP_LOCAL_SINGLE(dirties),            \INIT_IDS                            \INIT_PERF_EVENTS(tsk)                        \INIT_TRACE_IRQFLAGS                        \INIT_LOCKDEP                            \INIT_FTRACE_GRAPH                        \INIT_TRACE_RECURSION                        \INIT_TASK_RCU_PREEMPT(tsk)                    \
}
  • 继续跟进和进程信息密切相关的数据结构:
    • 进入\linux-2.6.32.63\include\linux\sched.h
/*
THREAD_SIZE在32位平台上一般定义为4K,所以stack的大小其实就是4KB,这就是初始任务在核心里所拥有的所有空间,除去thread_info和KERNEL_STACK_OFFSET占用的空间后,就是任务在核心里实际拥有堆栈的大小。KERNEL_STACK_OFFSET定义为5*8,由于是unsigned long,所以堆栈底部以上还有5*8*4B=200B的空间用来存放程序运行时相关的环境参数
*/
union thread_union 
{struct thread_info thread_info;unsigned long stack[THREAD_SIZE/sizeof(long)];
};
  • 小结
    1. 在linux中,整个内核栈是所有进程公用的,每个进程会像切蛋糕一样从中切去一份指定大小(THREAD_SIZE)的内存区域;
    2. 每个进程都在当前内核栈中分配一段内存区域( thread_union),这段内核栈内存被分为两个部分:
      • 低地址部分保存的: thread_info 。
      • 剩余的高地址部分保存当前进程的内核栈stack。
    3. thread_info当中就保存着当前进程的信息,所以我们可以从本质上理解,current宏其实并不神秘,它就是在做一个内存栈上的取地址操作
  • 拓展链接:
    • http://www.pagefault.info/?p=36
    • http://www.cnblogs.com/justinzhang/archive/2011/07/18/2109923.html


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部