在 Linux 系统编程中,守护进程(daemon)是一种在后台运行的进程,通常是由系统启动时自动启动的。它们通常没有与用户交互的终端,而是在系统运行时执行各种系统任务,如网络服务、日志记录等。
shell 终端是一个会话
一个会话里面可以有多个进程组
一个进程组由一个进程或多个进程组成
一个进程可以有多个线程
一个 shell 登录就可看为一个会话
那么怎么在一个会话中产生多个进程呢?
例如使用管道符,前面的输出为后面的输入
@DESKTOP-QDLGRDB:/$ ls | more
gaowanlu.md
README.md
SUMMARY-fa-bian-cheng
bing++-even-better
c-zuo-xi-tong
cao-suan-ji-wang-luo
ji-dev
server-ji-mo-shi
she-ju-jie-gou-yu-suan-fa
shu-ju-ku
shu testcode
前台进程组与后台进程组:最多只能有一个前台进程组
会话的标识为 sid
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);//创建新对话
pid_t getsid(pid_t pid);//获取指定进程sid
setsid 用于创建一个会话在调用 setsid 的进程不是其所在进程组的 leader 的话。当进程使用 setsid 后成为新的进程组的 leader 且脱离控制终端、且称为新对话的 leader
守护进程脱离控制终端 tty:? ,PID、PGID、SID 相同
gaowanlu@DESKTOP-QDLGRDB:/$ ps axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? 0 Ssl 0 0:00 /init
1 6 1 1 ? 0 Sl 0 0:00 plan9 --control-socket 6 --log-level 4 --server-fd 7 --pipe-fd 9 --socket-
1 9 9 9 tty1 0 Ss 0 0:00 /init
9 10 10 9 tty1 0 S 1000 0:00 -bash
1 96 96 96 tty2 0 Ss 0 0:00 /init
96 97 97 96 tty2 0 S 1000 0:00 -bash
97 724 724 96 tty2 0 S 0 0:00 su gaowanlu
724 725 725 96 tty2 0 S 1000 0:00 bash
1 921 921 921 tty3 0 Ss 0 0:00 /init
921 922 922 921 tty3 0 S 1000 0:00 -bash
922 993 993 921 tty3 0 R 1000 0:00 ps axj
// set/get process group
#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);
pid_t getpgrp(void); /* POSIX.1 version */
pid_t getpgrp(pid_t pid); /* BSD version */
int setpgrp(void); /* System V version */
int setpgrp(pid_t pid, pid_t pgid); /* BSD version */
创建一个不停在/tmp/out 文件中追加内容的守护进程
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
#define FILENAME "/tmp/out"
void myfunc()
{
pid_t pid = fork();
if (pid < 0)
{
("fork error");
perror(1);
exit}
if (pid > 0)
{
(0);
exit}
// 子进程
int fd = open("/dev/null", O_RDWR);
if (fd < 0)
{
("open error");
perror(1);
exit}
(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
dup2if (fd > 2)
{
(fd);
close}
();
setsid("/"); // 设置工作目录位置
chdir}
int main(int argc, char **argv)
{
();
myfuncFILE *fp;
= fopen(FILENAME, "w");
fp if (fp == nullptr)
{
("fopen error");
perror(1);
exit}
for (long long i = 0;; i++)
{
(fp, "%lld\n", i);
fprintf(fp);
fflush(1);
sleep}
return 0;
}
// 父进程提前结束
// 子进程setsid称为守护进程,PPID变为1即init进程接管
运行
gaowanlu@DESKTOP-QDLGRDB:/$ ./main
gaowanlu@DESKTOP-QDLGRDB:/$ tail -f /tmp/out
0
1
2
3
4
5
6
7
8
^C
gaowanlu@DESKTOP-QDLGRDB:/$ ps axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? 0 Ssl 0 0:00 /init
1 1133 1133 1133 ? 0 Ss 1000 0:00 ./main
gaowanlu@DESKTOP-QDLGRDB:/$ kill -9 1133
每个应用都有必要去写系统日志,在守护进程中在运行过程中记录有效的过程信息
一般存放在 /var/log 目录下
@DESKTOP-QDLGRDB:/$ ls /var/log
gaowanlu.log apt dist-upgrade journal lastlog private unattended-upgrades
alternatives.log landscape mysql ubuntu-advantage.log wtmp apache2 btmp dpkg
syslogd 服务,将要写的日志内容提交给 syslogd 服务
@DESKTOP-QDLGRDB:/$ ps axj | grep "syslogd"
gaowanlu
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND922 1141 1140 921 tty3 0 S 1000 0:00 grep syslogd
相关函数
//send messages to the system logger
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
//option 与 facility可以看man手册
void syslog(int priority, const char *format, ...);
//priority 看 man手册
void closelog(void);
void vsyslog(int priority, const char *format, va_list ap);
->syslog->closelog
openlog
: LOG_CONS |LOG_NDELAY| LOG_NOWAIT | LOG_ODELAY | LOG_PERROR | LOG_PID
option: LOG_AUTH |LOG_AUTHPRIV | LOG_CRON |LOG_DAEMON|LOG_FTP | LOG_KERN | LOG_LOCAL0 through LOG_LOCAL7| LOG_LPR | LOG_MAIL| LOG_NEWS | LOG_SYSLOG | LOG_USER (default)|LOG_UUCP
facility: LOG_CRIT|LOG_ERR | LOG_WARNING | LOG_NOTICE | LOG_INFO | LOG_DEBUG priority
实例
#include <iostream>
#include <syslog.h>
#include <unistd.h>
using namespace std;
int main(int argc, char **argv)
{
("gaowanlu", LOG_PID, LOG_DAEMON);
openlogfor (int i = 0; i < 10; i++)
{
(LOG_INFO, "%d", i);
syslog(1);
sleep}
();
closelogreturn 0;
}
//ubuntu
root@drecbb4udzdboiei-0626900:/tmp# make main
g++ main.cpp -o main
root@drecbb4udzdboiei-0626900:/tmp# ./main
root@drecbb4udzdboiei-0626900:/tmp# tail /var/log/syslog
Feb 22 00:52:41 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 0
Feb 22 00:52:42 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 1
Feb 22 00:52:43 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 2
Feb 22 00:52:44 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 3
Feb 22 00:52:45 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 4
Feb 22 00:52:46 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 5
Feb 22 00:52:47 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 6
Feb 22 00:52:48 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 7
Feb 22 00:52:49 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 8
Feb 22 00:52:50 drecbb4udzdboiei-0626900 gaowanlu[4046718]: 9
root@drecbb4udzdboiei-0626900:/tmp#
syslog 也有相关配置、配置文件一般在
/etc/rsyslog.conf
有时要确保某些守护进程只运行一个实例,不能有多个?这怎么实现
一般是利用锁文件/var/run/name.pid
@drecbb4udzdboiei-0626900:/etc# cat /var/run/docker.pid
root1034
@drecbb4udzdboiei-0626900:/etc# ps axf | grep "docker"
root1034 ? Ssl 10:11 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
4046887 pts/0 R+ 0:00 \_ grep --color=auto docker
@drecbb4udzdboiei-0626900:/etc# root
会对比 name.pid 内容中的 pid 对应想在的响应进程 pid,对比是否运行的 name 程序
@drecbb4udzdboiei-0626900:/etc# service docker start
root@drecbb4udzdboiei-0626900:/etc# service docker start
root@drecbb4udzdboiei-0626900:/etc# cat /var/run/docker.pid
root1034root@drecbb4udzdboiei-0626900:/etc#
/etc/rc*.d 中
但现在都使用 systemd
使用 systemd 设置开机启动
# 检查systemd
root@drecbb4udzdboiei-0626900:/etc# systemd --version
systemd 249 (249.11-0ubuntu3.6)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
# 编写自己的自启动service
root@drecbb4udzdboiei-0626900:/etc/systemd/system# ls
apache2.service dbus-org.freedesktop.timesync1.service multi-user.target.wants sshd.service
apache-htcacheclean.service default.target.wants network-online.target.wants sudo.service
cloud-init.target.wants emergency.target.wants open-vm-tools.service.requires sysinit.target.wants
cloudResetPwdAgent.service getty.target.wants paths.target.wants syslog.service
cloudResetPwdUpdateAgent.service graphical.target.wants rescue.target.wants timers.target.wants
dbus-org.freedesktop.ModemManager1.service iscsi.service sleep.target.wants vmtoolsd.service
dbus-org.freedesktop.resolve1.service mdmonitor.service.wants sockets.target.wants
dbus-org.freedesktop.thermald.service multipath-tools.service sshd-keygen@.service.d
如写一个/etc/systemd/system/autostart.service
#/etc/systemd/system/autostart.service
[Unit]
Description=python_detection
Documentation=
After=network.target
Wants=
Requires=
[Service]
ExecStart=/home/nvidia/autostart.sh
ExecStop=
ExecReload=/home/nvidia/autostart.sh
Type=forking
[Install]
WantedBy=multi-user.target
/home/nvidia/autostart.sh
#!/bin/bash
sleep 10
启动与设置自启动
$sudo systemctl start autostart.service
$sudo journalctl -f -u autostart.service # 查看程序输出
$sudo systemctl enable autostart.service # 设置开机自启
service 文件格式,Unit、Service、Install 必须要有
Description:运行软件描述
Documentation:软件的文档
After:因为软件的启动通常依赖于其他软件,这里是指定在哪个服务被启动之后再启动,设置优先级
Wants:弱依赖于某个服务,目标服务的运行状态可以影响到本软件但不会决定本软件运行状态
Requires:强依赖某个服务,目标服务的状态可以决定本软件运行。
ExecStart:执行命令
ExecStop:停止执行命令
ExecReload:重启时的命令
Type:软件运行方式,默认为simple
WantedBy:这里相当于设置软件,选择运行在linux的哪个运行级别,只是在systemd中不在有运行级别概念,但是这里权当这么理解。
代码样例都存在问题、因为没有解决信号杀死问题等