Linux Systemd type=simple和type=forking的区别

这篇具有很好参考价值的文章主要介绍了Linux Systemd type=simple和type=forking的区别。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. Type=forking

使用Type=forking时,要求ExecStart启动的命令自身就是以daemon模式运行的。

而以daemon模式运行的进程都有一个特性:总是会有一个瞬间退出的中间父进程,例如,nginx命令默认以daemon模式运行,所以可直接将其配置为forking类型:

Type=simple是一种最常见的通过systemd服务系统运行用户自定义命令的类型,也是省略Type指令时的默认类型。

例如,nginx命令默认以daemon模式运行,所以可直接将其配置为forking类型:

systemd type,ubuntu,linux,运维,服务器

注意上面status报告的信息中,ExecStart启动的nginx的进程PID=7912,且该进程的状态是已退出,退出状态码为0,这个进程是daemon类进程创建过程中瞬间退出的中间父进程。在forking类型中,该进程称为初始化进程。同时还有一行Main PID: 7913 (nginx),这是systemd真正监控的nginx服务主进程,其PID=7913,是PID=7912进程的子进程。

Type=forking类型代表什么呢?要解释清楚该type,需从进程创建开始说起。

因为systemd service启动的服务进程都是systemd的子进程,所以,在服务进程启动时,总是由pid=1的systemd进程fork()一个子进程(子systemd进程),再在此进程分支中通过systemd.exec配置该子进程的环境,最后使用exec()去调用ExecStart指定的服务启动命令。Exec()调用程序时会替换当前进程,所以启动后的服务进程将会替代子systemd进程,于是服务进程自身成为pid=1 systemd进程的子进程。

对于Type=forking来说,pid=1的systemd进程fork出来的子进程正是瞬间退出的中间父进程,且systemd会在中间父进程退出后就认为服务启动成功,此时systemd可以立即去启动后续需要启动的服务。

如果Type=forking服务中的启动命令是一个前台 命令会如何呢?比如将sleep配置为forking模式,将nginx daemon off配置为forking模式等。

答案是systemd会一直等待中间ExecStart启动的进程作为中间父进程退出,在等待过程中个,systemctl start会一直卡住,直到等待超时而失败,在此阶段中,systemctl status将会查看到服务处于activating状态。

systemd type,ubuntu,linux,运维,服务器

回到forking类型的服务。由于daemon类的进程会有一个瞬间退出的中间父进程(如上面的PID=7913的nginx进程),systemd是如何知道哪个进程是应该被监控的服务主进程(Main PID)呢?

答案是靠猜。没错,systemd真的就是靠猜的。当设置Type=forking时,有一个GuessMainPID指令其默认值为Yes,它表示systemd会通过一些算法去猜测Main PID。当systemd的猜测无法确定哪个是主进程时,后果是严重的:systemd将不可靠。因为systemd无法正确探测服务是否真的失败,当systemd误认为服务失败时,如果本服务器配置了自动重启(配置了Restart指令),重启服务器时可能会和当前正在运行但是systemd误认为失败的服务冲突(比如出现端口已被占用问题)。

多数情况下的猜测过程很简单,systemd只需去找目前存活的属于本服务的leader进程即可。但有些服务(少数)情况可能比较复杂,在多进程之间做简单的猜测并非总是可靠。

好在,Type=forking时的systemd提供了PIDFile指令(Type=forking通常都会结合PIDFile指令),systemd会从PIDFile指令所指定的PID文件中获取服务的主进程PID。例如,编写一个nginx的服务配置文件:

systemd type,ubuntu,linux,运维,服务器

GuessMainPID=Takes a boolean value that specifies whether systemd should try to guess the main PID of a service if it cannot be determined reliably. This option is ignored unless Type=forking is set and PIDFile= is unset because for the other types or with an explicitly configured PID file, the main PID is always known. The guessing algorithm might come to incorrect conclusions if a daemon consists of more than one process. If the main PID cannot be determined, failure detection and automatic restarting of a service will not work reliably. Defaults to yes.

该参数只有在启动类型为forking,且没有指定PIDFile参数时才有效。

  1. Type=forking时PIDFile指令的坑

关于PIDFile,有必要去了解一些注意事项,否则它们可能就会成为你的坑。

首先,PIDFile只适合在Type=forking模式下使用,其它时候没必要使用,因为其它类型的Service主进程的PID都是确定的。systemd推荐PIDFile指定的PID文件在/run目录下,所以,可能需要修改服务程序的配置文件,将其PID文件路径修改为/run目录之下,当然这并非必须。

但有一点必须注意,PIDFile指令的值要和服务程序的PID文件路径保持一致。例如nginx的相关配置:

systemd type,ubuntu,linux,运维,服务器

其次,systemd会在中间父进程退出后立即读取这个PID文件,读取成功后就认为该服务已经启动成功。但是,systemd读取PIDFile的时候,服务主进程可能还未将PID写入到PID文件中,这时systemd将出现问题。所以,对于服务程序的开发人员来说,应尽早将主进程写入到PID文件中,比如可以在中间父进程fork完之后立即写入PID文件,然后再退出,而不是在fork出来的服务主进程内部由主进程负责写入。

