我正在创建一个 Linux 发行版,现在我需要一个 init 程序。我可以很好地用 C 语言编写代码,而且我对 linux 了解很多(不多,但我已经使用 arch linux 进行开发 4 年了),所以我想我应该尝试用 C 语言编写自己的基本初始化脚本。只是想知道,init 执行哪些任务来将系统设置为简单的 shell? (当我问“init 做什么?”时,我确实知道 init 是什么以及它的用途。我只是不知道它执行哪些任务。)
我不需要代码,我什至可能不需要基本命令,但我做需要它们的运行顺序。
答案1
系统5init
只会告诉你故事的一小部分。
Linux 世界存在着一种短视现象。人们认为他们使用一种称为“系统 5 init
”的东西,这既是传统的,也是最好的起点。事实上情况并非如此。
首先,传统实际上并不像这些人所说的那样。 System 5init
和 System 5rc
可以追溯到 AT&T UNIX System 5,它几乎是在第一个 UNIX 之后的时间,就像我们现在(比方说)在 Linux-Mandrake 第一个版本之后的时间一样远。
第一版 UNIX 只有init
.它没有rc
。第一版汇编语言init
(谁的代码已经由 Warren Toomey 等人修复并提供。)直接生成并重新生成 12 个getty
进程,从内置表安装 3 个硬连线文件系统,并直接从名为 的用户的主目录运行程序mel
。该getty
表也直接位于程序映像中。
UNIX System 5 又过了十年,所谓的“传统”Linux init 系统出现了。 1992 年,Miquel van Smoorenburg(重新)编写了 Linux init
+rc
及其相关工具,人们现在将其称为“System 5 init
”,尽管它实际上并不是来自 UNIX System 5 的软件(并且不仅仅是init
)。
System 5 init
/rc
并不是最好的起点,即使你增加了 systemd 的知识,也还不到所需知识的一半。在过去二十年里,在 init 系统设计领域(针对 Linux 和 BSD)已经做了很多工作。各种工程决策都经过讨论、制定、设计、实施和实践。商业 Unices 也做了很多工作。
可供研究和学习的现有系统
这是一些主要初始化系统的不完整列表以外这两个,以及其中的一个或两个(几个)要点:
- 约阿希姆·尼尔森的有限值采取了使用更易于理解的配置文件的路线。
- 菲利克斯·冯·莱特纳迷你选择文件系统即数据库的配置系统、较小的内存占用以及
init
启动的事物之间的启动/停止依赖性。 - 格里特·佩普的运行去了我之前描述的刚刚生成四个 shell 脚本方法。
- 初始化NG旨在拥有依赖项、命名目标、多个配置文件和更灵活的配置语法,并为子进程加载更多设置。
- 暴发户我们进行了彻底的重新设计,将系统建模为服务和相互依赖关系,而不是由它们触发的事件和作业。
- 的设计开胃菜包括将所有服务管理(甚至包括
getty
生成和僵尸收割)推送到单独的服务管理器中,以及只是处理操作系统特定的“API”设备/符号链接/目录和系统事件。 - 西尼特是一个非常简单的初始化。它执行
/bin/rc.init
的任务是启动程序、挂载文件系统等。为此,您可以使用类似的东西微型计算机。
此外,大约 10 年前,daemontools 用户和其他人之间就使用svscan
as process #1 进行了讨论,这导致了诸如Paul Jarc 的 svscan 作为流程 1 研究,格里特·佩普的想法, 和Laurent Bercot 的 svscan 作为流程 1。
这让我们了解了流程#1 程序的作用。
进程 #1 程序执行什么操作
流程#1“应该”做什么的概念本质上是主观的。一个有意义的客观的设计标准是什么流程#1至少必须做。内核对其提出了几个要求。而且总是有一些特定于操作系统的各种事情需要它做。当谈到进程 #1 有什么时传统上完成,那么我们还没有达到那个最低限度,而且从来没有真正达到过。
各种操作系统内核和其他程序对进程#1 的一些要求是无法逃避的。
人们会告诉你,fork()
处理事物并充当孤立进程的父进程是进程 #1 的主要功能。讽刺的是,这是不真实的。处理孤立进程是(对于最近的 Linux 内核,如所解释的那样)https://unix.stackexchange.com/a/177361/5132)系统的一部分,人们可以在很大程度上将其从流程#1分解到其他流程中,例如专用的服务经理。所有这些都是服务管理器,在流程 #1 之外运行:
- IBM AIX
srcmstr
程序系统资源控制器 - 格里特·佩普的
runsvdir
来自鲁尼特 - 丹尼尔·J·伯恩斯坦
svscan
来自 daemontools,Adam Sampsonsvscan
的弗里特, 布鲁斯·冈特svscan
来自 daemontools-encore 和 Laurent Bercot 的s6-svscan
来自 s6 - 韦恩·马歇尔的
perpd
来自罪犯 - Solaris 10 中的服务管理工具
- 来自
service-manager
nosh
同样,正如所解释的https://superuser.com/a/888936/38062,整个/dev/initctl
想法不需要接近过程#1。讽刺的是,正是高度集中化的 systemd 证明了它可以从进程 #1 中移出。
init
相反,人们在他们的即兴设计中通常会忘记的强制事项是诸如处理从内核发送的SIGINT
、SIGPWR
、SIGWINCH
等以及执行发送的各种系统状态更改请求之类的事情来自“知道”进程 #1 的某些信号意味着某些事情的程序。 (例如:正如所解释的https://unix.stackexchange.com/a/196471/5132,BSD 工具集“知道”SIGUSR1
具有特定含义。)
还有一些一次性的初始化和终结任务是无法逃避的,或者如果不执行就会遭受很大损失,例如安装“API”文件系统或刷新文件系统缓存。
处理“API”文件系统的基础知识与init
ROM 第一版 UNIX 的操作几乎没有什么不同:一个程序有一个硬连线的信息列表,一个简单地mount()
保存列表中的所有条目。您会init
在从 BSD(原文如此!)、nosh到 systemd等各种系统中发现这种机制system-manager
。
“将系统设置为一个简单的 shell”
正如您所观察到的,init=/bin/sh
没有安装“API”文件系统,当一个类型exit
(https://unix.stackexchange.com/a/195978/5132),并且通常将其留给(超级)用户手动执行使系统最低限度可用的操作。
要了解人们实际上别无选择,只能在流程 #1 程序中执行哪些操作,从而为您的既定设计目标奠定良好的基础,您最好的选择是查看 Gerrit Pape 的 runit(Felix von)操作中的重叠部分Leitner 的 minit,以及system-manager
nosh 包中的程序。前两者展示了两种极简主义的尝试,但仍然处理无法避免的事情。
我建议,后者很有用,因为它对system-manager
程序有大量的手动输入,其中详细说明了安装了哪些“API”文件系统、运行了哪些初始化任务以及处理了哪些信号;在一个系统中按设计系统管理器只生成其他三个东西(服务管理器、附带的记录器和运行状态更改的程序),并且只执行进程 #1 中不可避免的操作。
答案2
Debian 上的 System V init(还有其他变体和变体)执行以下操作:
- 输入运行级别时,它会
/etc/rcX.d/S*
按字母数字顺序调用脚本,其中X
是运行级别。这些脚本应该设置运行级别。典型的设置是启动守护程序并执行该运行级别的设置任务。这是进入运行级别时一次性完成的事情。 - 在运行级别中时,它会启动
/etc/inittab
在该运行级别期间需要处于活动状态的守护程序。如果这些守护进程停止运行,它会重新启动它们。虽然您可以拥有任何您想要管理的守护程序init
,但至少您需要一些守护getty
程序,以便您可以登录。getty
登录完成后退出,然后init
重新启动它,提供新的登录提示。
- 如果守护进程在太短的时间内重新启动太多次,它会暂时停止尝试重新启动它。
- 仅仅因为进入运行级别时启动脚本启动了某些内容,并不会
init
自动尝试使其保持运行。您需要在/etc/inittab
.
- 退出运行级别时,它会按
/etc/rcX.d/K*
字母数字顺序调用 中的脚本,其中X
是运行级别。实现关机或重启的一种方法是为这些事件定义一个运行级别,并使最后执行的任务成为halt
或reboot
命令。 - 它将调用可执行文件来响应某些事件,例如电源事件或 Ctrl-Alt-Del。
- 它侦听套接字,如果收到某些消息,它将更改运行级别。
因此,如果您愿意,您可以用作init
基本的服务管理器,但现在的主要任务是保持 的getty
可用,以便用户可以登录并启动运行级别转换。
我只是想知道,init 执行哪些任务来将系统设置为一个简单的 shell?
任何你想要的。在 Debian 上,每个/etc/rcX.d
目录中都有一个指向脚本的符号链接/etc/init.d
,您可以完全自定义或删除这些脚本。该顺序是通过在每个脚本前面加上, 等来建立的00
。01
如果您只想生成一个 shell,您还可以指定一个-b
选项init
(即通过内核命令行) 。init
当你退出 shell 时,init
就会死掉,当init
死掉时,内核就会恐慌。
答案3
init 必须做的绝对最低限度是运行至少一个其他程序并且永远不退出。如果 init 退出,系统就会崩溃。我想,即使运行另一个程序也不是绝对必要的,但如果你不这样做,init 就必须负责完成系统期望做的每一件事,否则它不会很有用。
答案4
如果您致力于模块化“做一件事并做好”原则,那么程序init
应该启动进程。
启动进程
它应该在内核成功解压后执行,负责初始化系统运行所需的所有初始进程(例如安装 /etc/fstab 中找到的驱动器、启动网络接口以及很快)。
由于启动和关闭的过程本质上是相反的,因此 init 程序通常还确保在收到关闭命令时停止进程。
停止进程
这意味着它必须根据该进程的手册页停止进程(换句话说,不仅仅是公然的kill -9
,它应该以它想要结束的方式关闭该进程),卸载驱动器,并最终发出最终的断电命令。
参考
关于其他人如何做到这一点的一个很好的参考是查看Slackware 的 /etc/rc.d 脚本,以及已经存在的简单初始化系统,例如 尼尼特(部长的继任者)。它具有进程监督功能(意味着如果进程死亡,则会重新启动),这可以说不是 init 的工作,但它仍然非常基础且易于理解,尤其是通过作者的示例脚本。