SysVinit 服务管理

章节标题

SysVinit是Linux系统中传统的服务管理系统,虽然现代Linux发行版已经广泛使用systemd,但了解SysVinit仍然很重要,特别是对于维护旧系统或理解服务管理的基本概念。本章节将详细介绍SysVinit的基本概念、运行级别、服务脚本结构、管理命令和实际应用。

核心知识点讲解

1. SysVinit的基本概念

SysVinit的定义

SysVinit是基于System V Unix的初始化系统,它是Linux系统中最早使用的服务管理系统,负责在系统启动时初始化系统,管理服务的启动、停止和重启等操作。

SysVinit的特点

  • 基于运行级别:将系统分为不同的运行级别(0-6)
  • 使用shell脚本:每个服务对应一个shell脚本,位于/etc/init.d/目录
  • 串行启动:服务按顺序启动,启动速度较慢
  • 依赖管理:通过脚本中的注释声明依赖关系
  • 简单直观:使用简单的shell脚本,易于理解和修改
  • 广泛兼容:几乎所有Linux发行版都支持SysVinit脚本

2. 运行级别

运行级别的定义

运行级别(Runlevel)是SysVinit中用于定义系统状态的概念,不同的运行级别对应不同的服务启动集合。

标准运行级别

运行级别 描述 对应的systemd目标
0 关机 poweroff.target
1 单用户模式 rescue.target
2 多用户模式(无网络) multi-user.target
3 多用户模式(有网络) multi-user.target
4 未使用(自定义) multi-user.target
5 图形界面模式 graphical.target
6 重启 reboot.target

运行级别的配置

  • 默认运行级别:存储在/etc/inittab文件中
  • 查看当前运行级别:使用runlevel命令
  • 切换运行级别:使用init命令

3. 服务脚本结构

服务脚本的位置

SysVinit服务脚本通常位于以下目录:

  • /etc/init.d/:主要的服务脚本目录
  • /etc/rc.d/init.d/:某些发行版(如CentOS)的服务脚本目录

服务脚本的结构

一个标准的SysVinit服务脚本通常包含以下部分:

  1. 脚本头部:包含脚本的描述、作者、版权等信息
  2. 依赖声明:声明服务的依赖关系
  3. 函数定义:定义start、stop、restart等函数
  4. 参数处理:处理命令行参数
  5. 执行相应的函数:根据命令行参数执行相应的操作

服务脚本的示例结构

#!/bin/sh
### BEGIN INIT INFO
# Provides:          servicename
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Should-Start:      $network
# Should-Stop:       $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Description of the service
# Description:       Detailed description of the service
### END INIT INFO

# 函数定义
start() {
    echo "Starting service..."
    # 启动服务的命令
}

stop() {
    echo "Stopping service..."
    # 停止服务的命令
}

restart() {
    stop
    start
}

# 参数处理
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        # 查看服务状态的命令
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0

4. 服务管理命令

service命令

service命令是管理SysVinit服务的主要命令,它可以启动、停止、重启和查看服务的状态。

# 查看服务状态
service servicename status

# 启动服务
service servicename start

# 停止服务
service servicename stop

# 重启服务
service servicename restart

# 重新加载服务配置
service servicename reload

# 查看所有服务的状态
service --status-all

chkconfig命令

chkconfig命令用于管理服务的开机自启,它可以启用或禁用服务在特定运行级别的自动启动。

# 查看服务的开机自启状态
chkconfig --list servicename

# 启用服务的开机自启
chkconfig servicename on

# 禁用服务的开机自启
chkconfig servicename off

# 在特定运行级别启用服务
chkconfig --level 35 servicename on

# 在特定运行级别禁用服务
chkconfig --level 35 servicename off

# 添加新服务
chkconfig --add servicename

# 删除服务
chkconfig --del servicename

update-rc.d命令

update-rc.d命令是Debian/Ubuntu系统中用于管理服务开机自启的命令,它可以在/etc/rc*.d/目录中创建或删除服务的启动链接。

# 启用服务的开机自启
update-rc.d servicename defaults

# 启用服务的开机自启(指定优先级)
update-rc.d servicename defaults 99

# 禁用服务的开机自启
update-rc.d servicename remove

# 在特定运行级别启用服务
update-rc.d servicename start 20 2 3 4 5 . stop 80 0 1 6 .

# 强制删除服务的启动链接
update-rc.d -f servicename remove

5. 服务启动链接

启动链接的位置

