当前位置: 首页 > news >正文

【嵌入式Linux基础】启动初始化程序--init程序

文章目录

  • 内核启动后
  • 初始化程序简介
  • BusyBox init
    • Buildroot init 脚本
  • 后记

内核启动后

 内核引导代码在initramfs中通过内核命令行中的root=指定的文件系统中去寻找根系统文件,并执行一个相应的程序。在默认情况下,对于initramfs这个程序是/init,对于常规文件系统,是/sbin/init程序。init程序具有root权限,且因为它是第一个运行的进程,所以其进程ID(PID)是1。如果init程序未能启动,则内核将会崩溃。

 init程序是所有其他进程的祖先。在这里,通过pstree命令可以看出,在大部分版本中,init通常是psmisc包中的一部分:

# pstree -gn init(1)-+-syslogd(63)|-klogd(66)|-dropbear(99)`-sh(100)---pstree(109)

 init程序的任务是接管系统并使之运行。它和运行shell脚本的shell命令一样简单。

  • 在启动阶段,它启动守护进程,配置系统参数,以及负责让系统进入工作状态所需要做的配置操作。
  • 作为可选项,它可以启动其他守护进程,如在终端上的getty守护进程,该进程允许登录shell。
  • 接收那些因为直接父进程被终止,以及线程组中没有其他进程而形成的孤儿进程。
  • 它通过捕捉信号SIGCHLD并收集其返回值以防止它们成为僵尸进程,对init的直接子进程的终止进行响应。
  • 作为一个可选项,它重新启动那些已经终止的守护进程。
  • 它处理系统关机。

换句话说,init管理着系统从开机到关机的整个生命周期。

初始化程序简介

 在嵌入式设备中,你最有可能遇到三个初始化程序是BusyBox init、System V init 和 systemd。Buildroot默认只构建BusyBox init,你也可以选择构建所有三个初始化程序。Yocto项目默认构建System V init,当然其他两种也可以选择。

