第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. 使用Typescript重构axios(十三)——让响应数据支持泛型

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  2. 【洛谷】P2371 [国家集训队]墨墨的等式(屠版题)

    先讲讲曲折的思路吧...... 首先,应该是CRT之类的东西,乱搞 不行......打了打草稿,发现有解的情况是gcd(a1,a2.....an)|B,于是可以求gcd然后O(n)查询?但是B的范围直 ...

  3. RHEL7-Vsftpd匿名用户

    实现:匿名用户创建目录,可以上传.下载文件,但是不可删除文件,禁止本地用户登陆. Vsftpd.conf部分参数 第一步:虚拟机挂载镜像 略 第二步:执行挂载命令 略 第三步:编写yum仓库文件 略 ...

  4. 重写equals方法,也应该重写hashcode方法,反之亦然

    yls 2019年11月07日 一方面 hashcode原则:两个对象equals相等,hashcode值一定相等 默认的hashcode是Object类通过对象的内存地址得到的 若重写equals而 ...

  5. Netty处理器重要概念

    1.Netty的处理器可以分为两类:入站处理器和出战处理器 2.入站处理器顶层是ChannelInboundHandler,出战处理器顶层是ChannelOutboundHandler 3.数据处理时 ...

  6. Appium+python自动化(四十)-Appium自动化测试框架综合实践 - 代码实现(超详解)

    1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现.由于时间的关系,宏哥这里用代码给小伙伴演示两个模块:注册和登录. 2.业务模块封装 因为现在各种APP的层出不群 ...

  7. T-SQL Part XII: Access Remote SQL Server

    要链接远程的SQL Server,需要一下几个步骤(以下的步骤都是在远程系统上进行): 确认远程SQL Server所监听的端口号 官方的文档是使用SQL Server Configuration M ...

  8. Spring Boot Actuator监控使用详解

    在企业级应用中,学习了如何进行SpringBoot应用的功能开发,以及如何写单元测试.集成测试等还是不够的.在实际的软件开发中还需要:应用程序的监控和管理.SpringBoot的Actuator模块实 ...

  9. nyoj 811-变态最大值 (max)

    811-变态最大值 内存限制:64MB 时间限制:1000ms 特判: No 通过数:6 提交数:15 难度:1 题目描述: Yougth讲课的时候考察了一下求三个数最大值这个问题,没想到大家掌握的这 ...

  10. Freemarker + xml 实现Java导出word

    前言 最近做了一个调查问卷导出的功能,需求是将维护的题目,答案,导出成word,参考了几种方案之后,选择功能强大的freemarker+固定格式之后的wordxml实现导出功能.导出word的代码是可 ...