1.搭建环境

cd ~/Work/
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
xz -d linux-3.18..tar.xz
tar -xvf linux-3.18..tar
cd linux-3.18.
make i386_defconfig
make
cd ~/Work/
mkdir rootfs
git clone https://github.com/mengning/menu.git # 话说这里为什么用MenuOS 我个人觉得老师一来是节约编译时间 二来也可以做做广告
cd menu
sudo apt-get install libc6:i386 lib32stdc++ # 这两行安装非常有必要
sudo apt-get install lib32readline-gplv2-dev # 在64bit的Ubuntu环境下不能编译这个MenuOS的roofs 需要这些包来支持 即使用了-m32
gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip - > ../rootfs.img
cd ~/Work/
qemu -kernel linux-3.18./arch/x86/boot/bzImage -initrd rootfs.img
sudo apt-get install libncurses5-dev # 保证make menuconfig可用
make menuconfig
kernel hacking->
copile-time checks and compile options
[*] compile the kernel with debug info
qemu -kernel linux-3.18./arch/x86/boot/bzImage -initrd rootfs.img -s -S
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp:: 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

然后打开另一个shell窗口

gdb
(gdb)file linux-3.18./vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote: # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

2.分析start_kernel

可以在http://codelab.shiyanlou.com/xref/linux-3.18.6/init/main.c找到start_kernel所在函数的源代码。

关注需要关注的,删除不必要的代码:

asmlinkage __visible void __init start_kernel(void)
{
//init_task即手工创建的PCB,0号进程即最终的idle进程
set_task_stack_end_magic(&init_task);
//初始化中断向量
trap_init();
//内存管理模块
mm_init(); /*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
//内核启动时一直存在,0号进程创建了1进程和其他服务的内核的进程
rest_init();
}

执行start_kernel时,会对CPU、内存等各种硬件设备进行初始化,并加载非常多的不同内核模块。

rest_init是linux内核初始化进程的函数。如果在它执行之前自行创建我们自己的进程,并且利用自己的调度算法来调度之后创建的进程,那么rest_init则永远不会被执行,因为在它执行之前,自己创建的进程已经在轮转调度不会结束(如mykernel环境的搭建)。

再看rest_init

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); //创建新进程
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);
}

分析上述kernel_thread(kernel_init, NULL, CLONE_FS)中kernel_init部分代码

if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return ;
pr_err("Failed to execute %s (error %d)\n",ramdisk_execute_command, ret);
}

run_init_process是linux系统的1号进程,第一个用户态进程,默认在根目录下,如果根目录下没有继续找/sbin/init/,/bin/init/等来作为1号进程。

分析上述pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES)中kernel_thread代码

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
(unsigned long)arg, NULL, NULL);
}

kernel_thread实际上就是取fork一个线程

分析上述cpu_startup_entry(CPUHP_ONLINE)中cpu_idle_loop()

static void cpu_idle_loop(void)
{
while () {
/*
* If the arch has a polling bit, we maintain an invariant:
*
* Our polling bit is clear if we're not scheduled (i.e. if
* rq->curr != rq->idle). This means that, if rq->idle has
* the polling bit set, then setting need_resched is
* guaranteed to cause the cpu to reschedule.
*/ __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(); /*
* In poll mode we reenable interrupts and spin.
*
* Also if we detected in the wakeup from idle
* path that the tick broadcast device expired
* for us, we don't want to go deep idle as we
* know that the IPI is going to arrive right
* away
*/
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();
}
}

当系统没有进程需要执行时就调度到idle进程

3.回顾总结

rest_init当start_kernel启动时会一直存在(0号进程),再0号进程创建1号进程及其服务的内核进程。最后,会创建idle进程(0号进程),不能被调度,并利用循环来不断调号空闲的CPU时间片,并且从不返回。

