将可执行程序添加为 linux 系统服务

一、Linux 程序配置成服务

使用 unitree z1 机械臂时,z1-controller 程序需要到  build 目录下才能正常启动(配置文件路径问题),因其主要功能封装在 so 动态库内、外部无法修改,在做成 linux 系统服务时不能直接写程序的全路径,需要做特殊处理,先进入它所在的目录,再执行启动命令,格式如下所示:

[Unit]
Description=Unitree Z1 Controller Service
After=network.target

[Service]
Type=simple
User=my_name
Restart=on-failure
RemainAfterExit=yes
RestartSec=5s
ExecStart=/bin/bash -c "cd /home/unitree/z1-controller/build; ./z1_ctrl"

[Install]
WantedBy=multi-user.target

二、Linux 系统服务配置项说明:

Type=

设置进程的启动类型(simple, forking, oneshot, dbus, notify, idle):

"simple"  : 一般服务。 如果此进程需要为其他进程提供服务,
            在该进程启动前应先建立好通信渠道(例如:套接字),以加快后继单元的启动速度。

  "dbus"  : 进程需要在 D-Bus 上获得一个由 BusName= 指定的名称。 
            systemd 在启动后继单元前,确保该进程已经成功的获取了指定的 D-Bus 名称。
            此类型隐含的依赖了 dbus.socket 单元。

"oneshot" : 进程必须在 systemd 启动后继单元前退出。
            本类型需要设置 RemainAfterExit= 选项。

"forking" : 进程在启动过程中使用 fork() 系统调用。它是传统 UNIX 守护进程的经典做法。
            也就是当所有的通信渠道都已建好、启动成功后,父进程将会退出,
            子进程将作为该服务的主进程继续运行。 
            建议同时设置 PIDFile= 选项,以帮助 systemd 准确定位该服务的主进程,
            进而加快后继单元的启动速度。

"notify" : 进程在启动完成之后通过 sd_notify 发送一个通知消息。
           系统在启动后继单元前,确保该进程已经成功发送了消息。类型设置为 notify 那么
           NotifyAccess= 将只能设置为 "all" 或者 "main"(默认)。
           注意 Type=notify 不能在 PrivateNetwork=yes 的情况下正常工作。

  "idle" : 进程延迟到所有的操作都完成之后再执行。
RemainAfterExit=

    表示服务的所有进程全部退出后,是否依然将此服务视为活动(active)状态。
    默认值为 "no", 也可设为 "yes"。

GuessMainPID=

    无法明确定位服务主进程的情况下,systemd 是否猜测主进程的 PID(可能不正确)。
    该选项仅在设置了 Type=forking 但未设置 PIDFile= 的情况下有意义。
    如果 PID 猜测错误,那么该服务的失败检测与自动重启功能将失效。
    可设为"yes"(默认值)或"no"

PIDFile=

    守护进程的 PID 文件,必须是绝对路径。
    强烈建议在 Type=forking 的情况下明确设置此选项。 
    systemd 在服务启动后、从此文件中读取主守护进程的 PID 。
    systemd 不会写入此文件,但会在此服务停止后删除它(若存在)。

BusName=

    设置与此服务通信所使用的 D-Bus 名称。
    在 Type=dbus 的情况下必须明确设置此选项。

BusPolicy=

    如果设置了此项,那么 systemd 将会创建一个自定义的kdbus端点(endpoint),
    并将其安装为该服务默认的总线节点(bus node)。 
    这个自定义的端点可以拥有它自己的策略规则。端点的名称就是其所服务的单元的名称。
    端点的节点(node)将被绑定挂载到默认的总线节点的位置,这样该服务就只能通过它自己的端点访问总线。
   
    注意,自定义端点的默认策略是'拒绝所有',
    因此必须在 BusPolicy = 中明确的添加必要的允许策略。
    这个选项的值由两部分组成:总线名 + 访问级别,中间以空格分隔。
    访问级别必须是 see, talk, own 之一,并且 talk 隐含了 see,而 own 隐含了 talk 与 see 。 
    如果对同一个总线名称多次指定了访问级别,那么将以拥有最大权限的那个为准。
    例如: BusPolicy=org.freedesktop.systemd1 talk BusPolicy=org.foo.bar see 
    该选项仅在内核开启了kdbus(即将并入官方内核)支持的情况下有意义。

