内核的启动从入口函数 start_kernel() 开始。在 init/main.c 文件中,start_kernel 相当于内核的main 函数。打开这个函数,你会发现,里面是各种各样初始化函数 XXXX_init

第一步:在操作系统里面,先要有个创始进程,有一行指令 set_task_stack_end_magic(&init_task)。这里
面有一个参数 init_task,它的定义是 struct task_struct init_task = INIT_TASK(init_task)。它是系
统创建的第一个进程,我们称为0 号进程。这是唯一一个没有通过 fork 或者 kernel_thread 产生
的进程,是进程列表的第一个

第二步:对应的函数是 trap_init(),里面设置了很多中断门(Interrupt Gate),用于处理各种中
断。其中有一个 set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_32),这是系统调用
的中断门。系统调用也是通过发送中断的方式进行的

第三步:初始化的会议室管理系统。对应的,mm_init() 就是用来初始化内存管理模块。

第四步:项目需要项目管理进行调度,需要执行一定的调度策略。sched_init() 就是用于初始化调度模块。
vfs_caches_init() 会用来初始化基于内存的文件系统 rootfs。在这个函数里面,会调用 mnt_init()-init_rootfs()。这里面有一行代码,register_filesystem(&rootfs_fs_type)。在 VFS 虚拟文件系统里面注册了一种类型,我们定义为 struct file_system_type rootfs_fs_type。文件系统是我们的项目资料库,为了兼容各种各样的文件系统,我们需要将文件的相关数据结构和操作抽象出来,形成一个抽象层对上提供统一的接口,这个抽象层就是 VFS(Virtual FileSystem),虚拟文件系统。

第五步:start_kernel() 调用的是 rest_init(),用来做其他方面的初始化,

在其他初始化中,主要是创建1号进程和2号进程

初始化 1 号进程

rest_init 的第一大工作是,用 kernel_thread(kernel_init, NULL, CLONE_FS) 创建第二个进程,这个是1 号进程。

pid=1 :init进程,系统启动的第一个用户级进程,是所有其它进程的父进程,引导用户空间服务。
pid为 1 的一定是init进程.
它是内核运行后的第一个进程.

内核态和用户态

将能够访问关键资源的代码放在 Ring0,我们称为内核态(Kernel Mode);将普通的程序代码放在 Ring3,我们称为用户态(User Mode)

用户态的代码想要访问核心资源 ?

请求系统调用

比如当一个用户态的程序运行到一半,要访问一个核心资源,例如访问网卡发一个网络包,就需要暂停当前的运行,调用系统调用,接下来就轮到内核中的代码运行了。

首先,内核将从系统调用传过来的包,在网卡上排队,轮到的时候就发送。发送完了,系统调用就结束了,返回用户态,让暂停运行的程序接着运行

这个暂停怎么实现呢?其实就是把程序运行到一半的情况保存下来。例如,我们知道,内存是用来保存程运行时候的中间结果的,现在要暂时停下来,这些中间结果不能丢,因为再次运行的时候,还要基于这些中间结果接着来。另外就是,当前运行到代码的哪一行了,当前的栈在哪里,这些都是在寄存器里面的。

所以,暂停的那一刻,要把当时 CPU 的寄存器的值全部暂存到一个地方,这个地方可以放在进程管理系统很容易获取的地方。在后面讨论进程管理数据结构的时候,我们还会详细讲。当系统调用完毕,返回的时候,再从这个地方将寄存器的值恢复回去,就能接着运行了

这个过程就是这样的:用户态 - 系统调用 - 保存寄存器 - 内核态执行系统调用 - 恢复寄存器 - 返回
用户态,然后接着运行

从内核态到用户态

1,调用kernel_thread ,kernel_thread 的参数是一个函数kernel_init ,在 kernel_init里面,会调用 kernel_init_freeable(), 然后调用run_init_process 函数,会发现它调用的是 do_execve。

其中kernel_init_freeable()会去判断是否存在ramdisk_execute_command ,如果ramdisk_execute_command变量指定了要运行的程序,启动它。

