想在Linux开机时候启动一个程序?看看这篇文章
回顾历史
按下电源开关的那一刻,BIOS苏醒,然后加载开机管理程序(Boot Loader),接着Kernel上场。Kernel完成硬件与驱动的载入,成功后开始执行用户态的第一个程序。

用户态第一个程序有个特点,其pid=1.pid=1的进程会开始执行一堆预先定义好的“开机脚本”或者“开机服务”
因此要想让程序在开机时候启动,只需要准备好脚本,告诉pid=1的进程即可。

一般来讲,需要开机启动的程序有很多,东西一多,就需要考虑管理问题。怎么管理呢?SysV init和Systemd登场。
- SysV init
System V是Unix的一个版本,简称SysV。在此版本中用户态的第一个程序调用的是init,然后init唤醒所有的其他服务。
- Systemd
一款新的服务管理工具
还有种服务管理系统Upstart,存在感比较低,省略
这时候有人脑海里面就会闪现出一个问题:如何判断某个linux系统采用了那种服务管理系统?
可以从/proc/1/文件夹中获取到一些蛛丝马迹。在/proc目录中,有一个名称为exe的文件(和windows系统没有半毛钱关系)。
exe文件指向启动当前进程的可执行文件(完整路径)的符号链接。通过查看exe文件的指向,可以知道当前系统所使用的服务管理系统。
以下是在两种不同初始化系统上执行stat /proc/1/exe所得到的结果。从第二行结果可知,第一个系统采用的是Systemd,第二个系统采用的是SysV init初始化(init为其可执行文件)。
为什么例子中没有Upstart?因为找不到Upstart的系统
[root@ip-172-31-8-32 ~]# stat /proc/1/exe
File: ‘/proc/1/exe’ -> ‘/usr/lib/systemd/systemd’
Size: 0 Blocks: 0 IO Block: 1024 symbolic link
Device: 3h/3d Inode: 66971739 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
root@ip-172-31-9-10:/proc/1# stat /proc/1/exe
File: /proc/1/exe -> /init
Size: 0 Blocks: 0 IO Block: 1024 symbolic link
Device: 33h/51d Inode: 81 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
top命令也可达到同样效果:
Tasks: 124 total, 1 running, 123 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.3 st
KiB Mem : 1880524 total, 109836 free, 350800 used, 1419888 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 1226560 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 126128 4136 2152 S 0.0 0.2 4:58.20 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
2 root 20 0 0 0 0 S 0.0 0.0 0:00.29 [kthreadd]
top - 18:05:00 up 1:52, 0 users, load average: 0.19, 0.12, 0.13
Tasks: 10 total, 1 running, 9 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.1 us, 1.2 sy, 0.0 ni, 96.2 id, 0.2 wa, 0.0 hi, 0.3 si, 0.0 st
MiB Mem : 11921.9 total, 6530.6 free, 3152.8 used, 2238.5 buff/cache
MiB Swap: 3072.0 total, 3072.0 free, 0.0 used. 8108.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 1744 1088 1016 S 0.0 0.0 0:00.02 /init
122 root 20 0 1764 72 0 S 0.0 0.0 0:00.00 /init
123 root 20 0 1764 88 0 S 0.0 0.0 0:00.01 /init
我是谁?我在哪里?应该到哪里去?
当明确了当前linux系统的初始化系统后,紧接着第二个问题接踵而至:怎么做才能让初始化系统知道,在系统启动后要运行kafka或者nginx?
答案和把一头大象装入冰箱一样:1. 打开冰箱门 2. 把大象装进去 3. 关闭冰箱门
但是细节呢?细节就是1、完成服务脚本 2、将服务脚本放在固定的位置,下面是不同初始化系统放置服务脚本的位置
| SysV init | Systemd |
|---|---|
| /etc/init.d | /etc/systemd/system |
下面一一更详细解释
SysV init
- 在
/etc/init.d目录中,touch hello添加服务脚本。service --status-all命令,会列举/etc/init.d目录中所有服务。以下结果说明,系统已经认识了服务hello
root@ip-172-31-9-10:/etc/init.d# service --status-all
[ - ] apparmor
[ ? ] apport
[ - ] dbus
[ - ] hello
[ ? ] hwclock.sh
...
- 编写服务脚本。服务脚本主体采用shell语法编写,一般应用程序都会提供自启动脚本。下面为一段例子脚本
点击查看代码
#!/bin/bash
#service hello
start() {
echo 'start hello service...'
echo 'start hello service over'
}
stop() {
echo 'stop hello service...'
echo 'stop hello service over'
}
case $1 in
start)
start ;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "valid arg:[start|status|stop|restart]"
esac
exit 0
- 使用命令
chkconfig --add hello将服务hello添加到开机启动中。 在/etc目录中,有6个子目录,每个子目录代表服务脚本的不同执行场景。以下为目录和场景的对应关系。
| 目录 | level | 执行场景 |
|---|---|---|
| rc0.d | 0 | 关机 |
| rc1.d | 1 | 单用户模式 |
| rc2.d | 2 | 无网络连接的多用户命令行模式 |
| rc3.d | 3 | 有网络连接的多用户命令行模式 |
| rc4.d | 4 | 系统保留 |
| rc5.d | 5 | 带图形界面的多用户模式 |
| rc6.d | 6 | 重新启动 |
每个目录中的文件都链接至init.d目录中的服务脚本。以下为rc0.d目录内容,其作用为在关机场景时,将rc0.d目录中所有的服务关闭(K-kill)
以nginx服务脚本为例,chkconfig在rc0.d目录中创建K01nginx软链接,指向/init.d/nginx,达到关机时关闭nginx服务的目的。
root@weidengbai:/etc/rc0.d# ls -l
total 0
lrwxrwxrwx 1 root root 13 Apr 23 2020 K01atd -> ../init.d/atd
lrwxrwxrwx 1 root root 20 Apr 23 2020 K01cryptdisks -> ../init.d/cryptdisks
lrwxrwxrwx 1 root root 25 Apr 23 2020 K01multipath-tools -> ../init.d/multipath-tools
lrwxrwxrwx 1 root root 15 Mar 25 09:48 K01nginx -> ../init.d/nginx
....
当运行chkconfig --add hello命令时,chkconfig 会自动创建对应目录的软连接。因此,如果系统未安装chkconfig 服务,参考对应目录中的命名规格,手工创建软链接,可以达到一样的效果。
systemd
systemd在SysV init 之后出现,向下兼容。但systemd的服务脚本也有自己独有的脚本语法和放置位置。下面以常见的kafka开机启动为例说明。
在此之前,系统中已经存在zookeeper.service.
- 在/etc/systemd/system目录中添加脚本
touch kafka.service,然后systemctl list-unit-files |grep kafka查询此服务,确保添加成功:
[root@ip-172-31-8-32 system]# systemctl list-unit-files |grep kafka
kafka.service masked
- 编辑服务脚本:
点击查看代码
[Unit]
# Kafka服务的描述
Description=Kafka Service
# 服务依赖—在什么服务之后启动,一般为在网络服务启动后启动
After=network.target zookeeper.service
[Service]
Type=forking
# 启动环境参数
# 此脚本指定了Zookeeper日志和Java的目录
#Environment=ZOO_LOG_DIR=/data/kafak/logs/
#Environment=JAVA_HOME=/usr/local/jdk1.8
# 启动命令
ExecStart=/usr/local/data/kafka_2.13/bin/kafka-server-start.sh -daemon /usr/local/data/kafka_2.13/config/server.properties
# 停止命令
ExecStop=/usr/local/data/kafka_2.13/bin/kafka-server-stop.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target
- 加入开机启动
#系统重新加载服务
systemctl daemon-reload
#设置开机自启动
systemctl enable kafka.service

浙公网安备 33010602011771号