Ubuntu 实现shell文件的开机运行(从原理到实现)

2023-05-16

目录

0、Linux的开机启动顺序

1、完善 rc-local.service 脚本

3、创建 rc.local 脚本

4、总结


设置 shell 脚本开机启动的方法有挺多,比如:

  • 添加到 init.d 中的方法:将要开机启动的脚本 copy 到 etc/init.d 中,执行 sudo chmod +x [your_startup] 赋予执行权限,然后执行 sudo update-rc.d [your_startup] defaults 来设置开机启动。
  • 创建 systemd 服务(.service)的方法
  • rc.local 的方法

这里只记录一下写 rc.local 的方法。

0、Linux的开机启动顺序

要想整明白后面的开机启动的设置方法,最好不要只知其然而不知其所以然,这里要先从Linux的开机启动顺序开始说起。

Linux系统启动从你的设备接上电源按下开关开始到你登录系统结束,中间有一个复杂但很连贯的过程:

  1. 加载BIOS(Basic Input Output System,基本输入输出系统)。获取CPU相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息等信息
  2. 读取MBR(Master Boot Record,主引导记录)。MBR就是磁盘上第0磁道的第一个扇区,里面含有 boot loader 的代码。
  3. 运行 boot loader。Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
  4. 加载内核。根据boot loader设定的内核映像所在的路径,读取内核映像并进行解压缩操作。
  5. 运行 init 程序。init永远是系统启动后运行的的第一个进程,PID(进程编号)为1。位置在sbin/init ,之后会按照设置执行一大堆系统启动时要启动的脚本、加载的驱动等功能,我们要设置的开机启动项就是在这一步执行的。
  6. 执行/bin/login程序,进入登录状态。

init 系统能够管理和控制 init 进程的行为,并负责组织和运行许多独立的或相关的工作,让系统进入一个用户设定的运行模式中。大多数Linux发行版的 init 系统是 system V 相兼容的,因此被称为 sysvinitsysvinit 主要依赖于 shell 脚本,但是他一次一个串行的启动进程,决定了它最大的弱点:启动太慢。如果是服务器这类极少进行系统开关操作的话还好,但是如果是个人电脑这样需要经常开关机的话,开机时间太长就难以忍受了。

为了能够更快地启动系统,开发者们对 sysvinit 进行了改进,先后出现了 upstart systemd 这两个主要的新一代 init 系统。目前最新的 Ubuntu 系统就是采用的 systemd 来管理系统,不过仍然兼容 init 系统的启动模式。可以看到 Ubuntu20 的 /sbin/init 是软链接到 /lib/systemd/systemd 上的。systemd 与 init 虽然启动过程不太一样,但最终的目的是一致的,都是要启动那一堆需要开机运行的脚本文件。

init 系统模式下,内核调用 init 进程后会首先获取系统运行级别(run-level)的信息,运行级别在这里不是启动优先级,可以理解为运行模式,运行级别共有 0~6 七种:

  • 0:关机
  • 1:单用户模式
  • 2:多用户模式,没有网络支持
  • 3:多用户模式,有网络支持
  • 4:保留,未使用
  • 5:X11,与运行级别 3 类似,但加载使用 X-windows 支持的图形界面
  • 6:重启

这几种模式有什么用呢?举个例子,在Ubuntu的终端下,重启指令除了 reboot 外,使用 init 6 也可以实现重启,6 对应的运行级别就是重启,类似地,运行 init 0 指令对应的就是关机。

知道启动级别后就到 /etc/rc.d 文件夹中查找相应的脚本并运行。还会在 /etc/modules-load.d/modules.conf 文件中查找装载到内核的模块。

rc.d 中含有 rc.sysinitrcN.d(N=0~6,即不同的运行级别对应的运行文件夹,根据运行级别的不同,系统会运行 rc0.drc6.d 中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务)、rc.local 等文件及或脚本,目前 Ubuntu 已经没有 rc.d 文件夹了,而是把这个文件夹的大多内容直接放在了 /etc 文件夹下了,另外 rc.local 如果需要用到的话还需要自行创建rc.local 是在所有 init 脚本执行完之后才会运行的脚本,也就是说留给用户用来做一些拓展功能的脚本。

打开 rcN.d 文件夹可以看到里面的文件都是以 S 或者 K 夹数字开头的脚本,S 待表 StartK代表 Kill ,运行脚本时系统会根据这俩前缀符号来确定传入 start 或者 stop 参数。后面的数字代表执行优先级,也就是运行或者停止的执行优先级。

这里主要记录下如何在 Ubuntu20.04 中通过 /etc/rc.local 文件来设置 shell 脚本的开机启动。

1、完善 rc-local.service 脚本

在 Ubuntu20 的 systemd 启动方式下,rc.local 默认是没有启用的,启用它需要做些简单的配置。在 lib/systemd/system 里面有个叫 rc-local.service 的脚本,该脚本的内容规定了 rc.local 的启动顺序和行为,可以使用

cat /lib/systemd/system/rc-local.service 

命令查看这个文件的原始内容如下(中文注释是我后来加上的):