ExecStart=

    在启动该服务时需要执行的命令行(命令+参数)。
    仅在设置了 Type=oneshot 的情况下,才可以设置任意个命令行(包括零个),
    否则必须且只能设置一个命令行。

    多个命令行既可以在同一个 ExecStart= 中设置,
    也可以通过设置多个 ExecStart= 来达到相同的效果。 
    如果设为一个空字符串,那么先前设置的所有命令行都将被清空。
    如果不设置任何 ExecStart= 指令,那么必须确保设置了 RemainAfterExit=yes 指令。
    命令行必须以一个绝对路径表示的可执行文件开始,
       并且其后的那些参数将依次作为"argv[1] argv[2] ..."传递给被执行的进程。
    如果在绝对路径前加上可选的"@"前缀,
       那么其后的那些参数将依次作为"argv[0] argv[1] argv[2] ..."传递给被执行的进程。
    如果在绝对路径前加上可选的"-"前缀,
       那么即使该进程以失败状态(例如非零的返回值或者出现异常)退出,也会被视为成功退出。
    可以同时使用"-"与"@"前缀,且顺序任意。
    如果设置了多个命令行,那么这些命令行将以其在单元文件中出现的顺序依次执行。
    如果某个无"-"前缀的命令行执行失败,那么剩余的命令行将不会被执行,
      同时该单元将变为失败(failed)状态。
    当未设置 Type=forking 时,这里设置的命令行所启动的进程将被视为该服务的主守护进程。

ExecStartPre=, ExecStartPost=

    设置在 ExecStart= 之前/后执行的命令行。语法规则与 ExecStart= 完全相同。
    如果设置了多个命令行,那么这些命令行将以其在单元文件中出现的顺序依次执行。
    当某个无"-"前缀的命令行执行失败,剩余的命令行不会被执行,同时该单元将变为失败(failed)状态。
    仅在所有无"-"前缀的 ExecStartPre= 命令全部执行成功后,才会继续执行 ExecStart= 命令。
    ExecStartPost= 命令仅在服务已经被成功启动之后才会运行,判断的标准基于 Type= 选项。

    具体说来,
    对于 Type=simple 或 Type=idle 就是主进程已经成功启动;
    对于 Type=oneshot 来说就是主进程已经成功退出; 
    对于 Type=forking 来说就是初始进程已经成功退出;
    对于 Type=notify 来说就是已经发送了"READY=1"; 
    对于 Type=dbus 来说就是已经取得了 BusName= 中设置的总线名称。

    注意,不可将 ExecStartPre= 用于需要长时间执行的进程。
    因为所有由 ExecStartPre= 派生的子进程都会在启动 ExecStart= 服务进程之前被杀死。


ExecReload=

    可选的指令,用于设置当该服务被要求重新载入配置时所执行的命令行。
    语法规则与 ExecStart= 完全相同。
    还有一个特殊的环境变量 $MAINPID 可以用于表示主进程的 PID,
    例如:可以这样使用:/bin/kill -HUP $MAINPID 
    注意,像上例那样,通过向守护进程发送复位信号,强制其重新加载配置文件,并不是一个好习惯。
    因为这是一个异步操作,所以不适用于需要按照特定顺序重新加载配置文件的服务。
    我们强烈建议将 ExecReload= 设置为一个能够确保重新加载配置文件的操作同步完成的命令行。