上面的nginx服务配置文件是某个nginx版本yum包提供的,但却是有问题的,我曾经踩过这个坑,网上甚至将其报告为一个Bug。

上面的nginx.service文件可以正常启动服务,但无法systemctl reload,只要reload就报错,而且报错时提示kill命令语法错误。kill语法错误显然是因为没有获取到$MAINPID变量的值,而这正是因为systemd在nginx写入PID文件之前先去读取了PID文件,因为没有读取到内容,所以$MAINPID变量为空值。

解决办法是使用ExecStartPost=/usr/bin/sleep 0.1,让systemd在初始化进程(即中间父进程)退出之后耽搁0.1秒再继续向下执行,即推迟了systemd读取PID的过程,保证能让systemd从PID文件中读取到值。

最后,systemd只会读PIDFile文件而不会写,也不会创建它。但是,在停止服务的时候,systemd会尝试删除PID文件。因为服务进程可能会异常终止,导致已终止的服务进程的PID文件仍然保留着,所以在使用PIDFile指令时,通常还会使用ExecStartPre指令来删除可能已经存在的PID文件。正如上面给出的nginx配置文件一样。

  1. Type=simple

Type=simple类型的服务只适合那些在shell下运行在前台的命令。也就是说,当一个命令本身会以daemon模式运行时,将不能使用simple,而应该使用Type=forking。比如ls命令、sleep命令、非daemon模式运行的nginx进程以及那些以前台调试模式运行的进程,在理论上都可以定义为simple类型的服务。

例如,编写一个/usr/lib/systemd/system/test.service运行sleep进程:

systemd type,ubuntu,linux,运维,服务器

使用daemon-reload重载并启动该服务进程:

systemd type,ubuntu,linux,运维,服务器

10秒内,sleep进程以daemon模式运行在后台,就像一个服务进程一样。10秒之后,sleep退出,于是systemd将该进程从监控队列中踢出。再次查看进程的状态将是inactive:

systemd type,ubuntu,linux,运维,服务器

再来分析上面的服务配置文件中的指令。

ExecStart指令指定启动本服务时执行的命令,即启动一个本该前台运行的sleep进程作为服务进程在后台运行。

需注意,systemd service的命令行中必须使用绝对路径,且只能编写单条命令(Type=oneshot时除外),如果要命令续行,可在尾部使用反斜线符号\等。

此外,命令行中支持部分类似Shell的特殊符号,但不支持重定向> >> << <、管道|、后台符号&,具体可参考man systemd.service中command line段落的解释说明。

对于Type=simple来说,systemd系统在fork出子systemd进程后就认为服务已经启动完成了,所以systemd可以紧跟着启动排在该服务之后启动的服务。它的伪代码模型大概是这样的:

systemd type,ubuntu,linux,运维,服务器

例如,先后连续启动两个Type=simple的服务,进程流程图大概如下:

换句话说,当Type=simple时,systemd只在乎fork阶段是否成功,只要fork子进程成功,这个子进程就受systemd监管,systemd就认为该Unit已经启动。

因为子进程已成功被systemd监控,无论子进程是否启动成功,在子进程退出时,systemd都会将其从监控队列中踢掉,同时杀掉所有附属进程(默认行为是如此,杀进程的方式由systemd.kill中的KillMode指令控制)。所以,查看服务的状态将是inactive(dead)。

例如,下面的配置种,睡眠1秒后,该服务的状态将变为inactive(dead)。

systemd type,ubuntu,linux,运维,服务器

这没什么疑问。但考虑一下,如果simple类型下ExecStart启动的命令本身就是以daemon模式运行的呢?其结果是systemd默认会立刻杀掉所有属于服务的进程。

原因也很简单,daemon类进程总是会有一个瞬间退出的中间父进程,而在simple类型下,systemd所fork出来的子进程正是这个中间父进程,所以systemd会立即发现这个中间父进程的退出,于是杀掉其它所有服务进程。

例如,以运行bash -c '(sleep 3000 &)'的simple类型的服务,被systemd监控的bash进程会在启动sleep后立即退出,于是systemd会立即杀掉属于该服务的sleep进程。

systemd type,ubuntu,linux,运维,服务器

再例如,nginx命令默认是以daemon模式运行的,simple类型下直接使用nginx命令启动服务,systemd会立刻杀掉所有nginx,即nginx无法启动成功。

  1. Systemd Service:其它Type类型

  • simple:在fork出子systemd进程后,systemd就认为该服务启动成功了

  • exec:在fork出子systemd进程且子systemd进程exec()调用ExecStart命令成功后,systemd认为该服务启动成功

  • oneshot:在ExecStart命令执行完成退出后,systemd才认为该服务启动成功

  1. 因为服务进程退出后systemd才继续工作,所以在未配置RemainAfterExit 指令时,oneshot类型的服务永远无法出现active状态,它直接从启动状态到activating到deactivating再到dead状态

  1. 当结合RemainAfterExit指令时,在服务进程退出后,systemd会继续监控该Unit,所以服务的状态为active(exited),通过这个状态可以让用户知道,该服务曾经已经运行成功,而不是从未运行过

  1. 通常来说,对于那些执行单次但无需长久运行的进程来说,可以采用type=oneshot,比如启动iptables,挂载文件系统的操作、关机或重启的服务等文章来源地址https://www.toymoban.com/news/detail-769173.html

