当前位置: 首页 > news >正文

systemd使用入门

systemd负责管理整个操作系统,功能非常强大。我们在实际工作中,常常使用systemd来管理我们的服务。

(1)想要开机之后自动启动,从而不需要每次开机之后都手动启动进程,可以通过systemd来实现。

(2)如果我们要求进程异常崩溃之后要自动拉起来,可以通过systemd来实现。

(3)我们有3个服务:a、b、c,其中服务b和c的启动依赖于服务a,只有服务a启动之后,服务b和c才可以启动,这样的依赖关系管理,我们也可以通过systemd来实现。

(4)想要限制服务所使用的资源,比如将服务的cpu利用率限制在50%,内存使用限制在1G,我们可以通过systemd来实现。

(5)如果我们想要设置进程的调度策略,那么可以通过systemd来实现。

1最简example

如下是我们自己的代码,在main函数中有一个死循环,在循环中打印一条日志。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main() {int i = 0;while(1) {printf("systemd daemon example\n");}return 0;
}

service文件,该文件是服务的配置文件,也是systemd可以解析的文件。

[Unit]
Description=hello systemd[Service]
ExecStart=/home/wangyanlong/systemdtest/daemon[Install]
WantedBy=multi-user.target

将源码编译成可执行文件:gcc daemon.c -o daemon

将service文件拷贝到systemd的工作目录:cp helloworld.service /lib/systemd/system/

systemd配置文件重新加载:systemctl daemon-reload

启动服务:systemctl start helloworld.service

查看服务状态:ststemctl status helloworld.service

查看日志:joutnalctl -u helloworld.service

2服务类型

systemd中的服务类型,常用的有simple、forking和notify:

2.1simple

最简单的类型,默认的服务类型,上边的例子就是simple类型,当我们的服务本身就会一直运行,那么可以配置simple类型。

2.2forking

forking类型的服务,systemd启动的服务内部会创建子进程,子进程运行之后父进程就退出了。

如下代码,在主进程中会通过fork来执行一个子进程,fork之后,父进程退出。这种情况下,需要配置为forking类型。当父进程退出后,service的状态会切为active状态,父进程退出前,使用systemctl status xxx看到的服务状态为activating。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>int main() {printf("fork main\n");// sleep(20);printf("before fork\n");pid_t pid = fork();if (pid == 0) {execve("/home/wangyanlong/systemdtest/daemon", NULL, NULL);}sleep(20);printf("fork end\n");return 0;
}
[Unit]
Description=hello systemd[Service]
Type=forking
ExecStart=/home/wangyanlong/systemdtest/fork[Install]
WantedBy=multi-user.target

2.3notify

对于simple和forking类型来说,服务从activating切换为active状态的时机,完全由systemd内部来决定。simple类型,当把服务拉起来之后,便会切换为active状态;forking类型的服务,当主进程退出后,便会切换到active状态。这样的方式,用户不可控,当我们对多个服务有严格的启动顺序要求的时候,这种方式难以满足这样的使用场景,在这种情况下,就需要使用notify。

notify类型的服务,需要用户在代码中调用sd_notify函数来向systemd报状态,systemd收到信息之后则会将服务的状态切换为active状态。