ExecStop=

    可选的指令,用于设置当该服务被要求停止时所执行的命令行。
    语法规则与 ExecStart= 完全相同。执行完此处设置的命令行之后,
    该服务所有剩余的进程将会根据 KillMode= 的设置被杀死(参见 systemd.kill(5) 手册)。
    如果未设置此选项,那么当此服务被停止时,
    该服务的所有进程都将会根据 KillMode= 的设置被立即全部杀死。
    与 ExecReload= 一样,也有一个特殊的环境变量 $MAINPID 可以用于表示主进程的 PID,

    一般来说,仅仅设置一个结束服务的命令,而不等待其完成,是不够的。
    因为当此处设置的命令执行完之后,剩余的进程会被 SIGKILL 信号立即杀死,
    这可能会导致数据丢失。 因此,这里设置的命令必须是同步操作,而不能是异步操作。

ExecStopPost=

    可选的指令,用于设置该服务停止之后所执行的命令行。
    语法规则与 ExecStart= 完全相同。
    无论此服务是正常停止,还是异常退出,此处的设置都适用。
    RestartSec= 设定在重启服务(Restart=)前暂停多长时间。默认值是100毫秒(100ms)。
    如果未指定时间单位,那么将视为以秒为单位。例如设为"20"等价于设为"20s"。

TimeoutStartSec=

    设定该服务允许的最大启动时长。
    如果守护进程未能在限定的时长内发出"启动完毕"的信号,那么该服务将被视为启动失败,并会被关闭。
    如果未指定时间单位,那么将视为以秒为单位。
    例如设为"20"等价于设为"20s"。设为"0"则表示永不超时。
    当 Type=oneshot 时,默认值为"0",
    否则默认值等于 DefaultTimeoutStartSec= 的值(参见 systemd-system.conf(5) 手册)。

TimeoutStopSec=

    服务允许的最大停止时长。
    如果服务未能在限定的时长内成功停止,将会强制使用 SIGTERM 信号关闭,
    如果依然未能在相同的时长内成功停止,将会强制使用 SIGKILL 信号关闭(参见 systemd.kill(5)
    手册中的 KillMode= 选项)。如果未指定时间单位,那么将视为以秒为单位。
    例如设为 "20" 等价于设为 "20s"。设为 "0" 则表示永不超时。
    默认值等于 DefaultTimeoutStartSec= 的值(参见 systemd-system.conf(5) 手册)。

TimeoutSec=

    一个同时设置 TimeoutStartSec= 与 TimeoutStopSec= 的快捷方式。

WatchdogSec=

    设置该服务的看门狗(watchdog) 的超时时长。看门狗将在服务成功启动之后被激活。
    该服务在运行中必须周期性的以"WATCHDOG=1"("keep-alive ping") 调用 sd_notify(3) 函数。 
    如果在两次调用之间的时间间隔大于这里设定的值,那么该服务将被视为失败(failed)状态,
    并会被强制使用 SIGABRT 信号关闭。通过将 Restart= 设为"on-failure"或"always"可以实现
    在失败状态下的自动重启该服务。这里设置的值将会通过 WATCHDOG_USEC 环境变量传递给守护进程,
    这样就允许那些支持看门狗的服务自动启用"keep-alive ping"。

    如果设置了此选项,那么必须将 NotifyAccess= 设为"main"(此种情况下的隐含默认值)或"all"。
    如果未指定时间单位,那么将视为以秒为单位。
    例如 设为"20"等价于设为"20s"。默认值"0"表示禁用看门狗功能。
Restart=

    当服务进程正常退出、异常退出、被杀死、超时的时候,是否重新启动该服务。 
    当进程是由于 systemd 的正常操作(例如 systemctl stop|restart)而被停止时,
    该服务不会被重新启动。"超时"可以是看门狗的"keep-alive ping"超时,
    也可以是 systemctl start|reload|stop 操作超时。

    该选项可以取下列值之一 : 
         no, 
         on-success, 
         on-failure, 
         on-abnormal, 
         on-watchdog, 
         on-abort, 
         always

    "no"(默认值)表示不会被重启。
    "always"表示会被无条件的重启。
    "on-success"表示仅在服务进程正常退出时重启,

       所谓"正常退出"是指:
           退出码为"0",或者进程收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 信号
           并退出码符合 SuccessExitStatus= 的设置。

    "on-failure"表示仅在服务进程异常退出时重启,

       所谓"异常退出"是指: 
           退出码不为"0",或者进程被强制杀死(包括"core dump" 以及
           收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 之外的其他信号), 
           或者进程由于看门狗或者 systemd 的操作超时而被杀死。


