《Linux0.11内核完全注释》读书笔记之内核启动方式总结

Linux0.11启动方式

BIOS启动

当PC电源打开后,80X86结构的CPU将自动进入实模式,并从0xFFFF0开始自动执行程序,
这通常是ROM-BIOS中的地址。PC机的BIOS将执行某些系统检测,并在物理地址0处开始初
始化中断向量。此后将可启动设备的第一个扇区读入内存绝对地址0x7C00处,并跳转到这
个地方。

bootsect.s/setup.s

bootsect.s

Linux最前面的部分是boot/bootsect.s,它将由BIOS读入到内存绝对地址0x7C00处,
当它被执行时就会把自己移动到内存绝对地址0x90000(576KB)处,并把启动设备后
2KB字节代码boot/setup.s读入到内存0x90200处,而内核其他部分(system模块)
则被读入到内存地址0x10000(64KB)开始处,因此从机器家电开始的执行顺序如下图。

setup.s

在系统加载期间将显示信息“Loading…”。然后控制权将传递给boot/setup.s,这是
另一个实模式汇编语言程序。

启动部分识别主机的某些特性,如果需要会要求用书为控制台选择显示模式。然后将整
个系统从地址0x10000移至0x0000处,读取并为system模块保留系统参数,之后进入
保护模式并跳转至系统余下部分(0x0000处)。

system模块加载

head.s

head.s位于system模块头部。从这里开始,内核完全是在保护模式下运行。这个程序功
能比较单一。首先是加载各个数据段寄存器,重设中断描述符表idt,使各表项执行一个
只报错误的哑中断子程序ignore_int。然后重设gdt,检测A20地址线是否已真的开启,
如果没开启进入死循环。检测数学协处理器。
设置分页处理机制,将页目录表放在绝对物理地址0开始处(本程序会被覆盖)。最后,
head.s利用返回指令将预先放在堆栈中的/init/main.c程序的入口地址弹出,去运行
main()程序。

main.c

main.c程序首先利用前面setup.s程序取得的系统参数设置系统的根文件设备号以及一
些内存全局变量。

初始化基本环境

内核先进行所有方面的硬件初始化。包括陷阱门、块设备、字符设备和tty,包括人工设置
第一个任务(task 0)。待所有初始化完成后设置中断允许标志开启中断,并“手动”切换
到任务0中运行。

task 0启动(idle进程)

直接fork()创建task 1,由于是内核创建的进程无法使用copy-on-write,又和内核
和之后新建的task 1共用堆栈,因此不能使用堆栈破坏其他程序运行,所以使用了内
联函数fork()和pause()。创建完task 1后就开始pause()死循环。

task 1启动(init进程)

注意: init进程由用户态进程idle进程创建,因此可以使用copy-on-write,
所以能使用堆栈

在main()中已经进行了系统初始化,包括内存管理、各种硬件设备和驱动程序。init()
函数运行在任务0第一次创建的子进程(任务1)中。它首先对第一个将要执行的程序
(shell)的环境进行初始化,然后以非交互式shell方式加载该程序并执行之(见下方
task 2启动),并等待task 2结束。

init进程在task 2结束后再次创建新的shell进程,这次是作为登录shell运行/bin/sh
的。之后一直等待创建的子进程结束返回,对于所创建的子进程将关闭所有以前还遗留的
句柄(stdin, stdout, stderr),新创建一个会话并设置进程组号,然后重新打开
/dev/tty0作为stdin,并复制成stdout和stderr。再次执行/bin/sh。

wait()的另一个功能是处理孤儿进程。如果一个进程的父进程先终止了,那么这个进程
的父进程就会被设置为这里的init进程,并由init进程负责释放一个已经终止进程的任
务数据结构等资源。

task 2启动(/bin/sh)

首次创建