SysVinit使用符号链接来控制服务在不同运行级别的启动和停止,这些链接位于/etc/rc*.d/目录中:

  • /etc/rc0.d/:运行级别0(关机)
  • /etc/rc1.d/:运行级别1(单用户模式)
  • /etc/rc2.d/:运行级别2(多用户模式,无网络)
  • /etc/rc3.d/:运行级别3(多用户模式,有网络)
  • /etc/rc4.d/:运行级别4(未使用)
  • /etc/rc5.d/:运行级别5(图形界面模式)
  • /etc/rc6.d/:运行级别6(重启)

启动链接的命名规则

启动链接的命名遵循以下规则:

  • Sxxservicename:表示在该运行级别启动服务,xx是启动顺序(00-99)
  • Kxxservicename:表示在该运行级别停止服务,xx是停止顺序(00-99)

启动顺序的重要性

启动顺序决定了服务的启动顺序,数字越小的服务越先启动,数字越大的服务越后启动。这对于有依赖关系的服务非常重要,例如网络服务应该在依赖网络的服务之前启动。

6. SysVinit与systemd的兼容性

systemd对SysVinit的支持

现代的systemd系统仍然支持SysVinit脚本,它会将SysVinit脚本转换为systemd服务单元。

兼容性层

systemd提供了一个兼容性层,允许SysVinit脚本在systemd系统中正常工作:

  • /lib/systemd/system-generators/systemd-sysv-generator:将SysVinit脚本转换为systemd服务单元
  • **/etc/systemd/system/sysinit.target.wants/**:包含SysVinit服务的启动链接
  • systemctl:可以管理SysVinit服务,就像管理systemd服务一样

注意事项

在systemd系统中使用SysVinit脚本时,需要注意以下几点:

  • 启动速度:SysVinit脚本在systemd系统中可能启动较慢
  • 功能限制:SysVinit脚本不支持systemd的高级功能,如并行启动、socket激活等
  • 日志管理:SysVinit脚本的日志可能不会被journald捕获
  • 依赖管理:需要手动管理依赖关系

实用案例分析

案例1:管理SysVinit服务

场景描述

在使用SysVinit的旧系统中,需要管理SSH服务,包括启动、停止、重启、查看状态和配置开机自启。

解决方案

使用service和chkconfig命令管理SSH服务:

# 查看SSH服务状态
service ssh status

# 启动SSH服务
service ssh start

# 停止SSH服务
service ssh stop

# 重启SSH服务
service ssh restart

# 重新加载SSH配置
service ssh reload

# 启用SSH服务的开机自启
chkconfig ssh on

# 禁用SSH服务的开机自启
chkconfig ssh off

# 查看SSH服务的开机自启状态
chkconfig --list ssh

# 在特定运行级别启用SSH服务
chkconfig --level 35 ssh on

案例2:创建自定义SysVinit服务

场景描述

需要创建一个自定义的SysVinit服务脚本,用于管理一个后台应用程序,确保它在系统启动时自动启动,并在系统关闭时正确停止。

解决方案

创建一个自定义的SysVinit服务脚本:

# 创建服务脚本
sudo vi /etc/init.d/myapp

# 添加以下内容
#!/bin/sh
### BEGIN INIT INFO
# Provides:          myapp
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Should-Start:      $network
# Should-Stop:       $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: My Custom Application
# Description:       A custom application that runs in the background
### END INIT INFO

# 应用程序路径
APP_PATH="/usr/local/bin/myapp"
APP_PIDFILE="/var/run/myapp.pid"
APP_USER="myuser"

# 函数定义
start() {
    echo "Starting myapp..."
    if [ -f $APP_PIDFILE ]; then
        echo "myapp is already running."
        exit 1
    fi
    # 以指定用户启动应用程序
    su - $APP_USER -c "$APP_PATH > /dev/null 2>&1 & echo $! > $APP_PIDFILE"
    echo "myapp started."
}

stop() {
    echo "Stopping myapp..."
    if [ ! -f $APP_PIDFILE ]; then
        echo "myapp is not running."
        exit 1
    fi
    # 停止应用程序
    kill $(cat $APP_PIDFILE)
    rm -f $APP_PIDFILE
    echo "myapp stopped."
}

restart() {
    stop
    start
}

status() {
    if [ -f $APP_PIDFILE ]; then
        if ps -p $(cat $APP_PIDFILE) > /dev/null; then
            echo "myapp is running."
            exit 0
        else
            echo "myapp PID file exists but process is not running."
            exit 1
        fi
    else
        echo "myapp is not running."
        exit 3
    fi
}

# 参数处理
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0

# 设置脚本执行权限
sudo chmod +x /etc/init.d/myapp

# 添加服务到chkconfig
sudo chkconfig --add myapp

# 启用服务的开机自启
sudo chkconfig myapp on

# 启动服务
sudo service myapp start

# 查看服务状态
sudo service myapp status

案例3:在systemd系统中使用SysVinit脚本

场景描述

在使用systemd的现代系统中,需要使用一个旧的SysVinit服务脚本,确保它能够正常工作并在系统启动时自动启动。

解决方案

在systemd系统中使用SysVinit脚本:

# 创建SysVinit服务脚本
sudo vi /etc/init.d/legacy-service

# 添加服务脚本内容(参考案例2)

# 设置脚本执行权限
sudo chmod +x /etc/init.d/legacy-service

# 重新加载systemd配置
sudo systemctl daemon-reload

# 查看服务状态
sudo systemctl status legacy-service

# 启动服务
sudo systemctl start legacy-service

# 停止服务
sudo systemctl stop legacy-service

# 重启服务
sudo systemctl restart legacy-service

# 启用服务的开机自启
sudo systemctl enable legacy-service

# 禁用服务的开机自启
sudo systemctl disable legacy-service

# 查看服务的开机自启状态
sudo systemctl is-enabled legacy-service

# 查看服务是否运行
sudo systemctl is-active legacy-service

代码示例

示例1:SysVinit服务管理命令

# 查看所有服务的状态
service --status-all

# 查看特定服务的状态
service ssh status

# 启动服务
service ssh start

# 停止服务
service ssh stop

# 重启服务
service ssh restart

# 重新加载服务配置
service ssh reload

# 查看服务的开机自启状态(Red Hat/CentOS)
chkconfig --list ssh

# 启用服务的开机自启(Red Hat/CentOS)
chkconfig ssh on

# 禁用服务的开机自启(Red Hat/CentOS)
chkconfig ssh off

# 启用服务的开机自启(Debian/Ubuntu)
update-rc.d ssh defaults

# 禁用服务的开机自启(Debian/Ubuntu)
update-rc.d ssh remove

# 查看服务的启动链接
ls -la /etc/rc*.d/*ssh*

示例2:SysVinit服务脚本

简单服务脚本示例

#!/bin/sh
### BEGIN INIT INFO
# Provides:          simple-service
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Simple Example Service
# Description:       A simple example of a SysVinit service script
### END INIT INFO

start() {
    echo "Starting simple-service..."
    # 启动命令
    /usr/local/bin/simple-service > /dev/null 2>&1 &
    echo $! > /var/run/simple-service.pid
    echo "simple-service started."
}

stop() {
    echo "Stopping simple-service..."
    if [ -f /var/run/simple-service.pid ]; then
        kill $(cat /var/run/simple-service.pid)
        rm -f /var/run/simple-service.pid
    fi
    echo "simple-service stopped."
}

restart() {
    stop
    start
}

status() {
    if [ -f /var/run/simple-service.pid ]; then
        if ps -p $(cat /var/run/simple-service.pid) > /dev/null; then
            echo "simple-service is running."
            return 0
        else
            echo "simple-service PID file exists but process is not running."
            return 1
        fi
    else
        echo "simple-service is not running."
        return 3
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0

复杂服务脚本示例

#!/bin/sh
### BEGIN INIT INFO
# Provides:          complex-service
# Required-Start:    $remote_fs $syslog $network
# Required-Stop:     $remote_fs $syslog $network
# Should-Start:      $named $time
# Should-Stop:       $named $time
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Complex Example Service
# Description:       A complex example of a SysVinit service script with dependencies
### END INIT INFO

# 配置变量
DAEMON="/usr/local/bin/complex-service"
DAEMON_ARGS="--config=/etc/complex-service.conf"
DAEMON_USER="complex"
PIDFILE="/var/run/complex-service.pid"
LOCKFILE="/var/lock/subsys/complex-service"
LOGFILE="/var/log/complex-service.log"

# 加载系统函数库
if [ -f /etc/init.d/functions ]; then
    . /etc/init.d/functions
elif [ -f /lib/lsb/init-functions ]; then
    . /lib/lsb/init-functions
fi

# 函数定义
start() {
    echo -n "Starting complex-service: "
    
    # 检查守护进程是否存在
    if [ ! -x $DAEMON ]; then
        echo "Daemon not found at $DAEMON"
        return 1
    fi
    
    # 检查配置文件是否存在
    if [ ! -f /etc/complex-service.conf ]; then
        echo "Configuration file not found at /etc/complex-service.conf"
        return 1
    fi
    
    # 检查服务是否已经运行
    if [ -f $PIDFILE ]; then
        if ps -p $(cat $PIDFILE) > /dev/null; then
            echo "Service is already running."
            return 0
        else
            rm -f $PIDFILE
        fi
    fi
    
    # 启动服务
    if [ -f /etc/init.d/functions ]; then
        daemon --user=$DAEMON_USER "$DAEMON $DAEMON_ARGS > $LOGFILE 2>&1 & echo $! > $PIDFILE"
    else
        su - $DAEMON_USER -c "$DAEMON $DAEMON_ARGS > $LOGFILE 2>&1 & echo $! > $PIDFILE"
        echo "Starting..."
    fi
    
    # 创建锁文件
    touch $LOCKFILE
    
    echo "[OK]"
    return 0
}

stop() {
    echo -n "Stopping complex-service: "
    
    # 检查服务是否运行
    if [ ! -f $PIDFILE ]; then
        echo "Service is not running."
        return 0
    fi
    
    # 停止服务
    if kill $(cat $PIDFILE) > /dev/null 2>&1; then
        # 等待进程结束
        for i in {1..10}; do
            if ! ps -p $(cat $PIDFILE) > /dev/null 2>&1; then
                break
            fi
            sleep 1
        done
        
        # 强制终止
        if ps -p $(cat $PIDFILE) > /dev/null 2>&1; then
            kill -9 $(cat $PIDFILE) > /dev/null 2>&1
            sleep 1
        fi
        
        rm -f $PIDFILE
        rm -f $LOCKFILE
        echo "[OK]"
        return 0
    else
        echo "[FAILED]"
        return 1
    fi
}

restart() {
    stop
    start
}

reload() {
    echo -n "Reloading complex-service configuration: "
    
    if [ -f $PIDFILE ]; then
        if kill -HUP $(cat $PIDFILE) > /dev/null 2>&1; then
            echo "[OK]"
            return 0
        else
            echo "[FAILED]"
            return 1
        fi
    else
        echo "Service is not running."
        return 3
    fi
}

status() {
    if [ -f $PIDFILE ]; then
        if ps -p $(cat $PIDFILE) > /dev/null 2>&1; then
            echo "complex-service is running."
            return 0
        else
            echo "complex-service PID file exists but process is not running."
            return 1
        fi
    else
        echo "complex-service is not running."
        return 3
    fi
}

# 参数处理
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    reload)
        reload
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|reload|status}"
        exit 1
        ;;
esac

exit $?

实践练习

练习1:使用SysVinit管理服务

  1. 查看系统中所有的SysVinit服务
  2. 管理SSH服务(启动、停止、重启、查看状态)
  3. 配置SSH服务的开机自启
  4. 禁用不需要的服务
  5. 查看服务的启动链接

练习2:创建自定义SysVinit服务

  1. 编写一个简单的后台脚本
  2. 创建一个SysVinit服务脚本,包含start、stop、restart和status功能
  3. 设置脚本执行权限并添加到chkconfig
  4. 启用服务的开机自启
  5. 启动服务并查看状态
  6. 测试服务的自动重启功能

练习3:在systemd系统中使用SysVinit脚本

  1. 创建一个SysVinit服务脚本
  2. 在systemd系统中启动和停止服务
  3. 配置服务的开机自启
  4. 查看服务的状态和日志
  5. 比较SysVinit脚本和systemd服务的启动速度

总结

本章节详细介绍了Linux系统中传统的SysVinit服务管理系统的基本概念、运行级别、服务脚本结构、管理命令和实际应用,包括:

  1. SysVinit的基本概念:了解SysVinit是基于System V Unix的初始化系统,使用shell脚本管理服务

  2. 运行级别:了解不同运行级别的含义和用途

  3. 服务脚本结构:掌握SysVinit服务脚本的基本结构和编写方法

  4. 服务管理命令

    • service:管理服务的启动、停止、重启和状态
    • chkconfig:管理服务的开机自启(Red Hat/CentOS)
    • update-rc.d:管理服务的开机自启(Debian/Ubuntu)
  5. 服务启动链接:了解启动链接的位置、命名规则和启动顺序的重要性

  6. SysVinit与systemd的兼容性:了解在systemd系统中使用SysVinit脚本的方法和注意事项

通过本章节的学习,读者可以理解传统的SysVinit服务管理系统的原理和操作方法,掌握创建和管理SysVinit服务的技能,为维护旧系统或理解服务管理的基本概念打下基础。

« 上一篇 systemd 服务管理 下一篇 » 服务启动与停止