#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility  # 服务的描述,方便人们阅读
Documentation=man:systemd-rc-local-generator(8)  # 一组用空格分隔的文档URI列表,这些文档是对此单元的详细说明
ConditionFileIsExecutable=/etc/rc.local  # 检测指定的路径是否存在并且是一个可执行文件,必须使用绝对路径
After=network.target  # 定义启动顺序。
                      # Before=xxx.service,代表本服务在xxx.service启动之前启动;
                      # After=xxx.service,代表本服务在xxx.service之后启动。

[Service]
Type=forking
ExecStart=/etc/rc.local start  # 指定启动单元的命令或者脚本
TimeoutSec=0
RemainAfterExit=yes  # 如果设置这个选择为真,服务会被认为是在激活状态
GuessMainPID=no

不过,一般正常的启动文件要分为三部分:

[Unit]    : 启动顺序与依赖关系
[Service] : 启动行为,如何启动,启动类型
[Install] : 定义如何安装这个配置文件,即怎样做到开机启动

这个 .service 文件的 Unit 段有行代码:

ExecStart=/etc/rc.local start

这行代码规定了这个service在开机启动时所执行的命令是:/etc/rc.local start。即运行 /etc/rc.local 脚本。不过可以看出,这个脚本的内容少了 [Install] 段,也就是说,没有定义如何做到开机启动,所以显然这是这个service是无效的。 因此我们就需要在后面帮他加上 [Install] 段,首先执行 sudo chmod 777 /lib/systemd/system/rc-local.service 赋予修改权限,然后加入以下语句,然后保存退出。

[Install]
WantedBy=multi-user.target  # WantedBy:表示该服务所在的 Target(服务组)

然后设置该 rc-local 服务开机启动:

$ systemctl enable rc-local.service
Created symlink /etc/systemd/system/multi-user.target.wants/rc-local.service → /lib/systemd/system/rc-local.service.

这条指令的实际意义是向 /etc/systemd/system 中添加一个 /lib/systemd/system 的软链接(软链接可以理解为快捷方式),看终端中这条指令运行后打印的信息就知道了。

systemd 默认从目录 /etc/systemd/system 读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录 /lib/systemd/system,真正的配置文件存放在这个目录。systemclt enable 命令用于在上面两个目录之间建立符号链接关系。.service 文件在 Linux 中存在于三个位置:/etc/systemd/system、/run/systemd/system、/lib/systemd/system,这三个目录的配置文件优先级依次从高到低,如果同一选项三个地方都配置了,优先级高的会覆盖优先级低的。

另外:经过实际测试,不使用 systemclt enable 指令创建链接这一步其实也是可以实现开机启动的,这可能跟系统会在上述三种 system 目录下查找 service 并启动有关,只是启动顺序不同而已。

3、创建 rc.local 脚本

下一步是在 /etc 中创建 rc.local 脚本,然后可以把你要开机启动的内容写入到这个脚本中即可。

举个例子:

我现在在桌面新建一个 test 文件夹,在该文件夹内创建一个名为 hhh.sh 的 shell 脚本文件,内容如下:

#!/bin/sh
time_now=$(date "+%Y-%m-%d %H:%M:%S")
echo "test ok at [$time_now]" > a.log
exit 0

作用是获取当前时间,并将其写入 a.log 文件中,我们可以运行以下这个sh文件看看效果:

可以看到,执行完这个脚本之后在 test 文件夹下多出一个 a.log 的文件,里面就是我们要打印的内容。接下来,删除这个 a.log 文件,编辑前面在 /etc 文件夹下创建的 rc.local 脚本(sudo gedit rc.local):

#!/bin/sh
cd /home/wsx/Desktop/test
chmod 777 hhh.sh
./hhh.sh &  # 最后加上 & 是让脚本启动后在后台运行的作用
exit 0

然后执行以下命令赋予 rc.local 执行权限,这步一定要有,否则没效果的

sudo chmod 777 etc/rc.local

然后重启 ubuntu,再在 test 文件夹下查看是否生成了 a.log 文件。 

成功生成 a.log 文件并打印正确信息,启动完成~ 

4、总结

总之,在确定你要开机启动哪个 shell 文件的前提下,只需两个步骤:

(1)在 /etc/systemd/system/rc-local.service 脚本中添加一个 Install 段

(2)在  /etc 目录下新建 rc.local 脚本,写入开机启动的 shell 命令,并使用 chmod 赋予其执行权限

参考文献

Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local) - 走看看 (zoukankan.com)

Linux初始化系统init和systemd介绍_哔哩哔哩_bilibili

Linux /etc/systemd/system和/lib/systemd/system的区别_Linux_资源库

linux中systemd服务介绍_qq_863909的博客-CSDN博客_linux systemd详解

Ubuntu update-rc.d添加/禁止开机启动项_awtcheng的博客-CSDN博客_ubuntu禁止开机启动

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ubuntu 实现shell文件的开机运行(从原理到实现) 的相关文章