ramdisk_execute_command的取值分为三种情况:

a.如果命令行参数中指定了“rdinit=...”,则ramdisk_execute_command等于这个参数指定的程序。

b.否则,如果/init程序存在,ramdisk_execute_command就等于/init

c.否则,ramdisk_execute_command为空

execve 是一个系统调用,它的作用是运行一个执行文件。加一个 do_ 的往往是内核系统调用的实现。没错,这就是一个系统调用,它会尝试运行 ramdisk 的“/init”,或者普通文件系的“/sbin/init”“/etc/init”“/bin/init”“/bin/sh”。不同版本的 Linux 会选择不同的文件启动

后续执行do_execve->do_execveat_common->exec_binprm->search_binary_handler

ramdisk 的作用
init 终于从内核到用户态了。一开始到用户态的是 ramdisk 的 init,后来会启动真正根文件系统上的 init,成为所有用户态进程的祖先。
为什么会有 ramdisk 这个东西呢?还记得上一节咱们内核启动的时候,配置过这个参数:

init 程序是在文件系统上的,文件系统一定是在一个存储设备上的,例如硬盘。Linux 访问存储设备,要有驱动才能访问。如果存储系统数目很有限,那驱动可以直接放到内核里面,反正前面我们加载过内核到内存里了,现在可以直接对存储系统进行访问。但是存储系统越来越多了,如果所有市面上的存储系统的驱动都默认放进内核,内核就太大了。为了解决这个问题,只好先弄一个基于内存的文件系统。内存访问是不需要驱动的,这个就是ramdisk。这个时候,ramdisk 是根文件系统。然后,我们开始运行 ramdisk 上的 /init。等它运行完了就已经在用户态了。/init 这个程序会先根据存储系统的类型加载驱动,有了驱动就可以设置真正的根文件系统了。有了真正的根文件系统,ramdisk 上的 /init 会启动文件系统上的 init。
接下来就是各种系统的初始化。启动系统的服务,启动控制台,用户就可以登录进来了。

2号进程

rest_init第二大事情就是第三个进程,就是 2 号进程。进程。这里需要指出一点,函数名 thread 可以翻译成“线程”,这也是操作系统很重要的一个概念。它和进程有什么区别呢?为什么这里创建的是进程,函数名却是线程呢?从用户态来看,创建进程其实就是立项,也就是启动一个项目。这个项目包含很多资源,例如会议室、资料库等。这些东西都属于这个项目,但是这个项目需要人去执行。有多个人并行执行不同的部分,这就叫多线程(Multithreading)。如果只有一个人,那它就是这个项目的主线程。但是从内核态来看,无论是进程,还是线程,我们都可以统称为任(Task)都使用相同的数据结构,平放在同一个链表中。这些在进程的那一章节,我会更加详细地讲。这里的kthreadd,负责所有内核态的线程的调度和管理,是内核态所有线程运行的祖先