到了这里,关于Linux Systemd type=simple和type=forking的区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux systemd 定时任务

    哈喽大家好,我是咸鱼。 说到 Linux 定时任务,大家用得最多的就是 crond 服务,但其实 systemd 也有类似的功能。我们不但可以通过 systemd 来管理服务,还能设置定时任务,那就是 systemd timer。 与 crond 相比,systemd 定时任务具有以下优点: 更高的精度:systemd 定时任务可以精确

    2024年04月15日
    浏览(26)
  • 在 Linux 中使用 systemd 注册服务

    Systemd 是一种现代的 Linux 系统初始化系统和服务管理器。它旨在管理系统服务的初始化、配置和控制。Systemd 的一个关键特性是它可以管理服务,这些服务是为系统提供特定功能的后台进程。在本指南中,我们将探讨如何使用 systemd 在 Linux 中注册服务。 在 Linux 系统中,system

    2024年02月13日
    浏览(48)
  • Linux systemd的概述与发展历程

    systemd是一个系统和服务管理器,广泛用于现代Linux系统。它的设计目标是取代传统的SysVinit作为Linux系统的初始化系统,提供更快的启动速度、更好的并行性和更多的功能。本文将对systemd进行概述,并探讨其发展历程。 初始化系统 systemd负责启动Linux系统,并管理系统进程。它

    2024年01月19日
    浏览(36)
  • Linux 管理 Systemd 服务的命令行工具

    systemctl 是用于管理 Systemd 服务的命令行工具。下面是一些常用的 systemctl 命令及其功能: 1. `systemctl enable service`:启用一个服务,使其在系统启动时自动启动。 2. `systemctl start service`:启动一个服务。 3. `systemctl stop service`:停止一个服务。 4. `systemctl restart service`:重新启动一

    2024年01月16日
    浏览(38)
  • 将Linux init进程设置为systemd

    在Linux操作系统中,init进程是系统启动的第一个进程。然而,随着系统的发展,新的init进程systemd已经逐渐取代了旧的init进程。如果想要将Linux init进程设置为systemd,可以按照以下步骤操作: 首先,需要检查当前系统是否已经安装了systemd。可以通过以下命令进行检查: 如果

    2024年02月15日
    浏览(46)
  • linux下通过systemd配置开机自启

    1.创建对应服务的启动脚本,放在/etc/systemd/system,名字为服务名.service 2.赋予权限 chmod 777 自启脚本绝对路径 3.重新加载systemd配置 systemctl daemon-reload 4.使用以下命令启用Kafka服务,使其在系统启动时自动运行: systemctl enable kafka 5.使用以下命令启动Kafka服务: systemctl start kafka

    2024年02月10日
    浏览(38)
  • 【Linux】使用systemd设置开机自启动命令

    systemd是Linux系统中现代化的初始化系统,可以使用它来实现开机自动运行命令。在systemd中,可以通过创建一个service文件,把要执行的命令放在其中,然后将其添加到systemd的自启动项中。 具体操作步骤如下: 首先在终端中使用sudo权限创建一个.service文件,用于存储service配置

    2024年02月08日
    浏览(72)
  • Linux 系统服务日志查询 journalctl:查询 systemd 日记

    systemd 在取代 SUSE Linux Enterprise 12 中的传统 init 脚本时(参见第 13 章 “systemd 守护程序”),引入了自身的称为日记的日志记录系统。由于所有系统事件都将写入到日记中,因此,用户不再需要运行基于 syslog 的服务。 日记本身是 systemd 管理的系统服务,全名为 systemd-journal

    2024年02月07日
    浏览(47)
  • linux目录/usr/lib/systemd/system目录详解

    init 的进化经历了这么几个阶段: CentOS 5: SysV init,串行 CentOS 6:Upstart,并行,借鉴ubuntu CentOS 7:Systemd,并行,借鉴MAC 今天我们一起来看看systemd的使用 Systemd 新特性: (1)系统引导时实现服务并行启动:服务间无依赖关系会并行启动 (2)按需激活进程:若服务非立刻使用,不会立刻

    2024年02月01日
    浏览(43)
  • 将java项目打包部署在linux系统上(配置成systemd)

    1.前置条件 1.0一些一些小问题 1.1mysql安装 1.2redis安装 1.3nginx安装 1.4jdk安装 2.打包部署 接下来就是打包,将打包文件拉上来 还有nginx的配置 还有前端包打包上传小命令 这些下期再写...

    2024年03月09日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包