注意如下两个例外情况:

  (1) RestartPreventExitStatus= 中列出的退出码或者信号永远不会导致该服务被重启。 
  (2) RestartForceExitStatus= 中列出的退出码或者信号将会无条件的导致该服务被重启。

      对于需要长期持续运行的守护进程,推荐设为 "on-failure" 以增强可用性。 
      对于自身可以自主选择何时退出的服务,推荐设为 "on-abnormal"。
SuccessExitStatus=

    额外定义附加的进程"正常退出"状态。
    可以设为一系列以空格分隔的数字退出码或者信号名称,
    例如:SuccessExitStatus=1 2 8 SIGKILL 表示当进程的退出码是 1, 2, 8 
    或被 SIGKILL 信号终止时,都可以视为"正常退出"。 
    注意,退出码"0"以及 SIGHUP, SIGINT, SIGTERM, SIGPIPE 信号
    是标准的 "正常退出",不需要在此特别定义。

    注意,如果进程拥有自定义的信号处理器,并且在收到信号后通过调用 _exit(2) 退出,
    那么有关信号的信息就会丢失。

    在这种情况下,进程必须自己完成清理工作并使用相同的信号自杀。
    参见 Proper handling of SIGINT/SIGQUIT — How to be a proper program 
    如果多次使用此选项,那么最终的结果将是多个列表的合集。
    如果将此项设为空,那么先前设置的列表将被清空。

RestartPreventExitStatus=

    可以设为一系列以空格分隔的数字退出码或者信号名称,
    当进程的退出码或收到的信号与此处的设置匹配时,
    该服务将无条件的禁止重新启动(无视 Restart= 的设置)。
    例如:RestartPreventExitStatus=1 6 SIGABRT 表示
    退出码 1, 2, 8 与 SIGKILL 信号将不会导致该服务被重启。
    默认值为空,表示完全遵守 Restart= 的设置。

    如果多次使用此选项,那么最终的结果将是多个列表的合集。
    如果将此项设为空,那么先前设置的列表将被清空。

    RestartForceExitStatus= 可以设为一系列以空格分隔的数字退出码或者信号名称,
    当进程的退出码或收到的信号与此处的设置匹配时,
    该服务将无条件的被重新启动(无视 Restart= 的设置)。
    默认值为空,表示完全遵守 Restart= 的设置。
    如果多次使用此选项,那么最终的结果将是多个列表的合集。
    如果将此项设为空,那么先前设置的列表将被清空。

PermissionsStartOnly=

    设为 true 表示所有与权限相关的执行选项(例如 User= 之类的选项,
    参见 systemd.exec(5) 手册)仅对 ExecStart= 中的程序有效,
    而对 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost=
    中的程序无效。
    默认值 false 表示所有与权限相关的执行选项对所有 Exec*= 系列选项中的程序都有效。

RootDirectoryStartOnly=

    设为 true 表示根目录(参见 systemd.exec(5) 中的 RootDirectory= 选项)
    仅对 ExecStart= 中的程序有效,
    而对 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 
    中的程序无效。 默认值 false 表示根目录对所有 Exec*= 系列选项中的程序都有效。

NonBlocking=

    是否为所有通过socket激活传递的文件描述符设置非阻塞标记(O_NONBLOCK)。
    默认值为 false 设为 true 表示所有大于2的文件描述符(也就是 
    stdin, stdout, stderr 之外的文件描述符)都将被设置为非阻塞模式。
    该选项仅在与 socket 单元(systemd.socket(5))联用的时候才有意义。

