分析Linux启动代码

这是网易云课堂《Linux 内核分析》这门课的作业

先上实验截图

 

实验步骤:

 1 # 下载内核源代码编译内核
 2 cd ~/LinuxKernel/
 3 wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
 4 xz -d linux-3.18.6.tar.xz
 5 tar -xvf linux-3.18.6.tar
 6 cd linux-3.18.6
 7 make i386_defconfig
 8 make # 一般要编译很长时间,少则20分钟多则数小时
 9  
10 # 制作根文件系统
11 cd ~/LinuxKernel/
12 mkdir rootfs
13 git clone  https://github.com/mengning/menu.git
14 cd menu
15 gcc -o init linktable.c menu.c test.c -m32 -static –lpthread
16 cd ../rootfs
17 cp ../menu/init ./
18 find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
19  
20 # 启动MenuOS系统
21 cd ~/LinuxKernel/
22 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

先看看几个重要的Linux代码文件夹结构:

/linux-3.18.6/arch 支持不同CPU的源代码

/linux-3.18.6/arch/x86 X86体系的代码

/linux-3.18.6/drivers 驱动相关

/linux-3.18.6/firmware 硬件相关

/linux-3.18.6/init 启动相关

/linux-3.18.6/ipc  进程通信

/linux-3.18.6/kernel 内核

/linux-3.18.6/lib 库文件

/linux-3.18.6/mm 内存管理

/linux-3.18.6/net 网络

 

内核启动的起点在/linux-3.18.6/init/main.c 的 asmlinkage __visible void __init start_kernel(void) 函数。这个函数非常复杂了,我现在的水平只能走马观花的看看。以后继续深入研究相关模块的时候再回来精读。

asmlinkage __visible void __init start_kernel(void)
{
    lockdep_init(); // 这个函数主要作用是初始化锁的状态跟踪模块。锁的异常状态包括:
// 1. 同一个进程递归地加锁同一把锁。
// 2. 同一把锁在两次中断里加锁。
// 3. 几把锁形成一个闭环死锁。
    set_task_stack_end_magic(&init_task); // 设置一个magic number到初始task的栈底,做栈溢出保护
smp_setup_processor_id(); // 获取当前CPU, 如果是单CPU这个函数是空的
debug_objects_early_init(); // 对调试对象进行早期的初始化
cgroup_init_early(); // 对控制组进行早期的初始化。控制组是用来分配资源用的。
local_irq_disable(); // 关闭中断响应
boot_cpu_init(); // 设置CPU可用, 内核维护了两个bit map, cpu_online_map 和 cpu_possible_map 用来指示当前多少CPU可用和最多多少CPU可用
page_address_init(); // 初始化高地址内存的映射表。这个主要是为32位系统做的,因为32位系统只有4G内存空间,其中内核占用1G空间,如果要访问高于1G空 // 间的内存就需要使用映射表管理。
setup_arch(&command_line); // 这个函数主要作用是对内核架构进行初始化。分析引导程序传入的命令行参数,进行页面内存初始化,处理器初始化,中断早期 // 初始化等等。
setup_command_line(command_line); // 保存命令行
setup_nr_cpu_ids(); // 设置最多有多少个nr_cpu_ids结构
setup_per_cpu_areas(); // 设置每个CPU使用的内存空间
trap_init(); // 初始化中断向量
mm_init(); // 初始化内存
sched_init(); // 调度初始化
rest_init(); // 启动0号和1号进程
}

trap.c下的 void __init trap_init(void) 初始化了各种硬件中断和系统调用,像这样:

set_intr_gate(X86_TRAP_TS, invalid_TSS);

#ifdef CONFIG_X86_32
    set_system_trap_gate(SYSCALL_VECTOR, &system_call);
    set_bit(SYSCALL_VECTOR, used_vectors);
#endif
static noinline void __init_refok rest_init(void)
{
    int pid;

    rcu_scheduler_starting();
    /*
     * We need to spawn init first so that it obtains pid 1, however
     * the init task will end up wanting to create kthreads, which, if
     * we schedule it before we create kthreadd, will OOPS.
     */
    kernel_thread(kernel_init, NULL, CLONE_FS); // 创建1号进程
    numa_default_policy();
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); // 创建内核进程用来管理其它服务线程
    rcu_read_lock();
    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    rcu_read_unlock();
    complete(&kthreadd_done);

    /*
     * The boot idle thread must execute schedule()
     * at least once to get things moving:
     */
    init_idle_bootup_task(current);
    schedule_preempt_disabled();
    /* Call into cpu_idle with preempt disabled */
    cpu_startup_entry(CPUHP_ONLINE); // 进入idle循环
}

 

总结:Linux启动过程会执行start_kernel函数,在此之前都是些硬件准备。在start_kernel里会初始化各种模块,比如内存管理,进程调度等等。然后会初始化和启动1号进程,1号进程就是在根目录名为init的进程,这是第一个用户态进程。随后执行idle循环,等待有新的进程开始切换过去。

刘聪 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

转载于:https://www.cnblogs.com/nmyh/articles/4356869.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值