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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值