NotifyAccess=

    设置通过sd_notify(3)访问服务状态通知socket的访问模式。
    可以设为:none(默认值), main, all 之一。 
    "none" 表示不更新任何守护进程的状态,忽略所有的状态更新消息。
    "main" 表示仅接受主进程的状态更新消息。
    "all" 表示接受该服务cgroup内的所有进程的状态更新消息。
    当设置了 Type=notify 或 WatchdogSec= 的时候,
    此选项应该被设为"main"或"all",如果未设置,那么隐含为"main"。


Sockets=

    设置一个socket单元的名称,表示该服务在启动时应当从它继承socket文件描述符。
    通常并不需要明确设置此选项,因为所有与该服务同名(不算后缀)的socket单元的
    socket 文件描述符,都会被自动的传递给派生进程。

    注意:
      (1) 同一个socket文件描述符可以被传递给多个不同的进程(服务)。
      (2) 当socket上有流量进入时,被启动的可能是另一个不同于该服务的其他服务。

    换句话说就是:
      socket 单元中的 Service= 所指向的服务单元中的 Sockets= 设置未必要反向指回去。
      如果多次使用此选项,那么最终的结果将是多个 socket 单元的合集。
      如果将此项设为空,那么先前设置的 socket 单元的列表将被清空。

StartLimitInterval=, StartLimitBurst=

    限制该服务的启动频率。
    默认值是每10秒内不得超过5次(StartLimitInterval=10s StartLimitBurst=5)。
    StartLimitInterval= 的默认值等于 systemd 配置文件
    中 DefaultStartLimitInterval= 的值,"0"表示取消启动频率限制。

    StartLimitBurst= 的默认值等于systemd配置文件中 DefaultStartLimitBurst= 的值。
    虽然这两个选项经常与 Restart= 一起使用,但是它们不只限制 Restart= 罗辑所导致的重启,
    而是限制所有类型的启动(包括手动启动)。

    注意,当 Restart=逻辑所导致的重启超出了启动频率限制之后,
    Restart= 逻辑将会被禁用(也就是不会在下一个时间段内再次尝试重启),
    然而,如果该单元随后又被手动重启,那么 Restart= 罗辑将被再次激活。
    注意,"systemctl reset-failed ..."命令会清除该服务的重启次数计数器,
         这通常用于在手动启动之前清除启动限制。


StartLimitAction=

    设置到达启动频率限制后触发什么动作。
    可设为 none(默认值), 
          reboot, 
          reboot-force, 
          reboot-immediate, 
          poweroff, 
          poweroff-force, 
          poweroff-immediate 之一。

    "none" 表示除了禁止再次启动之外,不触发任何动作。
    "reboot"表示触发常规的系统重启的动作,相当于执行"systemctl reboot"命令。
    "reboot-force"表示触发系统的强制重启动作(强制杀死所有进程但不会造成文件系统
                  不一致),相当于执行"systemctl reboot -f"命令。
    "reboot-immediate"表示立即调用内核的reboot(2)函数,可能会造成文件系统的数据丢失。
     poweroff, poweroff-force, poweroff-immediate 
    与对应的"reboot*"项含义类似,不同之处仅仅在于是关机而不是重启。


FailureAction=

    设置当该服务进入失败 (failed) 状态时所触发的动作。
    取值范围与默认值都与 StartLimitAction= 完全相同。

RebootArgument=

    设置 reboot(2) 系统调用的可选参数,
    仅用于 StartLimitAction= 与 FailureAction= 的重启动作。
    其作用与 "systemctl reboot [arg]" 命令中的可选参数[arg]完全相同。

FileDescriptorStoreMax=

    允许在 systemd 中最多为该服务存储多少个使用 sd_pid_notify_with_fds(3)
    的 "FDSTORE=1" 消息的文件描述符,默认值为 "0" (不存储)。

    用于实现重启该服务而不会丢失其状态(前提是该服务将各种状态序列化之后保存在 /run 中,
    同时将文件描述符交给 systemd 暂存)。 

    所有被 systemd 暂存的文件描述符都将在该服务重启之后交还给该服务的主进程。
    所有被 systemd 暂存的文件描述符都将在遇到如下两种情况时被自动关闭: 
         (1) 收到 POLLHUP 或 POLLERR 信号;
         (2) 该服务被彻底停止,并且没有任何剩余的任务队列

