怎么修改公司网站内容,企业163邮箱怎么申请,wordpress采集公众号,百度网站大全系统服务
书接上文: linux自启任务详解
演示系统:ubuntu 20.04
开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起 这个功能在ubuntu系统中通常由systemd提供 如果仅仅需要达成上述的场…系统服务
书接上文: linux自启任务详解
演示系统:ubuntu 20.04
开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起 这个功能在ubuntu系统中通常由systemd提供 如果仅仅需要达成上述的场景功能,则systemd的自定义服务就可以满足
什么是systemd
systemd系统和服务管理器
功能 systemd 是一个初始化系统init system和服务管理器它负责在 Linux 系统启动时启动系统的核心服务和进程。它的任务是管理系统引导、服务管理、进程监控、资源管理等。 systemd 提供了服务启动、停止、重启、日志记录等功能并管理系统的运行状态。作用 启动和管理系统服务systemd 会在系统启动时根据配置文件服务单元文件启动必要的系统服务例如网络、日志记录、定时任务等。 管理进程和依赖关系systemd 确保服务按照正确的顺序启动并且根据需要重启或停止。 资源管理通过 cgroups控制组和其他技术systemd 能够限制服务对 CPU、内存等资源的使用。配置文件 systemd 使用以 .service 结尾的单元文件unit files来定义服务。每个服务有一个单独的配置文件这些文件描述了服务如何启动、停止、重启等。 例如/etc/systemd/system/ 和 /lib/systemd/system/ 目录下存放着这些单元文件。
什么是systemctl
systemctl管理 systemd 的命令行工具
功能 systemctl 是与 systemd 配合使用的命令行工具用于启动、停止、重新启动、查看、启用或禁用 systemd 管理的服务。它是用户与 systemd 交互的主要方式。作用 启动和停止服务通过 systemctl 命令你可以启动、停止或重启任何由 systemd 管理的服务。 查看服务状态systemctl status 命令可以用来查看服务的当前状态帮助管理员诊断服务是否正常运行。 管理系统systemctl 也可用于关闭、重启、挂起系统等操作。 启用/禁用服务systemctl enable 用于设置服务开机启动systemctl disable 用于禁止服务开机启动。常见命令示例
启动服务systemctl start service_name停止服务systemctl stop service_name查看服务状态systemctl status service_name重启服务systemctl restart service_name设置服务开机启动systemctl enable service_name设置服务不开机启动systemctl disable service_name
关系
systemd 是基础systemctl 是工具 systemd 是系统和服务的管理器它负责实际的服务管理、进程监控、资源分配等。而 systemctl 是一个命令行工具用户通过它与 systemd 进行交互执行启动、停止、查看状态等操作。 可以理解为systemd 是背后的系统管理框架而 systemctl 是用户与其交互的接口。systemctl 控制 systemd systemctl 是通过向 systemd 发送指令来管理服务和系统。例如当你通过 systemctl start service_name 启动一个服务时systemctl 会告诉 systemd 启动该服务systemd 会根据服务的配置文件启动服务并管理它。
自定义自启动服务
linux自启任务详解
想要自定义一个自启服务,需要两个东西:可执行程序(我们自己的后台业务程序)和systemd的服务脚本 假设我们自己的业务程序名为:test_demo,服务脚本名为:test_demo.service 当然了这个程序仅做演示比较简单,仅有一个test_demo_main.c文件,代码如下:
#include stdio.h
#include stdlib.h
#include string.h
#include stdbool.h
#include unistd.hconst char * filePath /home/lijilei/1.txt;
const char * text hello world\n;
const char * textend end lalala\n;
int g_count 0;int main(int argc,char**argv)
{FILE *fp NULL;fp fopen(filePath,a);assert(fp 0);while(true){sleep(6);fwrite(text, strlen(text),1,fp);fflush(fp); g_count;if(g_count 10){fwrite(textend, strlen(textend),1,fp);break;}}fprintf(fp,我要写东西: %s,东西);fflush(fp);fclose(fp);return 0;}
使用cc -o test_demo test_demo_main.c 可编译出test_demo程序 该演示程序逻辑相当简单:打开一个文件/home/lijilei/1.txt,向文件中分10次写入内容,然后退出
test_demo.service文件也相当简单
#move this file to /etc/systemd/system/
[Unit]
DescriptionStart up test_demo[Service]
Typesimple
ExecStart/home/lijilei/xlib_xdnd/test_demo
Restarton-failure[Install]
WantedBymulti-user.target脚本被systemd执行的时候会拉起ExecStart指定路径下的/home/lijilei/xlib_xdnd/test_demo程序; 将脚本放到/etc/systemd/system/目录下,按循序执行如下指令:
sudo systemctl enable test_demo.service 启用服务以便在系统启动时自动启动sudo systemctl start test_demo.service 启动test_demo.service服务,也就是变相的拉起配置的ExecStart/home/lijilei/xlib_xdnd/test_demo程序sudo systemctl status test_demo.service 停止服务
当修改.service文件后执行
sudo systemctl daemon-reload 当有修改.service文件时,需重新加载 上述的配置已经可以实现开机自启一个服务运行
自定义自启动守护进程
自启动守护进程的业务场景
在上述自启服务的基础上,将业务服务程序改为守护进程程序,使用守护进程去守护目标业务程序会更方便的控制业务程序的生命周期; 比如将守护进程改为看门狗程序,业务程序一直给看门狗发指令(喂狗),当业务程序因为业务崩溃了,则守护进程(看门狗主动拉起)业务程序,当然了我这里不会演示如何写一个看门狗程序,这里用定时查看进程快照的方式检测目标业务程序是否在执行,如果不在执行则拉起
什么是守护进程
守护进程是个孤儿进程,它的运行脱离了进程组的管控,无法接受进程退出信号,会一直运行在后台直到本身发生崩溃退出
为什么使用守护进程
守护进程的特性决定了它不会因为任何退出信号而关闭,所以适合用来执行监控任务,只要守护进程自带的业务逻辑足够简单,那守护进程将永远运行,直到系统关机,能让守护进程退出的方法只有三种
系统关机找到守护进程的pid手动kill守护进程因自己的运行bug崩溃退出
因为systemd的功能我们可以克服第一个方法跟第三个方法导致的守护进程因关机或崩溃而无法再次运行的问题
怎么写一个守护进程
这里创建一个名为daemond.c的文件,文件内容如下:
// daemon.c
#include stdio.h
#include signal.h
#include sys/param.h
#include sys/types.h
#include unistd.h
#include sys/stat.h
#include stdlib.h
#include time.h
#include syslog.h
#include errno.h
#include string.h
#include assert.hstatic FILE *g_fp NULL;
static time_t g_now;
static const char* PIDFile /var/daemond.pid;
//static const char* LOCKDir /var/run/daemond;
static const char* LOGFile /var/log/daemond.txt;//看护的程序名字,可以是多个
static const char* PROCESSName1 test_demo;
static const char* PROCESSName2 NULL;static int init_daemon(void)
{pid_t pid;int i;pid fork();if(pid 0){//第一步结束父进程使得子进程成为后台exit(0);}else if(pid 0){return -1;}/*第二步建立一个新的进程组在这个新的进程组中子进程成为这个进程组的首进程以使该进程脱离所用终端*/setsid();/*再次新建一个子进程退出父进程保证该进程不是进程组长同时让该进程无法再打开一个新的终端*/pid fork();if(pid 0){exit(0);}else if(pid 0){return -1;}//第三步关闭所用从父进程继承的不再需要的文件描述符for(i 0;i NOFILE;close(i));//第四步改变工作目录使得进程不与任何文件系统联系chdir(/);//第五步将文件屏蔽字设置为0umask(0);//第六步忽略SIGCHLD信号 执行第二步后就不需要执行该步骤signal(SIGCHLD,SIG_IGN);// 1. 忽略其他异常信号// 忽略子进程结束信号防止产生僵尸进程//signal(SIGCLD, SIG_IGN);// 忽略管道破裂信号防止程序因向已关闭的管道写入而异常退出//signal(SIGPIPE, SIG_IGN);// 忽略停止信号守护进程通常不应被外部信号随意停止//signal(SIGSTOP, SIG_IGN);return 0;
}static int program_running_number(const char *prog)
{if(prog NULL) {return 0;}FILE *fp;int count 0;char buf[8] {0};char command[128];snprintf(command, sizeof(command), \ps -ef | grep -v grep | grep -w -c %s, prog);command[sizeof(command) - 1] \0;fp popen(command, r);if (fp NULL) {time(g_now);fprintf(g_fp,系统时间\t%s\t\t execute %s failed: %s,ctime(g_now),command, strerror(errno));fflush(g_fp);return 0;}if (fgets(buf, sizeof(buf), fp)) {count atoi(buf);}pclose(fp);return count;
}static int createPIDFile(const char* File)
{umask(000);FILE *pidfile fopen(File, w);if (pidfile) {fprintf(pidfile, %d, getpid());fclose(pidfile);return 0;} else {return -1;}
}static int createLOCKDir(const char* dir)
{char cmd[256] {0};sprintf(cmd,mkdir %s,dir);int ret system(cmd);if (ret 0) {return 0;} else {return -1;}
}static void watchProcess(const char** prcsessList)
{for (const char **prog prcsessList; *prog; prog) {if (program_running_number(*prog) 0) {//fprintf(g_fp,%s is running.\n, *prog);} else {time(g_now);fprintf(g_fp,系统时间%s %s isnt running.\n,ctime(g_now),*prog);fflush(g_fp);//再次执行唤起目标程序指令(可替换拉起进程指令)char cmd[256] {0};sprintf(cmd,sudo systemctl start %s.service,*prog);fprintf(g_fp,执行命令: %s\n,cmd);int value system(cmd);if (value -1) {time(g_now);fprintf(g_fp,系统时间%s %s : system() failed\n,ctime(g_now),cmd);fflush(g_fp);} else if (WIFEXITED(value)) {time(g_now);fprintf(g_fp,系统时间%s %s executed successfully with exit code %d: succeed\n,ctime(g_now),cmd,WEXITSTATUS(value));fflush(g_fp);} else if (WIFSIGNALED(value)) {time(g_now);fprintf(g_fp,系统时间%s %s : terminated by signal %d\n,ctime(g_now),cmd,WTERMSIG(value));fflush(g_fp);} else {time(g_now);fprintf(g_fp,系统时间%s %s : Unknown status\n,ctime(g_now),cmd);fflush(g_fp);printf(Unknown status\n);}} }
}int main()
{init_daemon(); createPIDFile(PIDFile);//createLOCKDir(LOCKDir);while(1) {sleep(3);g_fp fopen(LOGFile,a);if(g_fp NULL) {return -1;}const char *program_name_list[] {PROCESSName1, PROCESSName2};//这里修改进程看护逻辑watchProcess(program_name_list);fflush(g_fp);fclose(g_fp);}return 0;
}使用cc -o daemond daemon.c 可编译出daemond守护进程程序 该daemond逻辑比较简单,就是负责监视test_demo程序,如果test_demo程序退出了就调用systemctl指令,执行test_demo.service,再次拉起test_demo
daemond.service的写法就稍微跟test_demo.service不同了
#move this file to /etc/systemd/system/
[Unit]
DescriptionStart up daemond
Afternetwork.target
[Service]
Userroot
Grouproot
ExecStart/home/lijilei/xlib_xdnd/daemond --single-instance
#当进程退出时自动重启
Restartalways
#适用于后台运行的服务systemd 等待父进程退出并且通过 PID 文件确认进程启动
Typeforking
#适用于后台运行的服务systemd 等待父进程退出并且通过 PID 文件确认进程启动
PIDFile/var/daemond.pid
#只终止主进程不终止子进程
KillModeprocess
#RestartSec5 #服务崩溃后会等待 5 秒钟再重启
#StartLimitIntervalSec10 #定义了一个 10 秒的时间窗口
#StartLimitBurst1 #在 10 秒内服务最多重启 1 次。如果超过这个次数systemd 将不会再重启服务
#删除PID文件
ExecStopPost/bin/rm -f /var/daemond.pid
#删除日志文件
ExecStopPost/bin/rm -f /var/log/daemond.txt
[Install]
WantedBymulti-user.target将脚本放到/etc/systemd/system/目录下,按顺序执行如下指令:
sudo systemctl enable daemond.service 启用服务以便在系统启动时自动启动sudo systemctl start daemond.service daemond.service服务,也就是变相的拉起配置的/home/lijilei/xlib_xdnd/daemond程序
执行效果
把test_demo.service和daemond.service都加入开机自启后会出现如下现象:
test_demo.service会拉起test_demo程序test_demo程序在完成打印后退出daemond查找进程快照发现test_demo退出,就执行systemctl脚本test_demo.servicetest_demo.service会拉起test_demo程序…如此反复执行
查看下daemon.service的执行状态
$ sudo systemctl status daemond.service ● daemond.service - Start up daemondLoaded: loaded (/etc/systemd/system/daemond.service; enabled; vendor preset: enabled)Active: active (running) since Fri 2024-11-22 01:43:28 UTC; 2 weeks 0 days agoMain PID: 125749 (daemond)Tasks: 1 (limit: 14203)Memory: 13.9MCGroup: /system.slice/daemond.service└─125749 /home/lijilei/xlib_xdnd/daemond --single-instanceWarning: journal has been rotated since unit was started, output may be incomplete.发现这个服务已经连续运行两周了 查看下1.txt内容:
发现已经打印了20几万行信息了
附录
如果你在 systemd 单元文件中使用了其他不熟悉或不常见的配置项建议通过以下命令来验证服务单元文件的正确性
sudo systemd-analyze verify /etc/systemd/system/your_service.service 这个框架有个问题就是daemon在调用system()函数时能执行但是返回值是-1,猜测是由systemctl导致的.后面我再研究研究 以上