# 开机
# Linux boot
Linux 系统启动(boot)的一般流程:
计算机接通电源,运行 ROM 中存储的 BIOS(Basic Input Output System) 系统。
BIOS 检查 CPU、内存、硬盘等计算机硬件是否正常,该过程称为开机自检。
- 如果检测到设备故障,则会在屏幕上显示报错信息。有的报错可以忽略,有的报错可能导致系统不能启动。
BIOS 从硬盘的 MBR 扇区读取 Bootloader 程序,载入内存,运行它。换句话说,将 CPU 的控制权转交给它。
Bootloader 从硬盘读取 Linux kernel 文件,载入内存,运行它其中的 start_kernel() 函数。换句话说,将 CPU 的控制权转交给它。
start_kernel() 函数负责启动内核。
- 首先执行
set_task_stack_end_magic(&init_task);
,配置当前进程的栈边界,防止栈溢出。当前进程称为 idle ,PID 为 0 。 - 然后执行 setup_boot_config()、fork_init() 等函数进行初始化。
- 最后执行 rest_init() 函数。
- 首先执行
rest_init() 调用 kernel_thread() 函数,创建第二个进程,名为 init ,PID 为 1 。
- 它会初始化系统,然后通过 fork() 创建所有用户态进程。
rest_init() 调用 kernel_thread() 函数, 创建第三个进程,名为 kthreadd ,PID 为 2 。
- 它会调用 create_kthread() -> kernel_thread() 创建所有内核线程。
- init、kthreadd 进程的 PPID 为 0 。
rest_init() 循环让 CPU 执行休眠指令。
- 当 CPU 空闲时,Linux 会让 CPU 执行 idle 进程,减少耗电量。而 idle 进程的优先级很低,当其它进程需要执行时,就会抢占 CPU 。
- idle 进程是唯一一个不通过 fork() 或 kernel_thread() 创建的进程,运行在内核态,不能用 ps 命令查看到。
# Bootloader
常见的几种 Bootloader 程序:
- grub(GRand Unified Bootloader)
- :于 1995 年发布。是 GNU 项目编写的一个程序。
- 它存储在
/boot/grub/
目录下,允许用户在开机时从多个操作系统中选择一个启动。 - grub 引导执行 Linux kernel 时,会先挂载一个临时的根文件系统 initrd(initial ram disk),以便 kernel 从硬盘找到并挂载 rootfs 根文件系统。
- grub2
- :于 2002 年发布,目前被很多 Linux 发行版采用。
- 它完全重写了 grub ,提高了性能。
- lilo
- syslinux
# init
Linux 开机启动 init 进程时,一般执行
/sbin/init
程序。- 如果该文件不存在,则尝试启动
/bin/sh
等终端来代替init 进程。 - 如果依然不存在,则 Linux 启动失败。
- 如果该文件不存在,则尝试启动
init 进程启动之后,首先负责初始化系统,包括:
- 读取配置文件
/etc/inittab
,确定当前的运行级别。 - 根据当前的运行级别,执行
/etc/rc*.d/
目录下的 shell 脚本。- rc 是 Run Command 的缩写。
- 这些脚本是指向
/etc/init.d/
目录下的脚本的符号链接,主要用于启动一些系统服务,比如 crond、sshd 等。 - 用户可以在
/etc/rc.local
中添加一些自定义的启动脚本。
- 读取配置文件
init 进程启动之后,会以 daemon 方式保持运行,负责管理所有用户态进程,担任它们的父进程或祖先进程。
- 如果其它进程产生了孤儿进程,则孤儿进程会自动从 init 进程的孙进程变成子进程,受 init 进程管理。
- 用 service、chkconfig 命令可以与 init 进程交互,从而管理系统服务。
init 有七种运行级别(run level):
- 0 :关机。
- 1 :单用户模式(simple mode),又称为紧急模式(emergency mode),可以在忘了 root 密码时修改密码。
- 2 :多用户模式。
- 3 :终端模式。
- 4 :保留。
- 5 :图形界面模式。
- 6 :重启。
相关命令:
init n # 切换运行级别
runlevel # 显示当前的运行级别
# service
service
<name>
start # 启动服务
stop # 停止服务
restart # 重启服务(先执行 stop 命令,再执行 start 命令)
status # 查看服务的状态
--status-all # 显示所有服务的状态
# chkconfig
chkconfig
<name> # 查看某服务是否开机自启动
on # 设置某服务开机自启动
off # 不开机自启动
--list # 列出所有已启动的服务
# systemd
- 传统的类 Unix 系统中,用 init 进程来初始化系统。
- 该 init 进程分为两种风格:
- System V :本文描述的是这种。
- BSD
- 该 init 进程分为两种风格:
- Ubuntu 6 开始,用 Upstart init 进程取代了 System V init 进程。
- 在
/etc/init/*.conf
文件中定义一些被 Upstart init 管理的进程,称为 job 。
- 在
- RHEL 7 、Ubuntu 15 开始,用 systemd 进程取代了 init 进程。
- 用 systemctl 命令可以与 systemd 进程进行交互,从而管理系统服务。
# systemctl
systemctl
start <unit>... # 启动 unit(会将它变成 active 状态)
stop <unit>... # 停止 unit
restart <unit>... # 重启 unit(先执行 stop 命令,再执行 start 命令)
reload <unit>... # 重新加载 unit
status <unit>... # 显示 unit 的状态
is-active <unit>... # 显示 unit 是否处于 active 状态
show <unit>... # 显示 unit 的配置信息
enable <unit>... # 启用 unit
disable <unit>... # 不启用 unit
is-enabled <unit>... # 查看 unit 是否被启用(从而会开机自启)
list-units # 显示已启动的 unit
--all # 显示所有 unit
--type=service # 显示指定类型的 unit
--failed # 显示启动失败的 unit
rescue # 使系统进入救援模式
emergency # 使系统进入紧急模式
# systemd unit
systemd 的管理对象称为 unit ,每个 unit 会保存一个特定扩展名的配置文件。主要类型如下:
xx.service # 系统服务
xx.socket # 进程间通信的 socket
xx.device # 硬件设备
xx.mount # 挂载点
xx.target # 一组 unit
例:添加配置 /usr/lib/systemd/system/ping.service
[unit] # 对该 unit 的通用描述
Description=A Demo of Ping # 名称
# Documentation=man:hello(8) # 帮助文档
# After=network.target sshd-keygen.service # 需要在哪些 unit 之后启动
# Before=network-pre.target # 需要在哪些 unit 之前启动
# Conflicts=iptables.service ip6tables.service # 与哪些 unit 冲突,即不能同时运行
# Wants=sshd-keygen.service # 依赖哪些 unit ,即需要同时运行
[Service] # Service 类型的 unit 的专用配置
# User=root # 用哪个用户来启动该 Service
# Group=root # 用户组
# WorkingDirectory=/root # 工作目录
# Environment="A=1" # 添加环境变量,可以重复使用该参数
# Environment="B=2"
# EnvironmentFile=/etc/sysconfig/sshd # 加载文件中的环境变量
Type=simple
ExecStart=/usr/bin/ping baidu.com # 启动命令,会被 systemctl start 调用
ExecStop=/bin/kill -HUP $MAINPID # 终止命令,会被 systemctl stop 调用。如果不指定 ExecStop ,则默认是发送 SIGTERM 信号给进程
ExecReload=/bin/kill -HUP $MAINPID # 重载命令,会被 systemctl reload 调用(这里调用了 $MAINPID 变量以获取主进程的 PID )
# ExecStartPre=... # 启动之前需要预先执行的命令
# ExecStopPost=... # 停止之后需要执行的命令
KillMode=process # 终止该 Service 时的方式
# TimeoutStopSec=10s
Restart=on-failure # 当该 Service 的主进程退出时的重启策略
RestartSec=1s # 等待多少秒才自动重启(默认是 100 ms)
[Install] # 如何安装该 unit
WantedBy=multi-user.target # 被该 unit 依赖,就能实现开机自启
- 每次修改 unit 的配置文件之后,要执行
systemctl daemon-reload
重新加载才能生效。 - Service 的 Type 的常见取值:
simple # 默认值,表示 ExecStart 启动的进程是该 Service 的主进程(适合在前台运行的进程) forking # ExecStart 启动的进程会 fork 出该 Service 的主进程(适合以 daemon 方式运行的进程) oneshot # 与 simple 类似,但是 systemd 会等待该进程退出之后才启动下一个 unit notify # 与 simple 类似,但是 systemd 会等收到该进程的通知之后才启动下一个 unit
- Restart 策略的常见取值:
no # 默认值,不会重启 always # 总是重启 on-failure # 只有异常退出时才重启
- KillMode 的常见取值:
none # 仅执行 ExecStop ,不检查进程是否退出 process # 如果主进程超过 TimeoutStopSec 时间之后还没退出,则发送 SIGTERM 信号,但是不管子进程 mixed # 向主进程发送 SIGTERM 信号,向子进程发送 SIGKILL 信号 control-group # 默认值,向 Cgroup 组中的所有进程发送 SIGKILL 信号
# 关机
- 执行关机命令需要 root 权限。
- 执行
init 0
关机时的一般流程:- 循环调用
close()
,关闭所有文件。 - 调用
kill(-1, SIGTERM);
,终止所有进程。然后等待一定时长,再调用kill(-1, SIGKILL);
,强制终止所有进程。 - 执行
swapoff -a
,停用所有 swap 分区。 - 执行
umount -a
,卸载所有文件系统。
- 循环调用
# shutdown
shutdown [time] [msg] # 在 1 分钟之后关机,并可以广播消息给所有用户
now # 立即关机
10 # 在 10 分钟之后关机
12:30 # 在指定时刻关机
-c # 取消即将进行的关机任务
-r # 重启,而不是关机
- shutdown 命令会调用
init 0
来关机,然后断开电源,相当于 poweroff 命令。 - halt 命令会停止运行操作系统,但计算机硬件没有断电。
# reboot
reboot # 重启