USBFunctionDescriptors=

    设为一个包含 USB FunctionFS 描述符的文件路径,以实现 USB gadget 支持。
    仅与配置了 ListenUSBFunction= 的 socket 单元一起使用。
    该文件的内容将被写入 ep0 文件。

    USBFunctionStrings= 设为一个包含 USB FunctionFS 字符串的文件路径。
    其行为与上面的 USBFunctionDescriptors= 类似。
    参见 systemd.exec(5) 与 systemd.kill(5) 手册页,以获取更多其他选项。
命令行 ExecStart=, ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 选项的命令行解析规则。

仅在设置了 Type=oneshot 的前提下,才可以设置多个命令行,且必须用分号(;)
将每个命令行隔开(分号自身用"\;"表示)。

例如 : ExecStart=/bin/echo one; /bin/echo "two two" 每个命令行的内部以空格分隔,每一项的边界都可以用单引号或者双引号进行界定。

第一项是要运行的命令,随后的各项则是命令的参数。
行尾的反斜杠(\)将被视作续行符,这和bash的续行语法类似。
例如 : ExecStart=/bin/echo / >/dev/null & \; \ /bin/ls 的含义是向 /bin/echo 命令传递五个参数:"/", ">/dev/null", "&", ";", "/bin/ls". 

命令行的语法刻意保持了与shell的相似性,但并不相同。
特别的,重定向(<, <<, >, >)、管道(|)、后台运行(&),以及其他下文未明确提及的符号都不被支持。
第一项,要运行的命令,必须使用绝对路径。可以在其中包含空格,但是不可以包含控制字符。
可以在随后的各项命令参数中使用 systemd.unit(5) 中描述的"%"系列特殊符号,
但不可用于命令自身(第一项)。 

此外,还可以使用C语言风格的转义序列(含义也相同),
但只能识别如下符号:\a \b \f \n \r \t \v \\ \" \' \s \xxx \nnn 此外,
还支持两种不同的环境变量替换方式("${FOO}"与"$FOO")。

下面的两个例子,将能清除的体现两者的差别:

例(1):
Environment="ONE=one" 'TWO=two two' 
ExecStart=/bin/echo $ONE $TWO ${TWO}
将 "one", "two", "two", "two two" 四个参数依次传递给 /bin/echo

例(2):
Environment=ONE='one' TWO="'two two' too" THREE=
ExecStart=/bin/echo ${ONE} ${TWO} ${THREE}
ExecStart=/bin/echo $ONE $TWO $THREE

给第一个 /bin/echo 依次传递如下三个参数: "'one'", "'two two' too", "" ,并同时
给第二个 /bin/echo 依次传递如下三个参数: "one", "two two", "too" 

具体说来就是:"${FOO}"的内容将原封不动的转化为一个单独的命令行参数,无论其中是否包含空格与引号,也无论它是否为空。

"$FOO"的内容将将原封不动的插入命令行中,但对插入内容的解释却遵守一般的命令行解析规则。
此外,如果想要传递美元符号($)自身,则必须使用"$$"。
而那些无法在替换时确定内容的变量将被当做空字符串。

注意,不可以在第一项(也就是命令的绝对路径)中使用变量替换。
这里使用的变量必须首先在 Environment= 或 EnvironmentFile= 中定义。
此外,在systemd.exec(5)手册的"派生进程中的环境变量"小节中列出的"静态变量"也可以使用。
例如 $USER 就是一个"静态变量",但是 $TERM 不是"静态变量"。

注意,这里的命令行并不直接支持 shell 命令,
但是可以通过模仿下面这个变通的方法来实现:
ExecStart=/bin/sh -c 'dmesg | tac'

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值