第3章  MenuOS的构造

1  Linux内核源代码简介

       计算机的“3大法宝”:存储程序计算机、函数调用堆栈和中断。

操作系统的“两把宝剑”:一把是中断上下文的切换——保存现场和恢复现场;另一把是进程上下文的切换。

Linux内核源码目录如下图所示:

       

其中可以把内核源代码目录分为系统最核心组件和系统次核心组件。

系统最核心组件包括:

 arch目录:该目录是与体系结构相关的子目录列表,里面存放了许多CPU体系结构的相关代码,比如arm、x86、MIPS、PPC等。该目录中的代码在Linux内核代码中占比相当庞大,主要原因是arch目录中的代码可以使Linux内核支持不同的CPU和体系结构。alpha、arm、arm64等不同目录分别支持不同的CPU。

 include目录:这个目录包含了Linux源代码目录中绝大部分头文件,每个体系结构都在该目录下对应一个子目录,该子目录中包含了给定体系结构所必需的宏定义和内联函数。

  init目录:该目录中存放的是系统核心初始化代码,内核初始化入口函数start_kernel就是在该目录中的文件main.c内实现的。

kernel目录:该目录中存放的是Linux内核的最核心代码,用于实现系统的核心模块,这些模块包括进程管理、进程调度器、中断处理、系统时钟管理和同步机制等。

scripts目录:该目录中不包含任何核心代码,该目录下存放了用来配置内核的脚本和应用程序源码。

lib目录:该目录主要包含两部分内容: gnuzip解压缩算法,用于在系统启动过程中将压缩的内核镜像解压缩;剩余的文件用于实现-一个C库的子集,主要包括字符串和内存操作等相关函数。

mm目录:该目录包含了体系结构无关的内存管理代码,包括通用的分页模型的框架、伙伴算法的实现和对象缓冲器slab的实现代码。

系统次核心组件包括:

Documentation目录:存放了与内核相关的文档。

block目录:用于实现块设备的基本框架和块设备的I/O调度算法。

crypto目录:该目录中存放了相关的加密算法的代码。

driver目录:用于存放各类设备的驱动程序。

net和fs目录:包含linux内核支持的众多网络协议和文件系统。

ipc目录:该目录中的文件用于实现System V的进程间通信模块。

 sound目录:存放了声音系统架构,如Open Sound System(OSS)、Advanced LinuxSound Architecture(AL SA)的相关代码和具体声卡的设备驱动程序。

security目录:存放了Security-Enhanced Linux(SELinux)安全框架的实现代码。

usr目录:该目录中的代码为内核尚未完全启动时执行用户空间代码提供了支持。

2  构造一个简单的Linux内核

使用实验楼的虚拟机打开shell,输入以下命令:

 cd ~/LinuxKernel/
qemu -kernel linux-3.18./arch/x86/boot/bzImage -initrd rootfs.img

上述代码分析:

qemu:启动已经安装在系统中的相当于虚拟机的程序qemu,这个程序为内核的启动提供一个上下文环境。

-kernel  文件名+路径:启动内核,内核经过编译之后形成一个名为init的文件,之前已经将其拷贝到rootfs文件目录下,并通过cpio的方式将rootfs下的文件打包成一个名为roofs.img的镜像文件。

-initrd rootfs.img:指定rootfs为为启动时的硬件驱动。

经过以上代码之后,rootfs.img会找到init这个可执行文件,init又是由MenuOS这个内核源代码编译而来,由此构建的Linux系统MenuOS截图如下所示:

     

输入help可见其只有三条命令help、version、quit,如下图所示:

3  跟踪调试Linux内核的启动过程

       使用gdb跟踪调试内核

qemu -kernel linux-3.18./arch/x86/boot/bzImage -initrd rootfs.img -s -S

关于-s和-S选项的说明:

-S:CPU初始化之前冻结起来(可使用‘C’继续执行);

-s:在1234端口上创建了一个gdb-server,可以另外打开一个窗口,用gdb把带有符号表的内核镜像加载进来,然后连接server,设置断点跟踪内核。若不想使用1234端口,可以使用-gdb tcp:xxxx来取代-s选项。

用上面的命令先把内核启动一下,可以看到被冻结起来了,代码没有被运行,如下图所示:

再另外打开一个shell窗口,用Ctrl+Shift+O实现水平分割,启动gdb,把内核加载进来,建立连接。

 file linux-3.18./vmlinux
target remote:
break start_kernel

在此之前内核一直是stop状态,如果按“c”则继续执行,系统开始启动,并启动到start_kernel函数的位置停在断点处,如下图所示:

再设置一个断点rest_init,继续执行,停在断点处,如下图所示:

可以看到rest_init是在start_kernel的尾部进行调用的。

start_kernel()函数的部分代码如下图所示:

  asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes; /*
506 * Need to run as early as possible, to initialize the
507 * lockdep hash:
508 */
lockdep_init();
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
............. /* Do the rest non-__init'ed, we're now alive */
rest_init();
}

C语言代码是从main函数启动的,C程序的阅读也从main函数开始。init目录中的main.c源文件是整个Linux内核启动的起点,但它的起点不是main函数,因为main.c中没有main函数,start_kernel()相当于C语言中的main函数,几乎涉及了内核的所有模块,如:trap_init()(中断向量的初始化)、mm_init()(内存管理模块的初始化)、sched_init()(调度模块的初始化)等。start_kernel是一切的起点,不论分析内核的哪一部分都会涉及到该函数,因为基本上所有模块的初始化都在main.c的start_kernel来调用。set_task_stack_end_magic(&init_task)设置第一个进程(pid=0)。

rest_init函数的部分代码为:

  static noinline void __init_refok rest_init(void)
{
int pid; rcu_scheduler_starting();
/*
399 * We need to spawn init first so that it obtains pid 1, however
400 * the init task will end up wanting to create kthreads, which, if
401 * we schedule it before we create kthreadd, will OOPS.
402 */
kernel_thread(kernel_init, NULL, CLONE_FS);/*创建pid=1的进程*/
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
........ /*
412 * The boot idle thread must execute schedule()
413 * at least once to get things moving:
414 */
init_idle_bootup_task(current);
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);/*注意这条语句,pid=0的进程变为了idle进程*/
}

rest_init这是Linux内核初始化的尾声, kernel_thread(kernel_init, NULL, CLONE_FS)创建一个进程(pid=1)。

cpu_idle_loop函数的部分代码为:

  static void cpu_idle_loop(void)
{
while () { ............
}
} void cpu_startup_entry(enum cpuhp_state state)
{
/*
259 * This #ifdef needs to die, but it's too late in the cycle to
260 * make this generic (arm and sh have never invoked the canary
261 * init for the non boot cpus!). Will be fixed in 3.11
262 */
...........
arch_cpu_idle_prepare();
cpu_idle_loop();
}

cpu_idle_loop是一个无限循环的函数,pid=0的进程(idle进程)根本就不会结束,当没有别的任务,该进程就被调用。

4  总结

Linux内核启动过程为:最初执行的进程即是0号进程init_task,它是在系统初始化阶段由start_kernel()函数从无到有手工创建的一个内核线程,进程0在创建1号内核线程kernel_init后,调用cpu_idle()成为idle进程,而idle进程就是当系统没有进程需要执行的时候来调度用的。1号内核进程负责执行内核的部分初始化工作及进行系统配置,然后使用kernel_thread(kernel_init, NULL, CLONE_FS)函数(也就是fork方式)建立了pid=1的1号进程,也叫init进程(用户态1号进程),成为系统中的其他所有进程的祖先,当调度程序选择到init进程时,init进程继续完成剩下的初始化工作。然后调用kernel_thread执行kthreadd,创建PID为2的内核线程,这一进程始终运行在内核空间,负责所有内核线程的调度和管理。