构造一个简单的linux系统的更多相关文章

  1. Linux内核设计第三周——构造一个简单的Linux系统

    Linux内核设计第三周 ——构造一个简单的Linux系统 一.知识点总结 计算机三个法宝: 存储程序计算机 函数调用堆栈 中断 操作系统两把宝剑: 中断上下文的切换 进程上下文的切换 linux内核 ...

  2. 第三节 构造一个简单的Linux系统MenuOS——20135203齐岳

    第三节 构造一个简单的Linux系统MenuOS By 20135203齐岳 Linux内核源代码 arch/ 支持不同cpu的源代码 Documentations/ 文档存储 init/ 内核启动相 ...

  3. Linux内核分析第三周学习总结:构造一个简单的Linux系统MenuOS

    韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.Linux内 ...

  4. Linux内核分析— —构造一个简单的Linux系统MenuOS(20135213林涵锦)

    Linux内核分析— —构造一个简单的Linux系统MenuOS 实验内容 Linux内核的启动过程,从start_kernel到init进程启动 使用实验楼的虚拟机打开shell cd LinuxK ...

  5. 《Linux内核分析》第三周笔记 构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS 一.linux内核源代码简介 三大法宝(存储程序计算机.函数调用堆栈.中断)和两把宝剑(中断上下文的切换:保存现场和恢复现场.进程上下文的切换) 1.在lin ...

  6. 20135202闫佳歆--week3 构造一个简单的Linux系统MenuOs--学习笔记

    此为个人学习笔记存档 week 3 构造一个简单的Linux系统MenuOs 复习: 计算机有三个法宝:存储程序计算机,函数调用堆栈,中断 操作系统有两把剑: 1.中断上下文的切换,保存现场和恢复现场 ...

  7. 《Linux内核分析》第三周学习小结 构造一个简单的Linux系统OS

    郝智宇 无转载 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 第三周 构造一个简单的Linux系统Me ...

  8. Linux内核分析-构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS linux内核目录结构 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel C ...

  9. 20135220谈愈敏Blog3_构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1 ...

  10. 第三周:构造一个简单的LINUX系统MENUOS

    吕松鸿 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.Linux内 ...

随机推荐

  1. Learning Puppet — Manifests

    Begin In a text editor — vim, emacs, or nano — create a file with the following contents and filenam ...

  2. 黄聪:WordPress 多站点建站教程(五):获取子站点用户信息(通过输入站点ID号来获取该站点的所有用户)

    得到站点ID为1的用户 <ul> <?php $blogusers = get_users('blog_id=1'); foreach ($blogusers as $user) { ...

  3. DBA_Oracle Startup / Shutdown启动和关闭过程详解(概念)

    2014-08-07 Created By BaoXinjian

  4. c#复习整理

    一.基本语法 1.数据类型 整数类型:int.long 浮点类型:float.double.decimal 布尔类型:bool 字符串类型:string 2.类型转换 int a; double b ...

  5. Apache2 Axis2/C 搭建 hello world

    参考 http://www.cnblogs.com/fjchenqian/archive/2012/08/05/2623601.html http://www.cnblogs.com/ezhong/a ...

  6. 在VMware虚拟机中配置DOS汇编开发环境!!

    操作系统:win7 32位 DOS环境:DosBox  下载:http://www.dosbox.com/ 选择当前适合自己版本,下载就可以了. 汇编编译器:MASM 5.0 下载:http://do ...

  7. RESTful Api 身份认证中的安全性设计探讨

    REST 是一种软件架构风格.RESTful Api 是基于 HTTP 协议的 Api,是无状态传输.它的核心是将所有的 Api 都理解为一个网络资源.将所有的客户端和服务器的状态转移(动作)封装到 ...

  8. mac下Android开发环境搭建

    之前一段时间在学习ios的开发,近一段时间想着也接触下Android开发,以来加深对移动端开发的理解.这里根据自己配置Android开发环境的过程,比较详细的来总结下自己的安装过程,希望对一些正准备配 ...

  9. SQL 锁的介绍

    锁的概述 一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 脏 ...

  10. log4j 缓存

    log4j.appender.A2.bufferedIO=truelog4j.appender.A2.bufferSize=512000