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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当然,我可以为您提供一个简单的 Linux 进程调度算法的模拟代码。下面是一个示例: ```python import random # 定义进程类 class Process: def __init__(self, pid, arrival_time, burst_time): self.pid = pid self.arrival_time = arrival_time self.burst_time = burst_time self.waiting_time = 0 def run(self): self.burst_time -= 1 def is_finished(self): return self.burst_time == 0 # 定义模拟函数 def simulate(processes): current_time = 0 total_waiting_time = 0 total_processes = len(processes) completed_processes = [] while len(completed_processes) < total_processes: available_processes = [p for p in processes if p.arrival_time <= current_time and p not in completed_processes] if available_processes: # 使用随机算法选择下一个要运行的进程 next_process = random.choice(available_processes) next_process.run() if next_process.is_finished(): completed_processes.append(next_process) total_waiting_time += next_process.waiting_time else: for p in available_processes: if p != next_process: p.waiting_time += 1 current_time += 1 average_waiting_time = total_waiting_time / total_processes return average_waiting_time # 创建进程列表 processes = [ Process(1, 0, 5), Process(2, 2, 3), Process(3, 4, 4), Process(4, 6, 2), ] # 运行模拟 average_waiting_time = simulate(processes) print(f"Average waiting time: {average_waiting_time}") ``` 这段代码使用了随机算法来模拟 Linux 进程调度。它创建了一个包含多个进程进程列表,每个进程具有到达时间和执行时间。模拟函数按照进程到达时间和执行时间模拟进程调度,并计算出平均等待时间。 请注意,这只是一个简单的示例,实际的 Linux 进程调度算法要复杂得多。这里的代码只是提供了一个基本的框架,您可以根据需要进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_George

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值