随机推荐

  • unzip: Ubuntu系统下解压文件失败的解决办法

    unzip Ubuntu系统下解压文件失败的解决办法 双击打开 zip压缩文件 xff0c 出现错误 xff1a 装入归档文件时出现了一个错误 使用命令unzip无法解压成功 xff0c span class token function
  • 运行moveit_rviz报错 Tried to advertise on topic [/move_group/filtered_cloud] with md5sum [060021388200f

    运行moveit rviz报错 ERROR 1656070551 111682083 1948 582000000 Tried to advertise on topic move group filtered cloud with md5
  • 使用sudo apt-get update报错:E: 无法下载 http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/dists/xenial/main/b

    使用sudo apt get update报错 xff1a E 无法下载 http mirrors tuna tsinghua edu cn ubuntu ports dists xenial main binary amd64 Packa
  • cout和printf的区别

    cout和printf的区别 缓冲机制 全缓冲 xff1a 全缓冲就是等待标准IO缓冲区填满或者flush操作 xff0c 才进行IO操作输入输出 行缓冲 xff1a 当遇到 n 回车换行符时 xff0c 进行IO操作输入输出 无缓冲 xf
  • Google chrome 浏览器提示【证书无效】问题

    问题描述 在使用google浏览器时 xff0c 访问某些网址时会显示网址不安全 xff0c 查看后发现网络证书无效 解决方法1 1 桌面找到google浏览器图标 xff0c 右键 xff0c 选择属性 2 在 34 目标 34 后空一格
  • Ubuntu18下Vscode的安装及环境配置

    Ubuntu18下Vscode的安装及环境配置 转载自 xff1a https www douban com note 719118404 在linux下使用vscode xff1a 1 安装 xff1a 在官网下载deb格式的文件 xff
  • ubuntu18.04安装docker和nvidia-docker2

    ubuntu18 04安装docker和nvidia docker 1 卸载旧版本的docker 旧版本的 Docker 被称为 docker docker io 或 docker engine 如果安装了这些 xff0c 需要卸载它们 x
  • docker上运行ros

    docker上运行ros 1 方法一 xff1a 使用小鱼的一键安装工具 xff1a span class token function wget span http fishros com install span class token
  • 记录使用docker运行ros过程中遇到的问题

    记录使用docker运行ros过程中遇到的问题 1 问题一 xff1a QStandardPaths XDG RUNTIME DIR not set defaulting to tmp runtime root No protocol sp
  • ROS 节点初始化步骤、topic/service创建及使用

    目录 1 节点初始化步骤 2 service 创建及使用 3 topic创建及使用 4 框架总结 这是一个总结复盘的记录 1 节点初始化步骤 在 mian 函数中使用 ros init 初始化节点 xff0c 注册节点名 xff0c 这里注
  • java 方法的注意事项

    方法的注意事项 1 方法不能嵌套定义 2 方法的返回值类型为void xff0c 表示该方法没有返回值 xff0c 没有返回值的方法可以省略return语句不写 xff0c 如果要编写return xff0c 后面不能跟具体的数据 3 re
  • java 方法重载

    方法名相同 xff0c 参数也完全相同 xff0c 称为方法的重复定义 xff0c 是一种冲突性的错误 如下 xff1a 在同一个类中 xff0c 定义了多个同名的方法 xff0c 但每个方法具有不同的参数或参数个数 xff0c 这些同名的
  • java 方法的参数传递(基本数据类型、引用数据类型)

    基本数据类型传递 xff1a span class token keyword public span span class token keyword class span span class token class name Test
  • java 方法求数组最大值

    需求 xff1a 设计一个方法用于获取数组中元素的最大值 xff1a span class token keyword public span span class token keyword class span span class t
  • docker 拉取镜像、创建并启动容器

    获取镜像 默认情况下 xff0c 使用docker pull命令 xff0c 会从官方的Docker Hub库中将镜像拉取到本地 首先介绍这条命令的格式 xff1a docker pull OPTIONS lt 仓库名 gt xff1a l
  • Docker 停止容器

    使用docker stop停止一个容器 docker stop可以用来终止一个正在运行的容器 它的命令格式如下 xff1a docker stop OPTIONS Container Container 其中 xff1a docker st
  • 进入docker容器

    进入docker容器的三种方法 1 使用ssh登陆进容器 xff1b 2 使用nsenter nsinit等第三方工具 xff1b 3 使用docker本身提供的工具 Docker目前主要提供了docker exec和docker atta
  • docker删除容器

    删除容器使用 docker rm 命令 xff1a 1 删除容器 1 首先需要停止所有的容器 docker stop docker ps a q 2 删除所有的容器 只删除单个时把后面的变量改为container id即可 docker r
  • docker 基于Commit定制镜像

    在Docker中提供了一个命令docker commit xff0c 该命令会把对容器的修改提交成一个镜像 换句话说 xff0c 就是在原有镜像的基础上 xff0c 再叠加上容器的存储层 xff08 该存储层仅仅保存了容器所做的修改 xff
  • Ubuntu 实现shell文件的开机运行(从原理到实现)

    目录 0 Linux的开机启动顺序 1 完善 rc local service 脚本 3 创建 rc local 脚本 4 总结 设置 shell 脚本开机启动的方法有挺多 xff0c 比如 xff1a 添加到 init d 中的方法 xf