嵌入式Linux-守护进程

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

1.守护进程

1.1 何为守护进程

守护进程Daemon也称为精灵进程是运行在后台的一种特殊进程它独立于控制终端并且周期性地执行某种任务或等待处理某些事情的发生主要表现为以下两个特点

  1. 长期运行。
    守护进程是一种生存期很长的一种进程它们一般在系统启动时开始运行除非强行终止否则直到系统关机都会保持运行。与守护进程相比普通进程都是在用户登录或运行程序时创建在运行结束或用户注销时终止但守护进程不受用户登录注销的影响它们将会一直运行着、直到系统关机。
  2. 与控制终端脱离。
    在 Linux 中系统与用户交互的界面称为终端每一个从终端开始运行的进程都会依附于这个终端当控制终端被
    关闭的时候该会话就会退出由控制终端运行的所有进程都会被终止这使得普通进程都是和运行该进程的终端相绑定的但守护进程能突破这种限制它脱离终端并且在后台运行脱离终端的目的是为了避免进程在运行的过程中的信息在终端显示并且进程也不会被任何终端所产生的信息所打断。

守护进程是一种很有用的进程。Linux 中大多数服务器就是用守护进程实现的譬如Internet 服务器inetd、Web 服务器 httpd 等。同时守护进程完成许多系统任务譬如作业规划进程 crond 等。

守护进程 Daemon通常简称为 d一般进程名后面带有 d 就表示它是一个守护进程。守护进程与终端无任何关联用户的登录与注销与守护进程无关、不受其影响守护进程自成进程组、自成会话即pid=gid=sid。通过命令"ps -ajx"查看系统所有的进程如下所示
在这里插入图片描述

  1. TTY 一栏是问号表示该进程没有控制终端也就是守护进程
  2. COMMAND 一栏使用中括号[]括起来的表示内核线程这些线程是在内核里创建没有用户空间代码因此没有程序文件名和命令行通常采用 k 开头的名字表示 Kernel内核。

1.2 如何编写守护进程的程序

  1. 创建子进程、终止父进程
    父进程调用 fork()创建子进程然后父进程使用 exit()退出这样做实现了下面几点。
    第一如果该守护进程是作为一条简单地 shell 命令启动那么父进程终止会让 shell 认为这条命令已经执行完毕。
    第二虽然子进程继承了父进程的进程组ID但它有自己独立的进程ID这保证了子进程不是一个进程组的组长进程这是下面将要调用 setsid 函数的先决条件

  2. 子进程调用 setsid 创建会话
    调用 setsid()会使得子进程创建一个新的会话子进程成为新会话的首领进程同样也创建了新的进程组、子进程成为组长进程此时创建的会话将没有控制终端。所以这里调用 setsid 有三个作用

  1. 让子进程摆脱原会话的控制
  2. 让子进程摆脱原进程组的控制
  3. 让子进程摆脱原控制终端的控制
    tips:在调用 fork 函数时子进程继承了父进程的会话、进程组、控制终端等虽然父进程退出了但原先的会话期、进程组、控制终端等并没有改变因此那还不是真正意义上使两者独立开来。setsid 函数能够使子进程完全独立出来从而脱离所有其他进程的控制。
  1. 将工作目录更改为根目录
    子进程是继承了父进程的当前工作目录由于在进程运行中当前目录所在的文件系统是不能卸载的这对以后使用会造成很多的麻烦。因此通常的做法是让“/”作为守护进程的当前目录当然也可以指定其它目录来作为守护进程的工作目录。

  2. 重设文件权限掩码 umask
    文件权限掩码 umask 用于对新建文件的权限位进行屏蔽由于使用 fork() 函数新建的子进程继承了父进程的文件权限掩码这就给子进程使用文件带来了诸多的麻烦。因此把文件权限掩码设置为 0确保子进程有最大操作权限、这样可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是 umask通常的使用方法为 umask(0)。

  3. 关闭不再需要的文件描述符
    子进程继承了父进程的所有文件描述符这些被打开的文件可能永远不会被守护进程此时守护进程指的就是子进程父进程退出、子进程成为守护进程读或写但它们一样消耗系统资源可能导致所在的文件系统无法卸载所以必须关闭这些文件这使得守护进程不再持有从其父进程继承过来的任何文件描述符。

  4. 将文件描述符号为 0、1、2 定位到/dev/null
    将守护进程的标准输入、标准输出以及标准错误重定向到/dev/null这使得守护进程的输出无处显示、也无处从交互式用户那里接收输入。

  5. 其它忽略 SIGCHLD 信号
    处理 SIGCHLD 信号不是必须的但对于某些进程特别是并发服务器进程往往是特别重要的服务器进程在接收到客户端请求时会创建子进程去处理该请求如果子进程结束之后父进程没有去 wait 回收子进程则子进程将成为僵尸进程如果父进程 wait 等待子进程退出将又会增加父进程的负担、也就是增加服务器的负担影响服务器进程的并发性能在 Linux 下可以将 SIGCHLD 信号的处理方式设置为
    SIG_IGN也就是忽略该信号可让内核将僵尸进程转交给 init 进程去处理这样既不会产生僵尸进程、又省去了服务器进程回收子进程所占用的时间。

下面举个守护进程的例子守护进程一般以单例模式运行

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int main(void)
{
 pid_t pid;
 int i;
 /* 创建子进程 */
 pid = fork();
 if (0 > pid) {
 perror("fork error");
 exit(-1);
 }
 else if (0 < pid)//父进程
exit(0); //直接退出
 /*
 *子进程
 */
 /* 1.创建新的会话、脱离控制终端 */
 if (0 > setsid()) {
 perror("setsid error");
 exit(-1);
 }
 /* 2.设置当前工作目录为根目录 */
 if (0 > chdir("/")) {
 perror("chdir error");
 exit(-1);
 }
 /* 3.重设文件权限掩码 umask */
 umask(0);
 /* 4.关闭所有文件描述符 */
 for (i = 0; i < sysconf(_SC_OPEN_MAX); i++)
 close(i);
 /* 5.将文件描述符号为 0、1、2 定位到/dev/null */
 open("/dev/null", O_RDWR);
 dup(0);
 dup(0);
 /* 6.忽略 SIGCHLD 信号 */
 signal(SIGCHLD, SIG_IGN);
 /* 正式进入到守护进程 */
 for ( ; ; ) {
 sleep(1);
 puts("守护进程运行中......");
 }
 exit(0);
}

在这里插入图片描述
从上图可知testApp 进程成为了一个守护进程与控制台脱离当关闭当前控制终端时testApp 进程并不会受到影响依然会正常继续运行而对于普通进程来说终端关闭那么由该终端运行的所有进程都会被强制关闭因为它们处于同一个会话。关于这个问题大家可以自己去测试下对比测试普通进程与守护进程当终端关闭之后是否还在继续运行。
在这里插入图片描述
运行之后没有任何打印信息输出原因在于守护进程已经脱离了控制终端它的打印信息并不会输出显示到终端在代码中已经将标准输入、输出以及错误重定位到了/dev/null/dev/null 是一个黑洞文件自然是看不到输出信息。
使用"ps -ajx"命令查看进程

本文参考正点原子的嵌入式LinuxC应用编程。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux