Linux 守护进程

简介: Linux 守护进程

Linux 守护进程又称为 Daemon 进程,为 Linux 的后台服务进程(独立于控制终端)。该进程通常周期性地执行某种任务或等待处理某些发生的事件。其生命周期较长,通常在系统启动时开始执行,在系统关闭时终止。 Linux 中很多系统服务都是通过守护进程实现的。


Linux 中,每一个从终端开始运行的进程都会依附于一个终端(系统与用户进行交互的界面),这个终端为进程的控制终端。当控制终端关闭时,这些进程就会自动结束,但守护进程不受终端关闭的影响。


如何将一个进程变成一个守护进程,只需要遵循一些特定的流程,下面通过 5 个步骤来讲解。


Step1. 创建子进程(子进程不退出,父进程退出)

很明显,由于父进程先于子进程退出,造成子进程成为孤儿进程。此时子进程的父进程变成 init/systemd 进程。


Step2. 在子进程中创建新会话

这个步骤在进程组与会话组中已经有所介绍,使用的函数是 setsid() 。该函数将会创建一个新会话,并使进程担任该会话组的组长。同时,在会话组中创建新的进程组,该进程依然也是进程组的组长。该进程成为新会话组和进程组中唯一的进程。最后使该进程脱离终端的控制,运行在后台。


之所以需要这样处理,是因为子进程在被创建时,复制了父进程的会话、进程组和终端控制等。虽然父进程退出,但原先的会话、进程组和控制终端等并没有改变。因此,子进程并没有实现真正意义上的独立。


Step3. 改变当前的工作目录

使用 fork() 函数创建的子进程继承了父进程的当前工作目录。系统通常的做法是让根目录成为守护进程的当前工作目录。改变工作目录的函数是 chdir()

#include <unistd.h>
int chdir(const char *path);点击复制复制失败已复制


Step4. 重设文件权限掩码

文件权限掩码的作用是屏蔽文件权限中的对应位。在文件的打开和关闭中有涉及该问题。文件被创建后,其用户操作权限 mode ,将会被执行 mode&~umask ( umask 为文件权限掩码,通常用八进制数表示)。例如,文件的权限为 0666umask 值为 0002 ,那么将 umask 取反,再与文件权限相与,则文件权限值变为 0664 。由于创建的子进程继承父进程的文件权限掩码,这给子进程(守护进程)操作文件带来一定影响。因此,通常把文件权限掩码设置为 0 ,这样可以增强守护进程的灵活性。此时,文件权限掩码取反全为 1 ,与任何文件权限相与,都可保持文件最原始的状态值。


使用函数 umask() ,改变文件权限掩码,参数即为要修改的掩码值。

#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);点击复制复制失败已复制


Step5. 关闭文件描述符

新创建的子进程会从父进程继承一些已经打开的文件描述符。这些描述符可能永远不会被守护进程访问,但它们却占有一定的资源。特别需要注意的是,守护进程脱离了终端的控制,所以与终端相关的标准输入、输出、错误输出的文件描述符 012 ,已经没有了任何价值,应对关闭。具体如下所示:

int num;
num = getdtablesize();  // 获取当前进程文件描述符表大小
for (i = 0; i < num; i++){
  close(i);
}点击复制复制失败已复制


其中, getdtablesize() 函数的功能为获取文件描述符表的大小,也可以理解为获取进程打开的文件描述符的最大数量。


实现

通过以上 5 步,可以实现创建守护进程,其流程如下:

微信截图_20221209152630.png


守护进程的代码具体如下所示,守护进程每隔 3 秒向日志文件 "/tmp/daemon.log" 中写入字符串。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, const char *argv[]) {
  pid_t pid;
  int i, fd;
  char *buf = "This is a Daemon\n";
  pid = fork(); // 第一步
  if (pid < 0) {
    perror("fork error");
    return -1;
  } else if (pid > 0) {
    exit(0); // 父进程退出
  } else {
    setsid();      // 第二步
    chdir("/tmp"); // 第三步
    umask(0);      // 第四步
    for (i = 0; i < getdtablesize(); i++) {
      close(i); // 第五步
    }
    if ((fd = open("daemon.log", O_CREAT | O_WRONLY | O_TRUNC, 0600)) < 0) {
      perror("open error");
      return -1;
    }
    while (1) {
      write(fd, buf, strlen(buf));
      sleep(3);
    }
    close(fd);
  }
  return 0;
}点击复制复制失败已复制


编译并运行,可以看到终端直接退出,接下来查看 /tmp/daemon.log 文件,可以看到日志内容:

This is a Daemon
This is a Daemon
This is a Daemon
This is a Daemon
This is a Daemon
……点击复制复制失败已复制


终端查看进程信息如下:

$ ps axj | grep ./a.out
   PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
   1202  496227  496227  496227 ?             -1 Ss    1000   0:00 ./a.out
$ ps axj | grep systemd
   PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
      1    1202    1202    1202 ?             -1 Ss    1000   0:00 /lib/systemd/systemd --user点击复制复制失败已复制


可以看出进程的 ID 与组 ID 、会话 ID 保持一致,说明当前守护进程为组长。其父进程为 systemd 进程, TTY 选项为 "?" ,表示其为后台进程

目录
相关文章
|
10月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
360 32
|
8月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
300 67
|
7月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
215 16
|
7月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
143 20
|
6月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
131 0
|
6月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
200 0
|
6月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
133 0
|
6月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
179 0
|
10月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
444 34