2019-2020-4 20199317《Linux内核原理与分析》第四周作业的更多相关文章

  1. 2019-2020-1 20199303<Linux内核原理与分析>第二周作业

    2019-2020-1 20199303第二周作业 1.汇编与寄存器的学习 寄存器是中央处理器内的组成部份.寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令.数据和位址.在中央处理器的控制部件中 ...

  2. 20169219 linux内核原理与分析第二周作业

    "linux内核分析"的第一讲主要讲了计算机的体系结构,和各寄存器之间对数据的处理过程. 通用寄存器 AX:累加器 BX:基地址寄存器 CX:计数寄存器 DX:数据寄存器 BP:堆 ...

  3. 2019-2020-1 20199314 <Linux内核原理与分析>第二周作业

    1.基础学习内容 1.1 冯诺依曼体系结构 计算机由控制器.运算器.存储器.输入设备.输出设备五部分组成. 1.1.1 冯诺依曼计算机特点 (1)采用存储程序方式,指令和数据不加区别混合存储在同一个存 ...

  4. Linux内核原理与分析-第一周作业

    本科期间,学校开设过linux相关的课程,当时的学习方式主要以课堂听授为主.虽然老师也提供了相关的学习教材跟参考材料,但是整体学下来感觉收获并不是太大,现在回想起来,主要还是由于自己课下没有及时动手实 ...

  5. 2019-2020-1 20199314 <Linux内核原理与分析>第一周作业

    前言 本周对实验楼的Linux基础入门进行了学习,目前学习到实验九完成到挑战二. 学习和实验内容 快速学习了Linux系统的发展历程及其简介,学习了下的变量.用户权限管理.文件打包及压缩.常用命令的和 ...

  6. Linux内核原理与分析-第二周作业

    写之前回看了一遍秒速五厘米:如果

  7. 20169219linux 内核原理与分析第四周作业

    系统调用 系统调用是用户空间访问内核的唯一手段:除异常和陷入外,它们是内核唯一的合法入口. 一般情况下,应用程序通过在用户空间实现的应用编程接口(API)而不是直接通过系统调用来编程. 要访问系统调用 ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 2019-2020-1 20199329《Linux内核原理与分析》第十三周作业

    <Linux内核原理与分析>第十三周作业 一.本周内容概述 通过重现缓冲区溢出攻击来理解漏洞 二.本周学习内容 1.实验简介 注意:实验中命令在 xfce 终端中输入,前面有 $ 的内容为 ...

  10. 2019-2020-1 20199329《Linux内核原理与分析》第十二周作业

    <Linux内核原理与分析>第十二周作业 一.本周内容概述: 通过编程理解 Set-UID 的运行机制与安全问题 完成实验楼上的<SET-UID程序漏洞实验> 二.本周学习内容 ...

随机推荐

  1. MIT线性代数:5.转置,置换,向量空间

  2. NOIP模拟 4

    T1没开longlong T2忘了有向... T3是个好题,可以说将复杂度从N^2优化到NlogN是一个质的飞跃 考虑分治(要想出log可不就要分治么!(segtree也行 但我不会) 对于一个分治区 ...

  3. Linux学习(推荐学习资源)——保持更新

    1. 介绍 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的Unix工具软件.应用程序和网络协议. ...

  4. canvas线条实践之运动的正方形

    原理说明: 1.通过rect实现正方形的绘制: 2.save保存canvas面板的保存,restore回复保存的canvas面板到初始状态: 3.translate用于改变canvas坐标的起始位置: ...

  5. 推荐Java五大微服务器及其代码示例教程

    来源素文宅博客:http://blog.yoodb.com/yoodb/article/detail/1339 微服务越来越多地用于开发领域,因为开发人员致力于创建更大,更复杂的应用程序,这些应用程序 ...

  6. Kubernetes概述

    1. Kubernetes是什么 Kubernetes是一个可移植的.可扩展的.用于管理容器化工作负载和服务的开源平台,它简化(促进)了声明式配置和自动化.它有一个庞大的.快速增长的生态系统.Kube ...

  7. 【Spring】Spring的定时任务注解@Scheduled原来如此简单

    1 简介 定时任务的实现非常多,JDK的Timer.Spring提供的轻量级的Scheduled Task.QuartZ和Linux Cron等,还有一些分布式的任务调度框架.本文主要介绍Schedu ...

  8. vue项目iframe的传值问题

    前言 项目需要,我需要引入一个已经封装好的浏览器插件.插件只能以html的方式调用, 所以.我把插件的使用封装了一个html页面.vue项目则利用iframe的方式引入. 到这里我就遇到了一个问题,那 ...

  9. VS2017,遇到异常:这可能是由某个扩展导致的

    网上看的解决办法没有解决,干脆自己亲自动手搞吧! 具体问题如下: 解决方案: 按照提示路径打开日志文件定位问题所在,打开之后,拉倒最后看到如下图所示: 我的问题是因为安装了一个叫 "Clau ...

  10. hdu 1509 Windows Message Queue (优先队列)

    Windows Message QueueTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Oth ...