Yk2eR0's Blog.

启动流程

字数统计: 2.6k阅读时长: 10 min
2021/01/22 Share

引导程序在系统之间的细节有所不同,但大致可以分为几个步骤引导程序在系统之间的细节有所不同,但大致可以分为几个步:
(1):硬件引导
(2):OS引导程序
(3):内核启动
(4):init脚本和inittab
(5):服务启动脚本

硬件引导

上电或硬重置后,将控制权交给存储在只读存储器中的程序(通常是PROM).在PC中,我们称其为BIOS.
该程序对机器进行自检,查找引导设备和扫描启动设备.然后访问引导设备,加载MBR,加载os引导程序并将控制权转移给os引导程序.

os引导程序

pc中,os引导程序位于引导设备的第一个扇区中-MBR(主启动记录).里面除分区表外的剩余空间为主引导程序的代码.在大多数系统中,主引导程序非常受限制.PC MBR(主引导扇区)的大小限制(包括分区表在内共512字节)使得os不能将完整的os加载程序放入mbr.
在Linux中,操作系统引导程序通常为lilo或grub。
引导程序在系统之间的细节有所不同,但大致可以分为几个步骤引导程序在系统之间的细节有所不同,但大致可以分为几个步:
(1):硬件引导
(2):OS引导程序
(3):内核启动
(4):init脚本和inittab
(5):服务启动脚本

硬件引导

上电或硬重置后,将控制权交给存储在只读存储器中的程序(通常是PROM).在PC中,我们称其为BIOS.
该程序对机器进行自检,查找引导设备和扫描启动设备.然后访问引导设备,加载MBR,加载os引导程序并将控制权转移给os引导程序.

os引导程序

pc中,os引导程序位于引导设备的第一个扇区中-MBR(主启动记录).里面除分区表外的剩余空间为主引导程序的代码.在大多数系统中,主引导程序非常受限制.PC MBR(主引导扇区)的大小限制(包括分区表在内共512字节)使得os不能将完整的os加载程序放入mbr.
在Linux中,操作系统引导程序通常为lilo或grub。
grub.png
因此大多数os将主引导程序称为辅助OS引导程序.它们都可以作为辅助加载器安装(MBR指向os引导程序的启动地址),或者作为两部分引导程序启动,加载内核.内核先自解压,从zImage,uImage等格式解压为vmlinux.

内核启动

加载内核后,它将通过驱动程序初始化设备,启动kswapd进程并挂在根文件目录(/)
这些行为可能会改变一些影响内核的参数(例如:你可以覆盖默认的根文件系统).
只有这样内核才能创建第一个(用户态)进程,它的pid为1.该进程执行程序/ sbin / init,并传递未被处理的所有内核参数。在kernel的linux/init/main.c中,start_kernel进行了kernel的相关初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
asmlinkage __visible void __init start_kernel(void) //here 
{
...
smp_setup_processor_id(); //smp相关
debug_objects_early_init();
...
early_boot_irqs_disabled = true; //关闭中断请求,防止初始化被打断
boot_cpu_init();
page_address_init(); //页地址初始化
pr_notice("%s", linux_banner); //显示内核信息
early_security_init();
setup_arch(&command_line);
setup_boot_config(command_line);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
boot_cpu_hotplug_init();
build_all_zonelists(NULL);
page_alloc_init(); //页alloc初始化
setup_log_buf(0);
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
sched_init(); //init调度器
preempt_disable(); //停止中断
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
radix_tree_init();
housekeeping_init();
workqueue_init_early();

rcu_init();
console_init(); //初始化console,可以使用printfk打印了
/* Trace events are available after this */
trace_init();

if (initcall_debug)
initcall_debug_enable();

context_tracking_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
rcu_init_nohz();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
rand_initialize();
add_latent_entropy();
add_device_randomness(command_line, strlen(command_line));
boot_init_stack_canary(); //canary保护init
...//省略大片init,主要是设备和控制程序的初始化

arch_call_rest_init();//最后调用rest_init()
}

关于rest_init:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __init __weak arch_call_rest_init(void){rest_init();}

noinline void __ref rest_init(void)
{
...
pid = kernel_thread(kernel_init, NULL, CLONE_FS); //执行/sbin/init,生成pid1
...
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
...
system_state = SYSTEM_SCHEDULING;

complete(&kthreadd_done);

/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}

init脚本和inittab

init进程是所有进程的父进程,init进程繁衍出完成通常操作所需的子进程.init依赖/etc/rc.d/rc.sysinit这个脚本对系统进行初始化。而/etc/rc.d/rc.sysinit的作用主要是:
1,激活udev和selinux
2,根据/etc/sysctl.conf文件设定内核参数
3,设定系统时钟
4,键盘的键映射,即识别键盘
5,启用swap分区
6,设定主机名
7,根文件系统检测及重新以读写方式挂载
8,激活RAID和LVM设备
9,启用磁盘配额
10,检测及挂载其他文件系统(挂载/etc/fstab中定义的设备)
11,清除过期的锁和PID文件
inittab是init的配置文件.init启动时会读取/etc/inittab以获取更多参数,该文件定义了应在不同运行级别下运行的文件,根据里面的配置来启动服务

服务启动脚本

服务启动脚本分为两种:

  1. 用户自定义的/etc/rc.local:

