文章目录
一、Ubuntu 启动过程
我们不去管计算机启动后加载 BIOS、读取 MBR 等操作,直接看加载内核后的初始化过程。
Linux init 系统主要有三种:
- SysVinit:Ubuntu 6.10 及之前的版本使用的init 系统;
- Upstart:Ubuntu 从 14.10 版本引入 Upstart ,并与 SysVinit 共存。
- Systemd:Ubuntu 15.04 之后的版本使用该init 系统,可以在开机选项选择使用 Systemd 或 Upstart,但是不可与 SysVinit 或 Upstart 同时使用。
1.1 SysVinit
内核文件加载以后,就开始运行第一个程序 /sbin/init,init 的进程编号(pid)是1(其他两个 init 系统也是如此),其他所有进程都是它的子进程,所以它的作用是初始化系统环境。
许多程序需要开机启动后在后台运行,它们在 Windows 叫做”服务”(service),而在 Linux 就叫做”守护进程”(daemon)。init进程的一大任务,就是去运行这些开机启动的程序。
在不同的应用场景下,需要开机启动的程序有所不同,比如要将 linux 作为服务器使用,就不需要图形界面。Linux 允许为不同的场合,分配不同的开机启动程序,这就叫做”运行级别”(runlevel),一共7级:
- 0:关机;
- 1: 单用户模式,用作系统维护使用;
- 2: 无网络及图形界面的多用户模式;
- 3: 无图形界面的多用户模式;
- 4: 未使用;
- 5: 标准多用户模式,有图形界面和网络;
- 6 : 重启;
切换运行级别:
sudo init 0 # 关机
sudo init 6 # 重启
sudo init 3 # 关闭图形界面
SysVinit 检查 /etc/inittab 文件中是否含有initdefault
项(该文件在 Ubuntu 15.04 版本后就不存在了),该项用来设置默认运行级别。在读取完运行级别后以及其他一些配置信息后,SysVinit 就去对应的 /etc/rcN.d (N是运行级别)目录,执行目录中的所有启动脚本,启动所有守护进程。
SysVinit 运行非常良好,概念简单清晰,但它采用的是串行启动:每次只启动一个进程,而且会检查彼此之间是否有依赖,在启动其它服务之前还要等待守护进程启动。这就暴露出了它最大的问题,启动太慢。为了更快地启动,人们开始改进 SysVinit,先后出现了 Upstart 和 Systemd 这两个主要的新一代 init 系统。
1.2 Upstart
UpStart 基于事件机制,采用事件驱动机制也带来了一些其它有益的变化,比如加快了系统启动时间,同时还兼容了 SysVinit 系统。
Upstart 主要的概念是作业(job)和事件(event):
- 一个作业就是 SysVinit 里面的一个脚本或者可执行文件。
- 事件则控制着作业的启动和关闭。
几乎系统所有的内部或外部状态变更都可以触发一个事件。比如,引导程序会触发 startup 事件,而文件系统加载则会触发 path-mounted 事件……
当一个事件被触发时,才会执行相应的作业,因此,Upstart 启动和终止守护进程的方式更加灵活,并且是并行启动的,所以开机速度更快。
1.3 Systemd
Systemd 与 Upstart 类似,也是按照需要启动进程,实现了进程的并发启动。但 Systemd 功能更加强大,作用也远远不仅是启动操作系统,它还能控制后台服务的结束、重启、状态查询以及设置定时任务等。因此,它是目前主流 Linux 发行版所采用的 init 系统。
Systemd 通过单元(unit)来管理各种系统资源,单元文件统一了过去各种不同系统资源配置格式,例如服务的启/停、定时任务、设备自动挂载、网络配置、虚拟内存配置等。而 Systemd 通过不同的文件后缀来区分这些配置文件。其中我们必须知道的,是.service
文件,它封装了守护进程的启动、停止、重启和重载操作,是最常见的一种单元文件。
Unit 文件被放置在三个目录中,这三个目录是有优先级的,在三个目录中有同名文件的时候,只有优先级最高的目录里的那个文件会被使用。优先级从高到底为:
- /etc/systemd/system:系统或用户自定义的配置文件;
- /run/systemd/system:软件运行时生成的配置文件;
- /usr/lib/systemd/system:系统或第三方软件安装时添加的配置文件。
在系统启动时时,Systemd
只会去执行 /etc/systemd/system
目录里面的.service
配置文件,每一个配置文件,就是一个需要启动的守护进程。
对于支持 systemd 的程序,安装的时候,会自动的在 /usr/lib/systemd/system 目录添加一个.service
配置文件。当我们使用systemctl
控制该程序时,比如执行开启httpd服务:systemctl start httpd.service
,systemctl
就会开启 httpd.service 配置里面指向的/usr/sbin/httpd
可执行文件。
二、制作系统服务
前面说过,通过源码安装的程序,是不能通过 systemctl 控制的,必须先制作成系统服务。制作方式也很简单,就是在/usr/lib/systemd/system目录下,创建一个.service
文件,比如单元的名称叫myapp
:
[Unit]
Description=myapp init service #单元的描述
Documentation=see /opt/init.sh #单元文档的位置,可以是一个url
After=xxx.service # myapp 在 xxx 启动之后,再启动
# 一般而言,这个 xxx 是 myapp 所依赖的另一个 unit
Before=yyy.service # myapp 必须在在 yyy 启动之前启动
[Service]
ExecStart=/usr/local/bin/myapp # 启动单元的命令或者脚本
ExecStop=/usr/local/bin/myapp -S stop # 停止单元的命令或者脚本
Restart=on-failure
Type=oneshot
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target # 指定运行级别,这里是 3 级
Alias=tsingyun.service
之后,就可以通过 systemctl 控制 myapp 了。
2.1 systemctl命令
systemctl status # 显示系统状态
systemctl start <单元> # 启动单元
systemctl stop <单元> # 停止单元
systemctl restart <单元> # 重启单元
2.2 开机自启动
如果,想要 myapp 开机自启动,则需要执行以下命令:
systemctl enable myapp.service # 开机自启动
systemctl status myapp.service # 查看状态,是否开机自启动
systemctl disable myapp.service # 取消开机自启动
三、进程管理
3.1 查看进程
ps -aux
命令用来查看当前的进程状态,它会输出以下内容:
USER | PID | %CPU | %MEM | VSZ | RSS | TTY | STAT | START | TIME | COMMAND |
---|---|---|---|---|---|---|---|---|---|---|
root | 1 | 0.0 | 0.3 | 165764 | 12408 | ? | Ss | 10:01 | 0.02 | /sbin/init splansh |
用户 | 进程ID | CPU占比 | 内存占比 | 占用虚拟内存的大小 | 占用物理内存的大小 | 由哪个终端启动的该进程,?表示内核程序,与终端无关 | 进程的运行状态,见下表 | 进程的启动时间 | 进程占用CPU的时间 | 该程序的运行指令,方括号表示系统进程,没有方括号表示用户进程 |
ps -aux
会列出命令执行那一刻所有的进程,不方便我们查看,所以,通常会和过滤命令合用:
ps -aux | grep 进程名称
另外,ps -aux
是静态的,如果需要实时查看进程的变化,可以使用top
命令。
3.2 杀死进程
kill
命令可将指定的信号送给进程,默认为 SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9) 信息尝试强制删除程序。
kill -信号数字 PID
常用的信号有三个:
数字编号 | 信号含义 | 说明 |
---|---|---|
1 | SIGHUP | 重新加载进程 |
9 | SIGKILL | 强制杀死进程 |
15 | SIGTERM | 终止进程。 |
kill
通过PID杀死单个进程,还有一个与之类似的pkill
命令,可以通过程序名称杀死程序的所有进程:
pkill -信号 程序名称
3.3 将进程放入后台运行
将进程放入后台运行,需要借助于一个工具:screen,该工具需要安装:
sudo apt install screen
使用方法:
# 开启一个窗口,指定名称
screen -S 窗口名称
# 执行任务
# ……
# 将窗口放入后台,不会结束任务
# 按下 CTRL+a+d
# 查看正在运行的窗口
screen -list
# 将窗口放回前台
screen -r 窗口名称
# 终止窗口(任务)
# 窗口在前台,结束任务:CTRL+c,然后退出窗口:CTRL+d