美文网首页
Linux中 service 命令浅析

Linux中 service 命令浅析

作者: 量U移动广告归因 | 来源:发表于2021-07-02 21:35 被阅读0次

    Service命令在不同的Linux发行版中都有不同的实现,并且同一发行版不同的版本号之间功能也不尽相同
    但无论怎么变化,Service命令永远都只是一组脚本
    下面对比的是 CentOS6.5、CentOS8.3以及Ubuntu20.04,Service命令的代码变化

    //CentOS6.5 位于/sbin/service
    #!/bin/sh
    
    . /etc/init.d/functions
    
    VERSION="$(basename $0) ver. 0.91"
    USAGE="Usage: $(basename $0) < option > | --status-all | \
    [ service_name [ command | --full-restart ] ]"
    SERVICE=
    SERVICEDIR="/etc/init.d"
    OPTIONS=
    
    if [ $# -eq 0 ]; then
       echo "${USAGE}" >&2
       exit 1
    fi
    
    cd /
    while [ $# -gt 0 ]; do
      case "${1}" in
        --help | -h | --h* )
           echo "${USAGE}" >&2
           exit 0
           ;;
        --version | -V )
           echo "${VERSION}" >&2
           exit 0
           ;;
        *)
           if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
              cd ${SERVICEDIR}
              for SERVICE in * ; do
                case "${SERVICE}" in
                  functions | halt | killall | single| linuxconf| kudzu)
                      ;;
                  *)
                    if ! is_ignored_file "${SERVICE}" \
                && [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                      env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
                    fi
                    ;;
                esac
              done
              exit 0
           elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
              SERVICE="${1}"
              if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
                env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
                exit $?
              fi
           elif [ -z "${SERVICE}" ]; then
             SERVICE="${1}"
           else
             OPTIONS="${OPTIONS} ${1}"
           fi
           shift
           ;;
       esac
    done
    
    if [ -f "${SERVICEDIR}/${SERVICE}" ]; then
       env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" ${OPTIONS}
    else
       echo $"${SERVICE}: unrecognized service" >&2
       exit 1
    fi
    
    //CentOS8.3 位于/sbin/service 实际链接指向 /usr/sbin/service
    #!/bin/bash
    
    . /etc/init.d/functions
    
    VERSION="$(basename $0) ver. 1.1"
    USAGE="Usage: $(basename $0) < option > | --status-all | \
    [ service_name [ command | --full-restart ] ]"
    SERVICEDIR="/etc/init.d"
    ACTIONDIR="/usr/libexec/initscripts/legacy-actions"
    SERVICE=
    ACTION=
    OPTIONS=
    
    if [ $# -eq 0 ]; then
       echo "${USAGE}" >&2
       exit 1
    fi
    
    cd /
    while [ $# -gt 0 ]; do
      case "${1}" in
        --help | -h | --h* )
           echo "${USAGE}" >&2
           exit 0
           ;;
        --version | -V )
           echo "${VERSION}" >&2
           exit 0
           ;;
        --ignore-dependencies)
           export SYSTEMCTL_IGNORE_DEPENDENCIES=1
           shift
           ;;
        --skip-redirect)
           export SYSTEMCTL_SKIP_REDIRECT=1
           shift
           ;;
        *)
           if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
              cd ${SERVICEDIR}
              for SERVICE in * ; do
                case "${SERVICE}" in
                  functions | halt | killall | single| linuxconf| kudzu)
                      ;;
                  *)
                    if ! is_ignored_file "${SERVICE}" \
                && [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                      env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
                    fi
                    ;;
                esac
              done
              exit 0
           elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
              SERVICE="${1}"
              if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
                env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
                exit $?
              fi
           elif [ -z "${SERVICE}" ]; then
             SERVICE="${1}"
           elif [ -z "${ACTION}" ]; then
             ACTION="${1}"
           else
             OPTIONS="${OPTIONS} ${1}"
           fi
           shift
           ;;
       esac
    done
    
    if [ -f "${SERVICEDIR}/${SERVICE}" ]; then
       # LSB daemons that dies abnormally in systemd looks alive in systemd's eyes due to RemainAfterExit=yes
       # lets reap them before next start
       if [ "${ACTION}" = "start" ] && \
       systemctl show -p ActiveState ${SERVICE}.service | grep -q '=active$' && \
       systemctl show -p SubState ${SERVICE}.service | grep -q '=exited$' ; then
           /bin/systemctl stop ${SERVICE}.service
       fi
       # Workaround to be able to "stop" network.service when it's in inactive state using service instead of systemctl
       # Useful for manual testing of network
       if [ "${SERVICE}" = 'network' ] && [ "${ACTION}" = 'stop' ] && \
       [ "$(systemctl show -p ActiveState network.service --value)" = 'inactive' ] && \
       [ "$(systemctl show -p SourcePath network.service --value)" = '/etc/rc.d/init.d/network' ]; then
           export SYSTEMCTL_SKIP_REDIRECT=1
       fi
       env -i PATH="$PATH" TERM="$TERM" SYSTEMCTL_IGNORE_DEPENDENCIES=${SYSTEMCTL_IGNORE_DEPENDENCIES} SYSTEMCTL_SKIP_REDIRECT=${SYSTEMCTL_SKIP_REDIRECT} "${SERVICEDIR}/${SERVICE}" ${ACTION} ${OPTIONS}
    elif [ -n "${ACTION}" ] && [ -x "${ACTIONDIR}/${SERVICE}/${ACTION}" ]; then
       env -i PATH="$PATH" TERM="$TERM" SYSTEMCTL_IGNORE_DEPENDENCIES=${SYSTEMCTL_IGNORE_DEPENDENCIES} SYSTEMCTL_SKIP_REDIRECT=${SYSTEMCTL_SKIP_REDIRECT} "${ACTIONDIR}/${SERVICE}/${ACTION}" ${OPTIONS}
    elif `echo $ACTION | grep -Eqw "start|stop|restart|try-restart|reload|force-reload|status|condrestart"` ; then
       SERVICE_MANGLED=$(/usr/bin/systemd-escape --mangle ${SERVICE})
       echo $"Redirecting to /bin/systemctl ${ACTION}${OPTIONS:+ }${OPTIONS} ${SERVICE_MANGLED}" >&2
       exec /bin/systemctl ${ACTION} ${OPTIONS} ${SERVICE_MANGLED}
    else
       echo $"The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, force-reload, status). For other actions, please try to use systemctl." >&2
       exit 2
    fi
    
    //Ubuntu20.04 位于/usr/sbin/service
    #!/bin/bash
    
    ###########################################################################
    # /usr/bin/service
    #
    # A convenient wrapper for the /etc/init.d init scripts.
    #
    # This script is a modified version of the /sbin/service utility found on
    # Red Hat/Fedora systems (licensed GPLv2+).
    #
    # Copyright (C) 2006 Red Hat, Inc. All rights reserved.
    # Copyright (C) 2008 Canonical Ltd.
    #   * August 2008 - Dustin Kirkland <kirkland@canonical.com>
    # Copyright (C) 2013 Michael Stapelberg <stapelberg@debian.org>
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation; either version 2 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    #
    # On Debian GNU/Linux systems, the complete text of the GNU General
    # Public License can be found in `/usr/share/common-licenses/GPL-2'.
    ###########################################################################
    
    
    is_ignored_file() {
        case "$1" in
            skeleton | README | *.dpkg-dist | *.dpkg-old | rc | rcS | single | reboot | bootclean.sh)
                return 0
            ;;
        esac
        return 1
    }
    
    VERSION="`basename $0` ver. 1.57"
    USAGE="Usage: `basename $0` < option > | --status-all | \
    [ service_name [ command | --full-restart ] ]"
    SERVICE=
    ACTION=
    SERVICEDIR="/etc/init.d"
    OPTIONS=
    is_systemd=
    
    
    if [ $# -eq 0 ]; then
       echo "${USAGE}" >&2
       exit 1
    fi
    
    if [ -d /run/systemd/system ]; then
       is_systemd=1
    fi
    
    cd /
    while [ $# -gt 0 ]; do
      case "${1}" in
        --help | -h | --h* )
           echo "${USAGE}" >&2
           exit 0
           ;;
        --version | -V )
           echo "${VERSION}" >&2
           exit 0
           ;;
        *)
           if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
              cd ${SERVICEDIR}
              for SERVICE in * ; do
                case "${SERVICE}" in
                  functions | halt | killall | single| linuxconf| kudzu)
                      ;;
                  *)
                    if ! is_ignored_file "${SERVICE}" \
                && [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                            out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
                            retval=$?
                            if echo "$out" | egrep -iq "usage:"; then
                              #printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
                              echo " [ ? ]  $SERVICE" 1>&2
                              continue
                            else
                              if [ "$retval" = "0" -a -n "$out" ]; then
                                #printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
                                echo " [ + ]  $SERVICE"
                                continue
                              else
                                #printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
                                echo " [ - ]  $SERVICE"
                                continue
                              fi
                            fi
                      #env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status
                    fi
                    ;;
                esac
              done
              exit 0
           elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
              SERVICE="${1}"
              # On systems using systemd, we just perform a normal restart:
              # A restart with systemd is already a full restart.
              if [ -n "$is_systemd" ]; then
                 ACTION="restart"
              else
                 if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                   env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" stop
                   env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" start
                   exit $?
                 fi
              fi
           elif [ -z "${SERVICE}" ]; then
             SERVICE="${1}"
           elif [ -z "${ACTION}" ]; then
             ACTION="${1}"
           else
             OPTIONS="${OPTIONS} ${1}"
           fi
           shift
           ;;
       esac
    done
    
    run_via_sysvinit() {
       # Otherwise, use the traditional sysvinit
       if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
          exec env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
       else
          echo "${SERVICE}: unrecognized service" >&2
          exit 1
       fi
    }
    
    update_openrc_started_symlinks() {
       # maintain the symlinks of /run/openrc/started so that
       # rc-status works with the service command as well
       if [ -d /run/openrc/started ] ; then
          case "${ACTION}" in
          start)
             if [ ! -h /run/openrc/started/$SERVICE ] ; then
                ln -s $SERVICEDIR/$SERVICE /run/openrc/started/$SERVICE || true
             fi
          ;;
          stop)
             rm /run/openrc/started/$SERVICE || true
          ;;
          esac
       fi
    }
    
    # When this machine is running systemd, standard service calls are turned into
    # systemctl calls.
    if [ -n "$is_systemd" ]
    then
       UNIT="${SERVICE%.sh}.service"
       # avoid deadlocks during bootup and shutdown from units/hooks
       # which call "invoke-rc.d service reload" and similar, since
       # the synchronous wait plus systemd's normal behaviour of
       # transactionally processing all dependencies first easily
       # causes dependency loops
       if ! systemctl --quiet is-active multi-user.target; then
           sctl_args="--job-mode=ignore-dependencies"
       fi
    
       case "${ACTION}" in
          restart|status|try-restart)
             exec systemctl $sctl_args ${ACTION} ${UNIT}
          ;;
          start|stop)
             # Follow the principle of least surprise for SysV people:
             # When running "service foo stop" and foo happens to be a service that
             # has one or more .socket files, we also stop the .socket units.
             # Users who need more control will use systemctl directly.
             for unit in $(systemctl list-unit-files --full --type=socket 2>/dev/null | sed -ne 's/\.socket\s*[a-z]*\s*$/.socket/p'); do
                 if [ "$(systemctl -p Triggers show $unit)" = "Triggers=${UNIT}" ]; then
                    systemctl $sctl_args ${ACTION} $unit
                 fi
             done
             exec systemctl $sctl_args ${ACTION} ${UNIT}
          ;;
          reload)
             _canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
             if [ "$_canreload" = "CanReload=no" ]; then
                # The reload action falls back to the sysv init script just in case
                # the systemd service file does not (yet) support reload for a
                # specific service.
                run_via_sysvinit
             else
                exec systemctl $sctl_args reload "${UNIT}"
             fi
             ;;
          force-stop)
             exec systemctl --signal=KILL kill "${UNIT}"
             ;;
          force-reload)
             _canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
             if [ "$_canreload" = "CanReload=no" ]; then
                exec systemctl $sctl_args restart "${UNIT}"
             else
                exec systemctl $sctl_args reload "${UNIT}"
             fi
             ;;
          *)
             # We try to run non-standard actions by running
             # the init script directly.
             run_via_sysvinit
             ;;
       esac
    fi
    
    update_openrc_started_symlinks
    run_via_sysvinit
    
    

    可以看得出,无论是Red Hat系还是Debian系,service命令的前半部分的逻辑都是十分相似的,不同的是后面部分,CentOS8.3在后面加上了 systemctl 命令的逻辑,这个命令在 CentOS6.5中是没有的,而 Ubuntu的后面部分也是自己特色的实现,不过这些这里暂且不表,这里只浅析一下 service --status-all 这个参数在不同发行版中的不同表现

    if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
              cd ${SERVICEDIR}
              for SERVICE in * ; do
                case "${SERVICE}" in
                  functions | halt | killall | single| linuxconf| kudzu)
                      ;;
                  *)
                    if ! is_ignored_file "${SERVICE}" \
                && [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                      env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
                    fi
                    ;;
                esac
              done
              exit 0
    ......
    

    从这一段代码可以看出,service加上--status-all参数后,就会将 /etc/init.d/ 目录下的所有脚本带上 status 参数循环执行一遍然后再对输出进行过滤格式化,但诡异的是,CentOS8.3 把 /etc/init.d/ 目录下的所有应用启动脚本全部删除了,空空如也


    没有启动脚本了

    所以在 CentOS8.3 中,执行 service --status-all 是没有任何输出的,不信大家可以试一下,这时如果想得知哪个服务是否安装了,只能使用 ps -aux 查看进程或者使用 netstat -tnlp 查看某个端口了,又或者使用包管理工具 rpm 来查看
    CentOS6.5 和 Ubuntu20.04下这个命令是没有问题的

    root@localhost:~# service --status-all
     [ + ]  aegis
     [ + ]  apache2
     [ - ]  apache-htcacheclean
     [ + ]  apparmor
     [ + ]  atd
     [ + ]  chrony
     [ - ]  console-setup.sh
     [ + ]  cron
     [ + ]  dbus
     [ - ]  fio
     [ + ]  grub-common
     [ - ]  hwclock.sh
     [ - ]  irqbalance
     [ - ]  keyboard-setup.sh
     [ + ]  kmod
     [ - ]  nginx
     [ - ]  ntp
     [ - ]  plymouth
     [ - ]  plymouth-log
     [ + ]  procps
    ....
    
    Ubuntu20.04

    当系统引入 systemctl 命令之后,service本质上就是对 systemctl 进行了一次包装,实际都是执行这个命令来调用.service配置文件(位于 /usr/lib/systemd/system )进行控制操作,这一点 Ubuntu20.04 和 CentOS8.3 很像,不像 CentOS6.5 ,脚本中是直接与应用二进制文件打交道。
    所以对服务的操控可以有两种写法(后者直接使用了 systemctl 命令调用 .service 配置文件)

    1. service apache2 start

    2.systemctl start apache2.service

    service的控制单元是一个个的 .service 配置文件,每个 .service 文件对应一个服务的控制操作,.service文件的书写有一定的规范,下面是 Ubuntu20.04下 apache2.service 的配置内容( CentOS 中是 httpd.service )

    [Unit]
    Description=The Apache HTTP Server
    After=network.target remote-fs.target nss-lookup.target
    Documentation=https://httpd.apache.org/docs/2.4/
    
    [Service]
    Type=forking
    Environment=APACHE_STARTED_BY_SYSTEMD=true
    ExecStart=/usr/sbin/apachectl start
    ExecStop=/usr/sbin/apachectl stop
    ExecReload=/usr/sbin/apachectl graceful
    PrivateTmp=true
    Restart=on-abort
    
    [Install]
    WantedBy=multi-user.target
    

    可以看出,service 配置文件才是真正与二进制文件进行关联的

    再来看看 /etc/init.d/apache2 脚本

    再次说明,CentOS8.3 是没有这个脚本的,而 CentOS6.5 是直接与二进制文件进行交互,这里就以 Ubuntu20.04 进行说明
    前面说过,service --status-all 实际上是将这个目录下的脚本全部循环加上 status 参数执行一遍,也就是说
    /etc/init.d/apache2 status 这条命令是会被执行的

    root@localhost:~# /etc/init.d/apache2 status
    ● apache2.service - The Apache HTTP Server
         Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
         Active: active (running) since Mon 2021-06-28 13:49:56 CST; 20h ago
           Docs: https://httpd.apache.org/docs/2.4/
        Process: 6856 ExecReload=/usr/sbin/apachectl graceful (code=exited, status=0/SUCCESS)
       Main PID: 2003 (apache2)
          Tasks: 55 (limit: 2193)
         Memory: 8.4M
         CGroup: /system.slice/apache2.service
                 ├─2003 /usr/sbin/apache2 -k start
                 ├─6860 /usr/sbin/apache2 -k start
                 └─6861 /usr/sbin/apache2 -k start
    
    Jun 28 13:49:56 localhost systemd[1]: Starting The Apache HTTP Server...
    Jun 28 13:49:56 localhost apachectl[2002]: AH00558: apache2: Could not reliably determine t…ssage
    Jun 28 13:49:56 localhost systemd[1]: Started The Apache HTTP Server.
    Jun 29 00:01:00 localhost systemd[1]: Reloading The Apache HTTP Server.
    Jun 29 00:01:00 localhost apachectl[6859]: AH00558: apache2: Could not reliably determine t…ssage
    Jun 29 00:01:00 localhost systemd[1]: Reloaded The Apache HTTP Server.
    Hint: Some lines were ellipsized, use -l to show in full.
    

    以上是直接执行 /etc/init.d/apache2 status 的输出结果,但为何执行 service --status-all 又变成了列表形式的呢,那时因为对输出结果进行了一系列变换格式化操作,现在我们看下为何 /etc/init.d/apache2 status 的输出结果是这样,本质上是哪部分代码被执行了
    用文本编辑器打开 /etc/init.d/apache2 脚本,大概在 53 行处

    . /lib/lsb/init-functions  //实际上指向 /usr/lib/lsb/init-functions
    

    输出的功能性代码就在这个引入脚本里,再打开这个脚本,在末尾处

    for hook in $(run-parts --lsbsysinit --list /lib/lsb/init-functions.d 2>/dev/null); do
        [ -r $hook ] && . $hook || true
    done
    

    又是循环执行了脚本,再次打开 /lib/lsb/init-functions.d (实际指向是 /usr/lib/lsb/init-functions.d )


    init-functions.d

    这里面的脚本都会执行一遍,重点就是那个 systemd ,用文本编辑器打开,最后几行代码

        case "$argument" in
            start|stop|restart|reload|force-reload|try-restart|status)
                systemctl_redirect $executable $argument
                exit $?
                ;;
        esac
    

    systemctl_redirect 这个函数的实现在 systemd 文本的前半部分

    systemctl_redirect () {
        local s
        local rc
        local prog=${1##*/}
        local command=$2
    
        case "$command" in
            start)
                s="Starting $prog (via systemctl)"
                ;;
            stop)
                s="Stopping $prog (via systemctl)"
                ;;
            reload|force-reload)
                s="Reloading $prog configuration (via systemctl)"
                ;;
            try-restart)
                s="Restarting $prog if running (via systemctl)"
                ;;
            restart)
                s="Restarting $prog (via systemctl)"
                ;;
        esac
    
        service="${prog%.sh}.service"
    
        # avoid deadlocks during bootup and shutdown from units/hooks
        # which call "invoke-rc.d service reload" and similar, since
        # the synchronous wait plus systemd's normal behaviour of
        # transactionally processing all dependencies first easily
        # causes dependency loops
        if ! OUT=$(systemctl is-system-running 2>/dev/null) && [ "$OUT" != "degraded" ]; then
            sctl_args="--job-mode=ignore-dependencies"
        fi
    
        [ "$command" = status ] || log_daemon_msg "$s" "$service"
        /bin/systemctl --no-pager $sctl_args $command "$service" //这行才是重点
        rc=$?
        [ "$command" = status ] || log_end_msg $rc
    
        return $rc
    }
    

    /bin/systemctl --no-pager $sctl_args $command "$service"这一行才是重点,大概在全文的 82 行左右
    如果把里面的变量替换展开,最后执行的命令行大概就是这样
    /bin/systemctl --no-pager status apache2.service
    直接运行这条命令,可以得到和 /etc/init.d/apache2 status 一样的输出

    结论

    没想到 service --staus-all 最后还是依赖于 systemctl 这个命令,不过 /etc/init.d 里面大量的脚本只是为了使 service --staus-all 这条命令有效,是不是有点浪费,如果像 CentOS8.3 那样直接删掉,是不是更显得简洁,你觉得呢?

    相关文章

      网友评论

          本文标题:Linux中 service 命令浅析

          本文链接:https://www.haomeiwen.com/subject/rvpcultx.html