什么是守护进程?

在Linux系统中,守护进程(Daemon)是一种在后台运行的特殊进程,它不依赖于终端控制,通常用来执行系统级任务或服务。守护进程的名字来源于古希腊神话中的"守护精灵"(daemon)概念,正如其名,它们默默地在后台工作,维持着系统的正常运行。

与普通进程不同,守护进程具有以下特点:

  • 脱离终端控制,在后台运行
  • 生命周期通常很长,从系统启动到关闭
  • 通常以root权限运行
  • 没有控制终端,不会接收终端信号
常见的守护进程示例

Linux系统中充满了各种各样的守护进程,以下是一些常见的例子:

  • sshd - Secure Shell守护进程,负责处理远程登录请求
  • httpd/apache2 - Web服务器守护进程
  • mysqld - MySQL数据库服务器
  • crond - 定时任务守护进程
  • syslogd/rsyslogd - 系统日志记录服务
  • ntpd - 网络时间协议守护进程,负责时间同步

我们可以使用命令px -elf可以看到很多进程。

$ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0  2014 ?        00:01:28 init [3]         
root         2     0  0  2014 ?        00:00:00 [kthreadd]
root         3     2  0  2014 ?        00:00:43 [migration/0]
root         4     2  0  2014 ?        00:07:34 [ksoftirqd/0]
root         5     2  0  2014 ?        00:00:01 [migration/0]
root         6     2  0  2014 ?        00:00:03 [watchdog/0]
root         7     2  0  2014 ?        00:03:04 [migration/1]
root         8     2  0  2014 ?        00:00:01 [migration/1]
root         9     2  0  2014 ?        00:01:44 [ksoftirqd/1]
root        10     2  0  2014 ?        00:00:01 [watchdog/1]
root        11     2  0  2014 ?        00:02:21 [migration/2]
root        12     2  0  2014 ?        00:00:01 [migration/2]`
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

其中带中括号的进程就是守护进程。

如果进程带有中括号,就是内核的守护进程;

不带中括号但是d结尾的就是程序的守护进程

Linux中的守护进程:默默守护系统的无名英雄_服务器

守护进程的基本特点

1、脱离终端运行:启动后与启动它的终端分离;不受终端关闭的影响;不接收终端的输入,也不向终端直接输出;通常通过系统日志记录运行信息。
2、长生命周期:守护进程通常具有很长的生命周期:从系统启动时开始运行,持续运行直至系统关闭;设计为7×24小时不间断工作,即使暂时没有任务也保持运行状态。
3、后台服务特性:守护进程本质上是提供服务的后台程序:通常监听某个端口或文件,等待服务请求的到来,响应请求并提供相应服务,服务完成后继续等待而非退出。
4、特殊的进程关系:守护进程有特殊的进程关系结构:通常是孤儿进程(父进程为init/systemd);创建新会话(session leader),没有控制终端(controlling terminal),进程组ID通常等于进程ID。
5. 特殊的运行环境:守护进程运行在特殊的环境中:工作目录通常设置为根目录(/),文件创建掩码(umask)通常设为0,继承但会关闭不必要的文件描述符,可能以特权用户(如root)身份运行。
6. 系统资源管理:守护进程需要特别注意资源管理:谨慎管理内存使用,避免内存泄漏,正确处理信号,特别是SIGHUP等,合理控制CPU使用率,妥善处理文件描述符。
7. 启动方式多样:守护进程可以通过多种方式启动:系统初始化时由init/systemd启动;由inetd/xinetd等超级守护进程按需启动;由其他守护进程或管理工具启动;虽然可以手动启动,但不推荐这样做。


与普通进程不同,守护进程正是通过这些特点确保了系统服务的稳定性和可靠性,成为Linux系统中不可或缺的组成部分。

守护进程开发方式
直接借助damon()函数开发

当调用这个函数时,此进程变为守护进程,在后台运行

//1.头文件
#include <unistd.h>

//2.函数原型
int daemon(int nochdir, int noclose);

//3.函数参数:
nochdir:为0时表示将当前目录更改至“/”(工作目录)
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
    
//4.返回值:
成功则返回0,失败返回-1
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
用例
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>

#include <stdbool.h>   //bool
static bool flag = true;

void handler(int sig)
{
    printf("I got a signal %d\nI'm quitting.\n", sig);
    flag = false;
}
int main()
{
    time_t t;
    int fd;
    //创建守护进程
    if(-1 == daemon(0, 0))
    {
        printf("daemon error\n");
        exit(1);
    }
    //设置信号处理函数
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(sigaction(SIGQUIT, &act, NULL))
    {
        printf("sigaction error.\n");
        exit(0);
    }
    //进程工作内容
    while(flag)
    {
        fd = open("/home/orangepi/daemon.log", O_WRONLY | O_CREAT | O_APPEND,
                  0644);
        if(fd == -1)
        {
            printf("open error\n");
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));
        close(fd);
        sleep(10);
    }
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

当编译后运行该程序,该程序会在后台运行。该程序会每十秒在文件目录下,写入当前时间。

我们可以使用命令px -elf |grep a.out|grep -v grep来进行查询到该程序的id值。

可以使用kill -l查看信号量列表:

Linux中的守护进程:默默守护系统的无名英雄_运维_02

运行结果如下:

Linux中的守护进程:默默守护系统的无名英雄_linux_03

Linux中的守护进程:默默守护系统的无名英雄_守护进程_04

开机自启程序

我们知道守护进程有个特点,随着内核的启动而启动,但是我们只是通过调用daemon函数来将这个进程变为了守护进程,内核并不知道。其次守护进程会被init进程来启动,但是init进程并不会无缘无故的启动你,他有对应的一些规则。一般说,在/etc/init.d目录下。
那,我们想要开机自启,怎么办,我们有更简单的方法。

方法一

可以修改/etc/rc.local文件。

在该文件末尾,添加好,可执行文件的路径。

Linux中的守护进程:默默守护系统的无名英雄_守护进程_05

重启终端,通过ps -elf来找到进程。

方法二
  1. 创建systemd服务单元文件
    创建服务配置文件/etc/systemd/system/mydaemon.service:
[Unit]
Description=我的自定义守护进程服务
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/mydaemon
Restart=on-failure
RestartSec=5s
User=root
Group=root

[Install]
WantedBy=multi-user.target
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

配置说明:

  • Type=simple:声明这是一个简单类型的服务
  • Restart=on-failure:当进程异常退出时自动重启
  • RestartSec=5s:重启前等待5秒
  • User和Group:指定运行服务的用户和组
  1. 重新加载systemd配置
    让systemd重新加载所有单元文件:
sudo systemctl daemon-reload
  • 1.
  1. 启动服务并测试
sudo systemctl start mydaemon
sudo systemctl status mydaemon
  • 1.
  • 2.

检查日志确认服务正常运行:

journalctl -u mydaemon -f
  • 1.
  1. 启用开机自启
sudo systemctl enable mydaemon
  • 1.
总结

守护进程是Linux系统的基石,它们默默无闻地执行着各种关键任务。了解守护进程的工作原理不仅有助于系统管理,也能帮助开发者编写更健壮的服务器程序。随着系统管理工具的发展,创建和管理守护进程变得越来越简单,但理解其背后的机制仍然是每个Linux用户和开发者的宝贵知识。