类别Busy Box initSystem V initsystemd
复杂性
启动速度
所需shellashash 或 bash
可执行程序数量0450(构建系统时的配置
支持的C库任意任意glibc
大小(MB)00.134(构建系统时的配置

systemd的50个可执行程序、34MB的大小都是基于Buildroot的配置。

一般来说,从BusyBox init到 systemd,灵活性和复杂性都会逐渐增加。

BusyBox init

 BusyBox 的 init 程序最小,它使用配置文件/etc/inittab来定义规则,在系统启动阶段控制程序启动,在关机阶段控制程序终止。通常情况下,实际工作是由shell脚本来完成的,而按照惯例,脚本会放置在/etc/init.d目录下。

 init 首先会读取配置文件/etc/inittab。配置文件包含了一个需要运行的程序列表,一行一个,格式如下:

<id>::<action>:<program>
  • id:指令所针对的控制终端
  • action:运行该指令的条件,将在下面的段落展示
  • program:待运行的程序

运行该指令的条件(action字段)的可填写的内容:

  • sysinit:在其他所有类型的操作之前,当初始化开始时,运行程序。
  • respawn:运行指定程序,如果程序终止则重新启动。
  • askfirst:与respawn相同,但是这个会向控制台输出“Please press Enter to activate this console”,并在按下Enter键时运行该程序。它用于在终端上启动一个交互式shell,且不提示输入用户名或密码。
  • once:运行指定程序,如果该程序终止,不会尝试重新启动它。
  • wait:运行指定程序,并等待其完成。
  • restart:当init接收到信号SIGHUP时,这表明其应该重新载入inittab文件,此时运行指定程序。
  • ctrlaltdel:当init接收到信号SIGINT时运行指定程序,这通常是由于用户在控制台按下了 Ctrl+Alt+Del 组合按键。
  • shutdown:当init关闭时运行指定程序。

这是一个完整的实际使用的inittab文件

# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run# Startup the system
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a 2>/dev/null
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::respawn:-/bin/sh
::sysinit:/etc/init.d/rcS# Put a getty on the serial port
#ttyFIQ0::respawn:/sbin/getty -L  ttyFIQ0 0 vt100 # GENERIC_SERIAL# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

/etc/inittab文件中<action>字段的意义

action名称执行条件说明
sysinit系统启动后最先执行只执行一次,init进程等待它结束后才继续执行其他动作
wait系统执行完sysinit进程后只执行一次,init进程等待它结束才继续执行其他动作
once系统执行完wait进程后只执行一次,init进程不等待它结束
respawn启动完once进程后init进程检测发现子进程退出时,重新启动它
askfirst启动完respawn进程后与respawn进程类型,不过init进程先输出“Please press Enter to activate this console”,等用户输入回车键之后才启动子进程。
shutdown当系统关机时执行即重启、关闭系统命令时
restartBusyBox中配置了CONFIG_FEATURE_USE_INITTAB,并且init进程接收到SIGHUP信号时先重新读取、解析/etc/inittab文件,再执行restart程序
ctrlaltdel按下 Ctrl+Alt+Del 组合键时

以下是一个小例子,包括挂载的 proc 和 sysfs ,以及在串行接口运行 shell:

null::sysinit:/bin/mount -t  proc proc /proc
null::sysinit:/bin/mount -t sysfs sysfs /sys
console::askfirst:-/bin/sh

 对于简单的项目,比如你只想启动少量的守护进程,或者是在串口终端启动一个登录shell,手写一个脚本也很容易。这种情况下,如果你创建一个简单的定制的嵌入式Linux是很合适的。然而,你会发现随着需要配置的事情不断增加,手写一个init脚本很快就会变得非常难以维护。

Buildroot init 脚本

  多年以来,Buildroot有效使用了BusyBox 的init程序。Buildroot在/etc/init.d目录中有两个脚本,名为 rcSrcK 。第一个脚本 rcS 在开机时运行,并从一个大写S加两位数字开始遍历所有的脚步,并按数字顺序运行,这就是开始脚本。rcK 脚本在关机时运行,从一个大写K 加两位数字开始遍历所有的脚本,并按数字顺序运行,这就是结束脚本。

 以上的做法也是存在的,实际上更多做法是在目录/etc/init.d里面只有众多的S开头的启动脚本,在S开头的脚本里面通过传入的参数start或者stop来执行开机时的操作逻辑或者关机时的操作逻辑。

一个典型实际使用的rcS脚本:

#!/bin/sh# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do# Ignore dangling symlinks (if any).[ ! -f "$i" ] && continuecase "$i" in*.sh)# Source shell script for speed.(trap - INT QUIT TSTPset start. $i);;*)# No sh extension, so fork subprocess.$i start;;esac
done

一个典型实际使用的rcK脚本:

#!/bin/sh# Stop all init scripts in /etc/init.d
# executing them in reversed numerical order.
#
for i in $(ls -r /etc/init.d/S??*) ;do# Ignore dangling symlinks (if any).[ ! -f "$i" ] && continuecase "$i" in*.sh)# Source shell script for speed.(trap - INT QUIT TSTPset stop. $i);;*)# No sh extension, so fork subprocess.$i stop;;esac
done

一个典型实际使用的S开头的用于alsa系统启动的脚本:
S04alsa.sh

#!/bin/sh
#case "$1" instart)if [ -f "/etc/asound.state" ];thenalsactl restore -f /etc/asound.statefi#enable spkecho 1 > /proc/rp_power/spk_onecho 0 > /proc/rp_power/spk_mute;;stop)echo "not stop function";;*)echo "Usage: $0 {start|stop}"exit 1;;
esac

在操作系统中,一般系统的服务都是以后台进程的方式存在,而且都会常驻系统中,直到关机才结束。这类服务也称Daemon,在Linux系统中就包含许多的Daemon。判断Daemon最简单的方法就是从名称上看。多数的Daemon都是由服务名称加上d。例如,在Linux操作系统中HTTP服务的Deamon就是httpd。

 通过这一套机制,Buildroot包能够很容易地提供自己的开始脚本和结束脚本,并利用两个数字编号影响这些脚本的运行顺序,使其按照应有的顺序执行,从而使系统成为可扩展的。

后记

其他的init程序比如:System V init 和 systemd,笔者也没有使用过,有兴趣可以去看《嵌入式Linux编程》【Chris Simmonds】这本书的【第9章 启动初始化程序】的9.4和9.5小节。

http://www.lryc.cn/news/91002.html

相关文章:

  • 基于Java实现农产品交易平台的设计与实现_kaic
  • 视频转换、视频压缩、录屏等工具合集:迅捷视频工具箱
  • 理解时序数据库的时间线
  • 音视频技术开发周刊 | 295
  • 15稳压二级管
  • 一些零零碎碎的记录
  • MyBatis - Spring Boot 集成 MyBatis
  • 常见开源协议介绍
  • 第十九章行为型模式—中介者模式
  • AKStream部署1:ZLMediaKit流媒体服务器(win)
  • 【Redis】Redis 中地理位置功能 Geospatial 了解一下?
  • Qt Qml 实现键鼠长时间未操作锁屏
  • 常用的数字高程模型(DEM)数据介绍,附免费下载
  • 字节跳动面试挂在2面,复盘后,决定二战.....
  • 简述熔断、限流、降级
  • Maven 工具
  • iptables扩展匹配条件
  • 直播录音时准备一副监听耳机,实现所听即所得,丁一号G800S上手
  • 回归测试最小化(贪心算法,帕累托支配)
  • Python系列模块之标准库shutil详解
  • pb如何播放Flash
  • 独立成分分析ICA
  • 从零开始之如何在React Native中使用导航
  • RAW、RGB 、YUV三种图像格式理解
  • 关于对【mysql存储过程】的理解与简述
  • 贪吃蛇游戏的制作记录
  • Go基础入门
  • JavaScript教程(二)
  • 设计模式之代理模式
  • 初识MySQL