由init进程创建,关闭文件描述符0(stdin),以只读方式打开/etc/rc文件,并使用execve()
函数将自身替换成/bin/sh程序,然后执行/bin/sh。由于这里sh的运行方式是非交互式的,因此
执行完rc文件中的命令后就会立即退出,进程2也随之结束。

非首次创建

以登录shell运行,交互式shell。

较新的启动方式

在前面的程序上,init()函数直接开始执行了命令解释程序(shell程序)/bin/sh,而
在实际可用的系统中却并非如此。为了能具有登录系统的功能和多人同时使用系统的能力,
通常的系统是在这里或类似地方执行系统初始化程序init.c,而此程序会根据系统/etc/
目录中配置文件的设置信息,对系统中支持的每个终端设备创建子进程,并在子进程中运
行终端初始化设置程序agettty(统称getty),gettty程序则会在终端上显示用户的登
录提示信息“login:”。当用户键入了用户名后,getty被替换成login程序。login程序验
证了用户输入口令的正确性后,最终调用shell程序,并进入shell交互工作界面。

init进程的主要任务是根据/etc/rc文件中设置的信息,执行其中设置的命令,然后根据
/etc/inittab文件中的信息,为每一个允许登录的终端设备使用fork()创建一个子进程,
并在每个新创建的子进程中运行getty。而init进程则调用wait(),进入等待子进程结束
状态。
每当一个子进程结束退出,就会根据返回的pid知道哪个对应终端的子进程结束了,因此
就会为相应终端设备再创建一个新的子进程。每个允许的终端设备都是中有一个对应的进
程为其等待处理。

当系统关闭时,init负责杀死所有其他进程,卸载所有的文件系统以及停止处理器的工
作,以及它配置成要做的工作。

getty程序

gettty程序主要任务是设置终端类型、属性、速度和线路规程。它打开并初始化一个tty
端口,显示提示信息,并等待用户键入用户名。该程序只能由超级用户执行。通常,若
/etc/issue文本文件存在,则getty会首先显示其中的文本信息,然后显示登录提示信
息,读取用户输入的登录名,并执行login程序。

login程序

login程序主要用于要求登录用户输入密码。根据用交输入的用户名,它从口令文件passwd
中取得对应用户的登录项,然后调用getpass()以显示“password:”提示信息,读取用户
键入的密码,然后使用加密算法对键入的密码进行加密处理,并与口令文件中该用户项中
pw_passwd字段作比较。如果登录失败,login程序会退出执行并被父进程init的wait()
接收,init会根据记录下来的信息再次创建一个子进程,并在进程中针对该终端设备再次
执行getty。

如果用户键入的密码正确,则login会把当前工作目录修改成口令文件中指定的用户起始
目录。并把对应终端的访问权限修改成用户读/写和组写,设置进程的组ID。然后利用所得
的信息初始化环境变量信息,如(HOME=)。接着显示/etc/motd(message-of-the-day)
文件中的文本信息,并检查并显示该用户是否有邮件的信息。最后login程序改变成登录
用户的用户ID并执行用户口令文件中该用户项指定的shell程序,如bash后csh等。如果
没有指定shell,则会使用默认/bin/sh。

shell程序

在登录过程中login开始执行shell时,所带参数argv[0]的第一个字符是‘-’,表示该
shell作为一个登录shell被执行。此时shell程序会根据该字符,执行某些与登录过程
相应的操作。登录shell首先从/etc/profile文件以及.profile文件(若存在)读取
命令并执行。如果在进入shell时设置了ENV环境变量或者在.profile中设置了该变量,
则shell下一步会从该变量命名的文件中读取命令并执行。因此用户应该把每次登录是
都要执行的命令放在.profile文件中,而把每次运行shell都要执行的命令放在ENV变
量指定的文件中。

如放在.profile中:

ENV=$HOME/.anyfilename;
export ENV
时间: 2024-10-12 04:33:08

《Linux0.11内核完全注释》读书笔记之内核启动方式总结的相关文章

读书笔记-NIO的工作方式

