刘柳 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 + [email protected]
所有进程的祖先叫做进程0 在系统初始化阶段由start_kernel()函数从无到有手工创建的一个内核线程 进程0最后的初始化工作创建init内核线程,此后运行cpu_idle,成为idle进程控制权的接力棒从bios-->bootloader-->idle,某种程度上说,就是完成子系统初始化使命后,就退居二线了。
(gdb) bt #0 cpu_idle_loop () at kernel/sched/idle.c:201 #1 cpu_startup_entry (state=<optimized out>) at kernel/sched/idle.c:274 #2 0xc175d22d in rest_init () at init/main.c:418 #3 0xc1a4bb59 in start_kernel () at init/main.c:680 #4 0xc1a4b360 in i386_start_kernel () at arch/x86/kernel/head32.c:49 #5 0x00000000 in ?? ()
static void cpu_idle_loop(void) { while (1) { /*如果本架构下面有标示轮询poll的bit位,我们会保持不变??(理解:始终在这个循环里) 如果idle没有被调度,那么poll bit是被清空的 反过来说, 如果 设置了poll bit,那么need_resched将会保证cpu进行重新调度。 */ __current_set_polling(); tick_nohz_idle_enter(); while (!need_resched()) { check_pgt_cache(); rmb(); if (cpu_is_offline(smp_processor_id())) arch_cpu_idle_dead(); local_irq_disable(); arch_cpu_idle_enter(); /* 在poll mode 中,我们会使能中断 和 自旋锁 同时 如果检测到唤醒(来自一些设备广播的), 我们将努力避免进入深度睡眠,因为我们知道 IPI (???)即将马上来到 */ if (cpu_idle_force_poll || tick_check_broadcast_expired()) cpu_idle_poll(); else cpuidle_idle_call(); arch_cpu_idle_exit(); } /* * Since we fell out of the loop above, we know * TIF_NEED_RESCHED must be set, propagate it into * PREEMPT_NEED_RESCHED. * * This is required because for polling idle loops we will * not have had an IPI to fold the state for us. */ preempt_set_need_resched(); tick_nohz_idle_exit(); __current_clr_polling(); /* * We promise to call sched_ttwu_pending and reschedule * if need_resched is set while polling is set. That * means that clearing polling needs to be visible * before doing these things. */ smp_mb__after_atomic(); sched_ttwu_pending(); schedule_preempt_disabled(); } }
进程1又称为init进程,是所有用户进程的祖先由进程0在start_kernel调用rest_init创建init进程PID为1,当调度程序选择到init进程时,init进程开始执行kernel_init ()函数init是个普通的用户态进程,它是Unix系统内核初始化与用户态初始化的接合点,它是所有用户进程的祖宗。在运行init以前是内核态初始化,该过程(内核初始化)的最后一个动作就是运行/sbin/init可执行文件
asmlinkage __visible void __init start_kernel(void) { ... //初始化0号进程pcb set_task_stack_end_magic(&init_task); ... /* 当只有一个CPU的时候这个函数就什么都不做, 但是如果有多个CPU的时候那么它就 * 返回在启动的时候的那个CPU的号 */ smp_setup_processor_id(); ... /* 关闭当前CPU的中断 */ local_irq_disable(); early_boot_irqs_disabled = true; ... /* 初始化页地址,使用链表将其链接起来 */ page_address_init(); /* 显示内核的版本信息 */ pr_notice("%s", linux_banner); /* * 每种体系结构都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个 * 体系结构的setup_arch()函数,由源码树顶层目录下的Makefile中的ARCH变量 * 决定 */ setup_arch(&command_line); ... /* 打印Linux启动命令行参数 */ pr_notice("Kernel command line: %s\n", boot_command_line); /* 对内核选项的两次解析 */ parse_early_param(); after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, set_init_arg); jump_label_init(); ... /* 初始化hash表,便于从进程的PID获得对应的进程描述符指针 */ pidhash_init(); /* 虚拟文件系统的初始化 */ vfs_caches_init_early(); sort_main_extable(); /* * trap_init函数完成对系统保留中断向量(异常、非屏蔽中断以及系统调用) * 的初始化,init_IRQ函数则完成其余中断向量的初始化 */ trap_init(); mm_init(); /* 进程调度器初始化 */ sched_init(); preempt_disable(); /* 检查中断是否已经打开,如果已经打开,则关闭中断 */ if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n")) local_irq_disable(); ... /* init some links before init_ISA_irqs() */ early_irq_init(); init_IRQ(); tick_init(); rcu_init_nohz(); init_timers(); /* 对高精度时钟进行初始化 */ hrtimers_init(); /* 初始化tasklet_softirq和hi_softirq */ softirq_init(); timekeeping_init(); /* 初始化系统时钟源 */ time_init(); sched_clock_postinit(); perf_event_init(); profile_init(); call_function_init(); WARN(!irqs_disabled(), "Interrupts were enabled early\n"); early_boot_irqs_disabled = false; local_irq_enable(); /* slab初始化 */ kmem_cache_init_late(); /* * 初始化控制台以显示printk的内容,在此之前调用的printk * 只是把数据存到缓冲区里 */ console_init(); if (panic_later) panic("Too many boot %s vars at `%s'", panic_later, panic_param); lockdep_info(); ... /* * CPU性能测试函数,可以计 算出CPU在1s内执行了多少次一个 * 极短的循环,计算出来的值经过处理后得 到BogoMIPS值(Bogo是Bogus的意思), */ calibrate_delay(); pidmap_init(); ... /* 创建init进程 */ rest_init();//66 analysis 0 #, never return ... }
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8+3) //用户段cs 计算出来14*8+3 =0x73 #define GDT_ENTRY_DEFAULT_USER_CS 14 #define __KERNEL_CS (GDT_ENTRY_KERNEL_CS*8)//核心段cs :0x60 #define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE+0) #define GDT_ENTRY_KERNEL_BASE (12)
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp) { set_user_gs(regs, 0); regs->fs = 0; regs->ds = __USER_DS; regs->es = __USER_DS; regs->ss = __USER_DS; regs->cs = __USER_CS; regs->ip = new_ip; regs->sp = new_sp; regs->flags = X86_EFLAGS_IF; /* * force it to the iret return path by making it look as if there was * some work pending. */ set_thread_flag(TIF_NOTIFY_RESUME); }