如下代码,在main函数中先睡20s,然后调用sd_notify来通知systemd。通过systemctl status helloworld.service可以看到,启动20s之后,服务的状态切换为active。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <systemd/sd-daemon.h>int main() {sleep(20);sd_notify(0, "READY=1");int i = 0;while(1) {printf("systemd daemon example\n");// sleep(5);}return 0;
}

3开机自动重启

使用systemctl enable xxx和systemctl disable xxx可以分别使能开机自启动和禁止开机自启动。

4异常退出自动重启

当服务异常退出时,systemd能够自动将服务重新拉起,这是我们实际工作中经常需要的。同时,当进程正常退出的时候,systemd不会将服务拉起。

如下是进程退出后重启的最简单的例子。Restart配置为always,说明只要进程退出,都会重启,不管进程是正常退出还是异常退出。RestartSec是进程退出之后,间隔多长时间才会重启。

[Unit]
Description=hello systemd[Service]
Type=simple
ExecStart=/home/wangyanlong/systemdtest/daemon
Restart=always
RestartSec=1[Install]
WantedBy=multi-user.target

Restart有多个配置:no、always、on-success、on-failure、on-abnormal、on-abort、on-watchdog。其中no是默认值,表示永远不会重启;always是永远会重启,不管进程是正常退出还是异常退出;on-success表示只有进程正常退出的时候才会重启,所谓正常退出,指的是退出码是0,或者被信号SIGHUP、SIGINT、SIGTERM、SIGPIPE或者被中的一个杀死;on-failure表示失败,所谓失败,是当退出码不为0,或者服务是被上述几个信号之外的信号杀死的。

在实际使用中,具体什么退出码是正常的,什么退出码是异常的,我们自己也可以修改,比如我们设置信号SIGKILL为正常的,那么使用SIGKILL杀死进程的时候,也属于正常退出,不会重启。

配置如下:

[Unit]
Description=hello systemd

[Service]
Type=simple
ExecStart=/home/wangyanlong/systemdtest/daemon
Restart=on-failure
RestartSec=1
SuccessExitStatus=SIGKILL

[Install]
WantedBy=multi-user.target

5调度策略

systemd可以设置服务的调度策略和优先级,如下配置,其中CPUSchedulingPolicy=rr和
CPUSchedulingPriority=50分别设置调度策略为RR,优先级是50。

[Unit]
Description=hello systemd[Service]
Type=simple
ExecStart=/home/wangyanlong/systemdtest/daemon
Restart=on-failure
RestartSec=1
SuccessExitStatus=SIGKILL
CPUSchedulingPolicy=rr
CPUSchedulingPriority=50[Install]
WantedBy=multi-user.target

6资源限制

使用systemd管理的服务,可以限制服务所使用的资源。systemd通过cgroup来对服务做资源限制,可以限制的资源包括cpu、内存、io等。

如下代码,main函数中是一个while(1)死循环,默认情况下cpu使用率达到100%。但是通过CPUQuota=50%可以将cpu使用限制到50%。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main() {while(1) {}sleep(10);return 0;
}

如下配置文件,如果限制了cpu,那么在cgroup下便会创建一个对应的cpu controller,如下图所示,可以看到,在service文件中配置的CPUQuota=50%,在cgroup中对应的参数是cpu.cfs_quota_us,值为50000。

[Unit]
Description=hello systemd

[Service]
Type=simple
ExecStart=/home/wangyanlong/systemdtest/daemon
CPUQuota=50%

[Install]
WantedBy=multi-user.target

7服务之间的依赖

与进程依赖相关的配置有多个After和Before,Requires,Wants。

After、Before的依赖关系最弱,Wants次之,Requires最强。

After和Before可以决定服务启动的顺序,只是约束启动顺序而已,如果在service2.service中配置After=service1.servce,那么即使service1没有启动,service2也是可以启动的。

Wants表示服务之间的依赖关系,假如在service2.service中配置了Wants=service1.servce,如果sevice1没有启动的情况下启动service2,那么会尝试启动service1,不管service1是否能启动,servie2最终都会启动。

Requires是最强的依赖,如果在service2.service中配置了Requires=service1.servce,如果service1启动失败,那么service2也会启动失败,也就是说必须要在service1正常运行的时候,service2才可以正常启动,正常运行。如果service1和service2都在正常运行,这个时候将service1关闭了,那么service2也会被关闭。同样的情况,如果是service2 Wants service1,两个服务都在运行,如果间service1关闭,那么service2还会运行,不会被关闭。

service1.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <systemd/sd-daemon.h>int main() {sleep(20);sd_notify(0, "READY=1");int i = 0;while(1) {printf("systemd s1\n");// sleep(5);}return 0;
}

service2.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <systemd/sd-daemon.h>int main() {sleep(20);sd_notify(0, "READY=1");int i = 0;while(1) {printf("systemd s2\n");// sleep(5);}return 0;
}

service1.service

[Unit]
Description=service1

[Service]
Type=notify
ExecStart=/home/wangyanlong/systemdtest/service1

[Install]
WantedBy=multi-user.target

service2.service

[Unit]
Description=service2
Wants=service1.service

[Service]
Type=notify
ExecStart=/home/wangyanlong/systemdtest/service2

[Install]
WantedBy=multi-user.target

相关文章:

  • 数据结构——顺序表(基础代码题)
  • golang 如何生成唯一的 UUID
  • 一个OpenHarmony rk3568编译问题
  • 品牌增长新引擎:TikTok达人内容营销策略解析
  • 6--苍穹外卖-SpringBoot项目中菜品管理 详解(二)
  • spring boot 项目中redis的使用,key=value值 如何用命令行来查询并设置值。
  • Python编码系列—Python访问者模式:为对象结构添加新功能的艺术
  • 如何快速免费搭建自己的Docker私有镜像源来解决Docker无法拉取镜像的问题(搭建私有镜像源解决群晖Docker获取注册表失败的问题)
  • vue3 商城系统中的 sku 功能的实现
  • 优秀在线 notion 头像制作工具分享-Notion Avatar Maker
  • 35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
  • Chromium 设置页面打开系统代理源码分析c++
  • C语言 | Leetcode C语言题解之第443题压缩字符串
  • 《中国电子报》报道: 安宝特AR为产线作业者的“秘密武器
  • 桥接模式和NET模式的区别
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • Docker 笔记(2):Dockerfile
  • Fabric架构演变之路
  • Java应用性能调优
  • magento 货币换算
  • spark本地环境的搭建到运行第一个spark程序
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 给Prometheus造假数据的方法
  • 面试总结JavaScript篇
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 硬币翻转问题,区间操作
  • 用jQuery怎么做到前后端分离
  • 浅谈sql中的in与not in,exists与not exists的区别
  • ​决定德拉瓦州地区版图的关键历史事件
  • ‌Excel VBA进行间比法设计
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • #Linux(make工具和makefile文件以及makefile语法)
  • #Spring-boot高级
  • #面试系列-腾讯后端一面
  • (1)bark-ml
  • (1)Nginx简介和安装教程
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (bean配置类的注解开发)学习Spring的第十三天
  • (python)数据结构---字典
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (搬运以学习)flask 上下文的实现
  • (四)Linux Shell编程——输入输出重定向
  • (转)3D模板阴影原理
  • (转)四层和七层负载均衡的区别
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .net 流——流的类型体系简单介绍
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .net2005怎么读string形的xml,不是xml文件。
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • .NET微信公众号开发-2.0创建自定义菜单
  • [C#]winform部署yolov9的onnx模型