趣谈linux操作系统笔记-内核初始化的更多相关文章

  1. 趣谈linux操作系统笔记-从BIOS到bootloader

    BIOS 在主板上,有一个东西叫ROM(Read Only Memory,只读存储器).这和咱们平常说的内存RAM(Read Access Memory,随机存取存储器)不同. 而 ROM 是只读的, ...

  2. 《趣谈 Linux 操作系统》学习笔记(一):为什么要学 Linux 及学习路径

    前言:学习的课程来自极客时间的专栏<趣谈 Linux 操作系统>,作者用形象化的比喻和丰富的图片让课程变得比较易懂,为了避免知识看过就忘,打算通过写学习笔记的形式记录自己的学习过程. Li ...

  3. 深挖计算机基础:趣谈Linux操作系统学习笔记

    参考极客时间专栏<趣谈Linux操作系统>学习笔记 核心原理篇:内存管理 趣谈Linux操作系统学习笔记:第二十讲 趣谈Linux操作系统学习笔记:第二十一讲 趣谈Linux操作系统学习笔 ...

  4. 趣谈Linux操作系统学习笔记:第二十四讲

    一.小内存的分配基础 1.kmem_cache_alloc_node的作用 通过这段代码可以看出,它调用了kmem_cache_alloc_node函数,在task_struct的缓存区域task_s ...

  5. 趣谈Linux操作系统学习笔记:第二十五讲

    一.mmap原理 在虚拟内存空间那一节,我们知道,每一个进程都有一个列表vm_area_struct,指向虚拟地址空间的不同内存块,这个变量名字叫mmap struct mm_struct { str ...

  6. 趣谈Linux操作系统学习笔记:第二十六讲

    一.内核页表 和用户态页表不同,在系统初始化的时候,我们就要创建内核页表了 我们从内核页表的根swapper_pg_dir开始找线索,在linux-5.1.3/arch/x86/include/asm ...

  7. 趣谈Linux操作系统学习笔记:第二十七讲

    一.文件系统的功能规划 1.引子 咱们花了这么长的时间,规划了会议室管理系统,这样多个项目执行的时候,隔离性可以得到保证. 但是,会议室里面被回收,会议室里面的资料就丢失了.有一些资料我们希望项目结束 ...

  8. 趣谈Linux操作系统学习笔记:第二十讲

    一.引子 1.计算两方面的原因 2.内存管理机制 二.独享内存空间的原理 1.会议室和物理内存的关系 和会议室一样,内存都被分成一块块儿的,都编号了号,例如3F-10就是三楼十号会议室.内存页有这样一 ...

  9. 《趣谈 Linux 操作系统》学习笔记(二):对 Linux 操作系统的理解

    首先,我们知道操作系统是管理和控制计算机硬件与软件资源的计算机程序.这里把操作系统想象为一个软件外包公司,其内核就相当于这家外包公司的老板,那么我们可以把自己的角色切换成这家外包公司的老板,设身处地的 ...

随机推荐

  1. 代码报错--------EOFError: Compressed file ended before the end-of-stream marker was reached

    背景:运行LeNet识别CIFAR-10的图像的代码时,报错: EOFError: Compressed file ended before the end-of-stream marker was ...

  2. springmvc视图文件的保护

    可以将视图文件,包含视图文件的目录放在WEB-INF下,WEB-INF是受保护的,放在该目录下,就只能在代码中访问到了view文件了,用户无法通过浏览器直接输入地址访问.

  3. 【银川网络赛G】Factories

    题目大意:给定一棵 N 个节点的树,边有边权,选定 M 个叶子节点,使得任意两个叶子节点的树上距离之和最小,求最小值是多少. 题解:任意两点的树上距离和问题应从边的贡献角度考虑. 设 \(f[u][i ...

  4. 一个tornado框架下的文件上传案例

    html部分----使用了form表单,注意三要素 method="post"  action="/loaddata" enctype="multip ...

  5. html5 音乐 audio

    手机做小项目,需要添加音乐,做此笔记,依赖jquery,代码如下: <!DOCTYPE html> <html lang="en"> <head> ...

  6. Acwing-279-自然数拆分(背包)

    链接: https://www.acwing.com/problem/content/281/ 题意: 给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复. 求拆分的方案 ...

  7. Struts2-Action接受参数方式、method属性使用及通配符的配置

    一.Action接受参数的方式 1.属性方式接收 首先编写一个用于上传参数的页面 <%@ page contentType="text/html;charset=UTF-8" ...

  8. 【leetcode】1271. Hexspeak

    题目如下: A decimal number can be converted to its Hexspeak representation by first converting it to an ...

  9. Protocol Buffers 开发者指南

    欢迎来到 protocol buffers 的开发者指南.protocol buffers 是一个语言中立,平台中立针对通讯协议,数据存储和其他领域中对结构化数据进行序列化的扩展方法. 本文档主要针对 ...

  10. G.subsequence 1(dp + 排列组合)

    subsequence 1 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 You are ...