读书笔记-NIO的工作方式 1.BIO是阻塞IO,一旦阻塞线程将失去对CPU的使用权,当前的网络IO有一些解决办法:1)一个客户端对应一个处理线程:2)采用线程池.但也会出问题. 2.NIO的关键类Channel和Selector,Selector相当于调度系统,他可以轮询每个Channel的状态,Channel要比Socket更加具体:Buffer类比Stream更加具体: NIO引入Selector.Channel.Buffer就是想把信息具体化,让程序员有机会控制他们. 3.Buffer是

《C++primer》v5 第11章 关联容器 读书笔记 习题答案

11.1 map是关联容器,vector是顺序容器 11.2 略 11.3 int main() { map<string,int> word; string s; while(cin>>s) word[s]++; for(auto i:word) cout<<i.first<<" "<<i.second<<endl; return 0; } 11.4 void convers(string &s) { s

Linux内核完全注释阅读笔记1:O(1)时间复杂度查找timeout定时器

前言 一直有Linux kernel情节,之前也一直在看Linux kernel相关的书和代码,但是每次到最后又由于兴趣转变而荒废了.这次终于静下心来想把Linux内核相关的代码好好看看,算是对自己的一个沉淀吧.由于之前工作做的是分布式调度这块的东西,也稍微有过分布式文件系统相关的实习经历,所以阅读Linux内核代码的重心可能会往调度和文件系统这两大块倾斜. 个人感觉读读Linux kernel还是蛮有必要的,其实现在各种分布式框架.各种云计算,其实很多的思想都是借鉴Linux kernel的.

[C++11] Effective Modern C++ 读书笔记

本文记录了我读Effective Modern C++时自己的一些理解和心得. item1:模板类型推导 1)reference属性不能通过传值参数传入模板函数.这就意味着如果模板函数需要一个reference类型的参数,必须在模板声明中将其声明为reference,否则,即使使用一个reference类型的变量调用模板函数,类型推导的结果将不带reference属性. 2)constant和volatile属性也不能通过传值参数传入模板函数,但是可以通过reference参数传入这些属性. 3

《Linux内核》课本读书笔记 第三章

【托马斯微积分11版---英文】读书笔记:前言--未完待续

一.实数和实线 1. 实数---Real numbers 定义:可以用小数(decimals)表示的数. 实线---Real line 定义:把实数用几何形式表示在一条线上的点,这个线就是实线. 用R表示实数系统(System)或者实线. 2. 实数的三个属性: a.代数属性 algebraic properties:在通用的算术规则下:实数可以加减乘除(除了不能除以0)而产生更多的实数. b.顺序属性 order properties:如果a,b是实数,则满足: 1. a < b => a+

[读书笔记]xampp mysql启动失败解析(win7)

1.  [mysql]  MySQL Service detected with wrong path  [mysql]  Change XAMPP MySQL and Control Panel settings or  [mysql]  Uninstall/disable the other service manually first  [mysql]  Found Path: "D:\Program Files\MySQL\MySQL Server 5.6.7\bin\mysqld-nt

Ubuntu 12.10安装Bochs 2.6, 调试linux-0.11内核

http://blog.csdn.net/sinzou1/article/details/5903968 Linux(ubuntu)安装bochs http://wenku.baidu.com/view/f968e23b0912a21614792914.html ubuntu下安装bochs http://www.linuxidc.com/Linux/2011-07/38371.html Ubuntu 10.04安装Bochs 2.4.5笔记 http://blog.csdn.net/trace

Linux内核架构读书笔记 - 2.5.3 处理优先级

1 优先级的内核表示 内核使用 0 - 139 表示内部优先级,值越低,优先级越高.0 -99 实时进程使用 nice 值 [-20,19]映射到范围100 - 139,如下图 内核定义了一系列宏来辅助优先级之间的转换 sched.h 1 /* 2 * Priority of a process goes from 0..MAX_PRIO-1, valid RT 3 * priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH 4 *