写在这里的程序将会在执行完level3启动服务后执行它们无需登录,在开机后自动执行.也无需再用符号链接链接到rc3.d.
常用方法:静态路由的设置,服务器服务的启动

  1. 系统自启动脚本/etc/init.d:
    每个被系统管理的服务都有一个启动脚本在特定目录(/etc/init.d).命令service和systemd就是使用这些脚本来管理服务.
    ![]
    /etc/rc[0-6S].d中有每个脚本的符号链接,在inittab中调用.这些文件夹代表不同的优先级.

rc[0-6S].d会在用户登陆之前读取,这个文件中写入了什么命令,在每次系统启动时都会执行一次。

主脚本通过排序目录中的链接调用服务脚本.名称以”S”开头的所有链接都将使用参数”start”来调用,以”K”开头的名称将以”stop”来调用.
因此大多数os将主引导程序称为辅助OS引导程序.它们都可以作为辅助加载器安装(MBR指向os引导程序的启动地址),或者作为两部分引导程序启动,加载内核.内核先自解压,从zImage,uImage等格式解压为vmlinux.

内核启动

加载内核后,它将通过驱动程序初始化设备,启动kswapd进程并挂在根文件目录(/)
这些行为可能会改变一些影响内核的参数(例如:你可以覆盖默认的根文件系统).
只有这样内核才能创建第一个(用户态)进程,它的pid为1.该进程执行程序/ sbin / init,并传递未被处理的所有内核参数。在kernel的linux/init/main.c中,start_kernel进行了kernel的相关初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
asmlinkage __visible void __init start_kernel(void) //here 
{
...
smp_setup_processor_id(); //smp相关
debug_objects_early_init();
...
early_boot_irqs_disabled = true; //关闭中断请求,防止初始化被打断
boot_cpu_init();
page_address_init(); //页地址初始化
pr_notice("%s", linux_banner); //显示内核信息
early_security_init();
setup_arch(&command_line);
setup_boot_config(command_line);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
boot_cpu_hotplug_init();
build_all_zonelists(NULL);
page_alloc_init(); //页alloc初始化
setup_log_buf(0);
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
sched_init(); //init调度器
preempt_disable(); //停止中断
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
radix_tree_init();
housekeeping_init();
workqueue_init_early();

rcu_init();
console_init(); //初始化console,可以使用printfk打印了
/* Trace events are available after this */
trace_init();

if (initcall_debug)
initcall_debug_enable();

context_tracking_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
rcu_init_nohz();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
rand_initialize();
add_latent_entropy();
add_device_randomness(command_line, strlen(command_line));
boot_init_stack_canary(); //canary保护init
...//省略大片init,主要是设备和控制程序的初始化

arch_call_rest_init();//最后调用rest_init()
}

关于rest_init:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __init __weak arch_call_rest_init(void){rest_init();}

noinline void __ref rest_init(void)
{
...
pid = kernel_thread(kernel_init, NULL, CLONE_FS); //执行/sbin/init,生成pid1
...
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
...
system_state = SYSTEM_SCHEDULING;

complete(&kthreadd_done);

/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}

init脚本和inittab

init进程是所有进程的父进程,init进程繁衍出完成通常操作所需的子进程.init依赖/etc/rc.d/rc.sysinit这个脚本对系统进行初始化。而/etc/rc.d/rc.sysinit的作用主要是:
1,激活udev和selinux
2,根据/etc/sysctl.conf文件设定内核参数
3,设定系统时钟
4,键盘的键映射,即识别键盘
5,启用swap分区
6,设定主机名
7,根文件系统检测及重新以读写方式挂载
8,激活RAID和LVM设备
9,启用磁盘配额
10,检测及挂载其他文件系统(挂载/etc/fstab中定义的设备)
11,清除过期的锁和PID文件
inittab是init的配置文件.init启动时会读取/etc/inittab以获取更多参数,该文件定义了应在不同运行级别下运行的文件,根据里面的配置来启动服务

服务启动脚本

服务启动有四种方式,其中脚本分为两种:

  1. 用户自定义的service:/etc/rc.local:

写在这里的程序将会在执行完level3启动服务后执行它们无需登录,在开机后自动执行.也无需再用符号链接链接到rc3.d.
常用方法:静态路由的设置,服务器服务的启动

  1. 系统自启动脚本upstart:/etc/init.d:
    每个被系统管理的服务都有一个启动脚本在特定目录(/etc/init.d).命令service和systemd就是使用这些脚本来管理服务.
    init.d
    /etc/rc[0-6S].d中有每个脚本的符号链接,在inittab中调用.这些文件夹代表不同的优先级.

rc[0-6S].d会在用户登陆之前读取,这个文件中写入了什么命令,在每次系统启动时都会执行一次。

主脚本通过排序目录中的链接调用服务脚本.名称以”S”开头的所有链接都将使用参数”start”来调用,以”K”开头的名称将以”stop”来调用.

  1. cron:由corntab管理
  2. startup:
    配置文件在/etc/xdg/autostart,以及~/.config/autostart

review

This browser does not support PDFs. Please download the PDF to view it: Download PDF.

原文作者:Yk2eR0

原文链接:https://www.yk2er0.fun/2021/01/22/qdlc/

发表日期:一月 22日 2021, 9:55:23 上午

更新日期:March 25th 2021, 11:40:45 am

版权声明:非商业用允许转载

CATALOG
  1. 1. 硬件引导
  2. 2. os引导程序
  3. 3. 硬件引导
  4. 4. os引导程序
  5. 5. 内核启动
  6. 6. init脚本和inittab
  7. 7. 服务启动脚本
  8. 8. 内核启动
  9. 9. init脚本和inittab
  10. 10. 服务启动脚本
  • review