第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. (转载)学校搭建使用nginx同时编译rtmp-module进行直播的技术文档

    原文地址:学校搭建使用 nginx 同时编译 rtmp-module 进行直播的技术文档 转载自我的大佬同学 MetalkgLZH.学校有几次需要全校观看网络直播的情况,但是学校的带宽不允许所有的班一 ...

  2. C/C++顺序数据结构——动态数组测试

    这是一篇顺序表数据结构——动态数组的测试, 实现 //初始化数组 //插入 //根据位置删除 //根据值删除 //查找 //打印 //释放动态数组的内存 //清空数组 //获得动态数组容量 //获得动 ...

  3. JSP——九大隐藏对象之四大域对象

    你一定在你的Jsp文件中的监本片段中使用过以下九个对象的几种:out.config.page.pageContext.exception.request.response.application.se ...

  4. [转载]2.1 UiPath条件判断活动If的介绍和使用

    一.if的介绍 if语句是指编程语言(包括c语言.C#.Python.Java.汇编语言等)中用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一. 二.if在UiPath ...

  5. 创建linux系统下的虚拟机

    1.打开VMware软件 2.创建新的虚拟机 3.下一步 4.点击 稍后安装操作系统——下一步 5.选择   其中版本 我的镜像是这个版本所以选择这个 6.下一步  设置虚拟机名称  要保存的位置 7 ...

  6. 迁移桌面程序到MS Store(11)——应用SVG图标

    在传统桌面程序中,对图标的使用大多是直接嵌入JPG或者PNG的图片.在祖传的1366x768分辨率下,并没有什么问题.相对于手机硬件的突飞猛进,也侧面反映了PC行业的落寞和桌面程序开发的不思进取.用3 ...

  7. map的线程安全问题

    为什么HashMap是线程不安全的 总说 HashMap 是线程不安全的,不安全的,不安全的,那么到底为什么它是线程不安全的呢?要回答这个问题就要先来简单了解一下 HashMap 源码中的使用的存储结 ...

  8. 深入理解计算机系统 第九章 虚拟内存 Part1 第二遍

    这次花了4小时40分钟,看了第 559~575 页,共 17 页 第一遍对应地址 https://www.cnblogs.com/stone94/p/10264044.html 注意:本章的练习题一定 ...

  9. 【Java】面向对象之多态

    生活中,比如动物中跑的动作,小猫.小狗和大象,跑起来是不一样的.再比如飞的动作,昆虫.鸟类和飞机,飞起来也是不一样的.可见,同一类的事物通过不同的实际对象可以体现出来的不同的形态.多态,描述的就是这样 ...

  10. 深度剖析Javascript执行环境、作用域链

    一.执行环境 执行环境(也叫做执行上下文,Execution Context)是Javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问其他数据,决定了它们各自的行为.每个执行环境都 ...