LSB5.0核心内容中文版

目的

LSB(Linux Standard Base)规范了Linux发行版中所必须包含的一系列基本内容。创造它的目的是减少不同发行版之间的不同。LSB极大的降低了在发行版间移植应用的压力,同时也降低了后期对这些程序进行维护的压力。

![[lsb_concept_tools.png]]

目前最新的版本是LSB 5.0。

单元

![[lsb_elements.png]]

LSB由四个顶级单元组成:模块,ELF元素,RPM标签和解释语言。模块又包括系统库和命令,我们在这里只讨论模块。
最新的LSB包含文件可以在 LSB Navigator 获取,也可以在这里进行LSB的认证。

内容

LSB的实现极其依赖FHS标准和GNU Coreutils。

命令规范

一个符合LSB标准的系统必须包含以下基本命令:
Shell内置命令

1 2 3 4 5
alias command getopts read umask
bg fc hash type unalias
cd fg jobs ulimit wait

核心模块命令

1 2 3 4 5
[ du install mv strings
ar echo install_initd newgrp strip
at ed ipcrm nice stty
awk egrep ipcs nl su
basename env join nohup sync
batch expand kill od tail
bc expr killall passwd tar
cat false ln paste tee
chfn fgrep locale patch test
chgrp file localedef pathchk tic
chmod find logger pax time
chown fold logname pidof touch
chsh fuser lp pr tput
cksum gencat lpr printf tr
cmp getconf ls ps true
col gettext lsb_release pwd tsort
comm grep m4 remove_initd tty
cp groupadd mailx renice umount
cpio groupdel make rm uname
crontab groupmod man rmdir unexpand
csplit groups md5sum sed uniq
cut gunzip mkdir sendmail useradd
date gzip mkfifo seq userdel
dd head mknod sh usermod
df hostname mktemp shutdown wc
diff iconv more sleep xargs
dirname id mount sort zcat
dmesg infocmp msgfmt split

桌面模块命令

1 2 3 4 5
fc-cache fc-match xdg-desktop-menu xdg-icon-resource xdg-open
fc-list xdg-desktop-icon xdg-email xdg-mime xdg-screensaver

图像模块命令

1 2 3 4 5
foomatic-rip gs

语言模块命令

1 2 3 4 5
perl python

用户与组的规范

以下这些用户在每个系统中都必须包含:

用户名 组名 作用
root root 拥有最高权限的管理用户
bin bin 对旧版本的兼容性用户和组
daemon daemon 对旧版本的兼容性的用户和组

以下这些用户为可选用户:

用户名 组名 作用
adm adm 特殊管理用户
lp lp 用于打印机的用户
sync sync 登录这个用户,系统会进行同步
shutdown shutdown 登录这个用户,系统会进行关机
halt halt 登录这个用户,系统会进行关机
mail mail 用于发送邮件的用户
news news 用于接收新闻的用户
uucp uucp 用于UUCP的特殊用户
operator root 特殊管理用户
man man 用于手册的用户
nobody nobody 用于网络存储的用户

定时任务规范

除了在FHS 3.0中规定的/var/spool/cron目录外,crontab还必须执行以下这些目录或文件中的计划任务:

  • /etc/crontab
  • /etc/cron.d/*

任何软件包都不能直接修改这两个文件,它们只能被用户手动修改。

如果一个cron程序还支持更宽时间的计划任务,那么这些任务应该放在以下目录:

  • /etc/cron.hourly
  • /etc/cron.daily
  • /etc/cron.weekly
  • /etc/cron.monthly

顾名思义,这些文件分别会被每小时,每天,每周,每月执行一次。

如果软件包要创建自己的定时任务,应当安装在/etc/cron.d/cron-name,内容包含以下七项:

  1. 分,0-59
  2. 时,0-23
  3. 日,1-31
  4. 月,1-12
  5. 周,0-6
  6. 用户名
  7. 命令

用户名可以省略,以表示使用系统级用户运行。任务会按照指定的时间以指定的用户执行指定的命令。

初始化脚本规范(重点)

主干内容
一个想要在开机时启动的程序必须提供对应的一个或多个初始化脚本,初始化脚本可以接受以下几个参数:

参数 含义
start 启动服务
stop 停止服务
restart 如果服务在运行,则重启服务;否则启动服务
try-restart 如果服务在运行,则重启服务;否则什么也不做
reload 重新加载服务的配置文件,而不重新运行
force-reload 重新加载服务的配置文件,如果不成功,则重启服务
status 打印服务状态

初始化脚本必须至少支持start,stop,restart,force-reload和status参数,reload和try-restart参数为可选项,初始化脚本也可以自行添加其他的参数。

初始化脚本必须确保在执行start或stop时的行为是可感知的,而且绝不能杀死其他不相干的进程。最好的实现办法是使用/lib/lsb/init-functions中提供的函数。

当执行reload时,服务会重载它的配置文件,reload应当假定重载行为是成功的。

restart,try-restart,reload和force-reload的行为应当是原子化的,这就是说如果一个服务知道自己重启或重载后不能工作,它应该返回错误并拒绝执行相应的命令。

status命令应当在以下情况下返回对应的值:

返回值 含义
0 服务正常运行
1 服务已经停止,但是PID文件仍然存在
2 服务已经停止,但是锁文件仍然存在
3 服务没有运行
4 服务状态未知

对于其他的命令,如果执行成功,初始化脚本应当返回0,否则返回非0值。除了一般概念上的“执行成功”,成功的定义还应当额外包含以下几种:

  • 通过force-reload命令重启服务
  • 在服务正在运行的情况下执行start
  • 在服务尚未运行的情况下执行stop
  • 在服务尚未运行的情况下执行restart
  • 在服务尚未运行的情况下执行try-restart

在执行初始化脚本遇到错误时(除了status命令),脚本不仅需要返回非零值,而且还需要打印错误信息,两者之间的对应关系如下:

返回值 错误信息
1 通用或未定义的错误
2 不合法的参数
3 尚未实现的功能
4 用户权限不足
5 程序尚未安装
6 程序缺少配置
7 程序尚未运行

错误信息应当通过log_success_msg()log_failure_msg()log_warning_msg()函数打印,可以打印在标准输出流或标准错误流上,但是不必特别显示给用户。

注释内容
初始化脚本必须通过在软件包安装时通过/usr/lib/lsb/install_initd程序激活,在软件包卸载之前通过/usr/lib/lsb/remove_initd程序卸载。
install_initdremove_initd程序根据脚本中的一组特殊格式的注释来判断行为,这一组注释的位置通过这两行注释确定:

1
2
3
### BEGIN INIT INFO
...
### END INIT INFO

定界行的开头可以有空格,会被自动无视。任何定界行内的注释块都必须以#开头,以防止Shell解释器将它们解释运行。每一行的格式都像下面这样:

1
# 关键字: 参数1 [参数2...]

#和关键字之间,:和参数之间都有且只有一个空格,但是有一个例外,在Description关键字的下一行,直到下一个关键字之前,#之后可以有多个空格,它们会作为上一行的延续内容,被安全的无视。
通过注释块被解析出来的内容会被安装工具或初始化系统使用,确保初始化脚本会按照正确的顺序运行。收集到的信息会在install_initd时使用,还是在运行脚本时使用,这件事是无法确定的。注释块中的信息必须包括运行等级和启动设施。
注释块可以使用以下关键字:

  • Provides: 设施1 [设施2...]
    初始化脚本提供的启动设施,当脚本通过start参数启动时,对应的启动设施会被认为已经启动,因而需要这些启动设施的启动脚本后续也会逐步启动。当脚本通过stop参数停止时,对应的启动设施会被认为不再存在。
  • Required-Start: 设施1 [设施2...]
    这个服务启动时必须依赖的启动设施。初始化系统必须确保提供这个启动设施的初始化脚本提前已经启动了。
  • Required-Stop: 设施1 [设施2...]
    这个服务停止时必须依赖的启动设施。初始化系统必须确保提供这个启动设施的初始化脚本还没有被停止。
  • Should-Start: 设施1 [设施2...]
    如果存在,应当在这个服务启动时可用的启动设施。这允许给服务设置弱依赖,如果对应的启动设施不存在,也不会导致服务启动失败。但是,服务也可能在启动后缺少功能。应用的工作不能依赖这个功能。
  • Should-Stop: 设施1 [设施2...]
    同理,应道在这个服务停止时可用的启动设施。
  • Default-Start: 运行等级1 [运行等级2...]
    Default-Stop: 运行等级1 [运行等级2...]
    默认情况下初始化脚本应当执行start(或stop)的运行等级。两者一般是相反的,例如,如果一个脚本只能在运行等级3,4和5运行,那么就需要指定Default-Start: 3 4 5Default-Stop: 0 1 2 6
  • Short-Description: 短描述
    对初始化脚本的功能进行简短的描述,只能有一行。
  • Description: 长描述
    对初始化脚本的功能进行详尽的描述。可以占用多行,每行必须以#开头,后面必须有至少两个空格。多行注释会在遇到第一行不符合这个规则的内容时视为终止。
    例如,以下是一个规范的初始化脚本注释块:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ### BEGIN INIT INFO
    # Provides: lsb-ourdb
    # Required-Start: $local_fs $network $remote_fs
    # Required-Stop: $local_fs $network $remote_fs
    # Default-Start: 2 3 4 5
    # Default-Stop: 0 1 6
    # Short-Description: start and stop OurDB
    # Description: OurDB is a very fast and reliable database
    # engine used for illustrating init scripts
    ### END INIT INFO
    注意,以上注释块仅用于需要进行安装后在开机时启动的初始化脚本。需要在运行时通过程序唤醒的初始化脚本不需要遵守这个规则。

安装初始化脚本
初始化脚本应当放置在/etc/init.d,然后软链接到其他路径。
要安装初始化脚本,必须通过/usr/lib/lsb/install_initd程序实现,激活初始化脚本的行为包括根据脚本的依赖分析正确的脚本启动顺序,然后将脚本软链接到合适的run-level目录中。
install_initd目录可以被认为是一个对安装过程具体实现的封装,用户不需要关心初始化脚本的具体启动时间。
例如:如果一个脚本的设定为Default-Start: 3 4 5Default-Stop: 0 1 2 6,那么install_initd会在/etc/rc3.d/etc/rc4.d/etc/rc5.d目录下创建以S开头的脚本软链接,它会在启动时自动执行start参数;而且会在/etc/rc0.d/etc/rc1.d/etc/rc2.d/etc/rc6.d目录下创建以K开头的脚本软链接,它会在停止时自动执行stop参数。
这样一种模式类似于System V Init的原理,但是绝不是唯一的实现方法。
install_initd接受单个参数,参数应当是初始化脚本的完整路径。再次强调,初始化脚本必须安装在/etc/init.d目录下。install_initd并不会拷贝初始化脚本,只会激活它。
例如:/usr/lib/lsb/install_initd /etc/init.d/example
install_initd命令会在激活成功或脚本已经激活时返回0。如果初始化脚本的依赖无法满足,那么程序会返回1,初始化脚本也不会被激活。
当软件包被移除时,/usr/lib/lsb/remove_initd程序必须运行,移除对应的初始化脚本。这一行为必须执行在脚本源文件被移除之前,因为依赖信息来自于脚本源文件。因此卸载程序的pre-remove阶段必须调用remove_initd,以初始化脚本的完整路径作为参数。卸载脚本本身还必须承担删除脚本源文件的责任,因为remove_initd程序本身不会删除脚本的源文件。
例如:/usr/lib/lsb/remove_initd /etc/init.d/example
remove_initd命令会在卸载成功或脚本已经卸载时返回0。如果卸载初始化脚本会破坏其他脚本对启动设施的依赖,那么程序会返回1,初始化脚本也不会被卸载。安装程序此时也必须停止运行,因为remove_initd程序本身不会删除脚本的源文件。

运行等级
脚本的运行等级通过Default-Start:Default-Stop:关键字声明,install_initd程序会利用这个信息。
系统并不需要完全实现所有的运行等级,可以将一个运行等级定为另一个运行等级的别名,提供完全相同的功能。程序不能依赖特定的运行等级号。

运行等级号 含义
0 关机
1 单用户模式
2 多用户文字终端,离线模式
3 标准的多用户文字终端模式
4 等同于3
5 多用户GUI模式
6 重新启动

启动设施
启动设施用于标识初始化脚本的依赖。设施名通过Provides:关键字声明,以$开头的设施名代表着系统设施名,一般的应用不能提供以$开头的设施。
以下为系统设施及其含义:

设施名 含义
$local_fs 已挂载好本地文件系统
$network 已启动基本的网络支持
$named DNS服务已经启动
$portmap RPC服务已经启动
$remote_fs 已挂载好远程文件系统
$syslog syslog服务已启动
$time 网络时间服务已启动,系统时间已经校准

其他非系统的设施可以被其他应用程序定义,这些设施可以用初始化脚本名本身或其变体进行命名。一般来说,使用的都是初始化脚本名本身。

函数库
/lib/lsb/init-functions文件中提供了一系列实用函数,任何规范的初始化脚本都应该通过. /lib/lsb/init-functions引入这些函数。此外,初始化脚本也不能设置-e选项,以防意外退出。
这个文件包含了以下这些函数:

  • start_daemon [-f] [-n NICE等级] [-p PID文件] 路径 [参数]
    以守护进程状态运行指定的程序。这个函数应当检查程序是否已经运行,如果是,那么不再创建另一个守护进程,除非提供了-f选项,-n选项用于指定NICE等级。这个函数应当在程序成功启动或已经在运行时返回0,否则返回非0值。
  • killproc [-p PID文件] 路径 [信号]
    这个函数用于停止程序。如果指定了信号,那么会通过指定的信号杀死程序,否则会连续发送一个SIGTERM信号和一个SIGKILL以确保程序被杀死。如果程序被停止,那么PID文件也应该被移除;如果程序已经被停止,但是仍然可以找到PID文件,那么就直接移除PID文件。这个函数应当在程序成功停止或已经停止时返回0,否则返回非0值。如果指定了信号,那么这个函数应当仅在程序成功停止返回0。
  • pidofproc [-p PID文件] 路径
    这个函数应当返回某个守护进程的一个或多个进程的标识符。只有运行中的进程的标识符应当被返回。多个标识符通过一个空格分隔。这个函数应当在程序在正常运行时返回0,否则返回非0值。
  • log_success_msg 信息
    这个函数用于在日志文件中写入成功信息,信息不限格式,但是不能长于60个字符,信息也可能被写入标准输出。
  • log_failure_msg 信息
    这个函数用于在日志文件中写入失败信息,信息不限格式,但是不能长于60个字符,信息也可能被写入标准输出。
  • log_warning_msg 信息
    这个函数用于在日志文件中写入警告信息,信息不限格式,但是不能长于60个字符,信息也可能被写入标准输出。

start_daemonkillprocpidofproc函数应当使用以下算法确定程序的状态和进程标识符:

  1. 如果指定了-p PID文件选项,而且PID文件确实存在,那么读取PID文件的开头第一行。如果这一行包含以空格为分隔符的一个或多个数字,那么使用这些数字作为进程标识符;如果PID文件并不存在,那么函数应当假定程序并没有运行。
  2. 如果没有指定-p PID文件选项,那么读取/var/run/文件名.pid文件的开头第一行。如果这一行包含以空格为分隔符的一个或多个数字,那么使用这些数字作为进程标识符。当然,也可以使用一些其他的方法确定进程标识符,但是,仅限于没有指定-p PID文件选项的情况下才能使用这些方法。

确定程序状态的方法尚不确定,但是应当适用于非二进制程序。
当一个程序停止时,它应当删除自己的PID文件,多个进程的标识符在PID文件中以单个空格为分隔符进行显示,可以通过pidofproc函数获取。