C语言实现守护进程

C语言实现守护进程

守护进程(daemon)是在后台运行的一种特殊进程,它没有控制终端,通常用于在系统启动时启动一些需要常驻后台的服务程序。

Linux的大多数服务器就是用守护进程的方式实现的。如web服务器进程http等。守护进程在后台运行,类似于Windows中的系统服务。

查看系统中的进程:

ps axj

参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,
参数x表示不仅列有控制终端的进程,也列出所无控制终端的进程,
参数j表示列出与作业控制相关的信息。

凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,

因此没有程序文件名和命令行, 通常采用以k开头的名字,表示Kernel。守护进程通 常采用以d结尾的名字,表示Daemon。

守护进程的原理是通过fork()创建一个子进程,让子进程脱离控制终端,设置新的会话组,关闭标准输入输出和错误输出,最后通过umask()函数来设置文件的权限掩码,使得守护进程在运行过程中不会被其他用户所访问。

daemon()守护进程函数

原型

int daemon(int nochdir,int noclose)

其中,nochdir参数用于指定是否改变工作目录,如果给它传递0,则工作目录将被设置为“/”(根目录),否则继续使用当前工作目录。

noclose参数为0时,标准输入、标准输出和标准错误输出都被重定向到/dev/null文件,否则依然使用原来的设备。

该函数成功时返回0,失败返回-1,并设置errno。
例如:
daemon(0,0):nochdir为0 改为根目录,noclose为0 关闭所有文件描述符;

将当前工作目录更改为根目录的作用:
防止当前目录有一个目录被删除,导致守护进程无效。
使用fork()创建的子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。

因此通常的做法是让“/”作为守护进程的当前目录,当然也可以指定其他的别的目录来作为守护进程的工作目录。

以下是用C语言实现守护进程的完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void daemonize()
{
  pid_t pid;

  /* Step 1: Create a child process */
  pid = fork();

  if(pid < 0) /* Error occurred */
  {
    perror("fork");
    exit(1);
  }

  if(pid > 0) /* Parent process, exit */
  {
    printf("Parent process: %d\n", pid);
    exit(0);
  }

  /* Step 2: Create a new session */
  if(setsid() == -1)
  {
    perror("setsid");
    exit(1);
  }

  /* Step 3: Change working directory */
  if(chdir("/") == -1)
  {
    perror("chdir");
    exit(1);
  }

  /* Step 4: Set file permissions */
  umask(0);

  /* Step 5: Close all open file descriptors */
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
}

int main()
{
  daemonize();

  /* Do daemon work here */
  while(1)
  {
    /* Do something */
  }

  return 0;
}

在上面的代码中,daemonize()函数用于创建守护进程。首先通过fork()函数创建一个子进程,然后让子进程调用setsid()函数创建一个新的会话组,使得守护进程与原来的终端完全隔离。接着调用chdir()函数将当前工作目录设置为根目录,防止守护进程占用当前工作目录的文件系统导致该文件系统无法卸载。调用umask()函数设置文件创建权限掩码,确保守护进程创建的文件权限不会被其他用户访问。最后,关闭所有的文件描述符,防止程序在后台运行时产生意料之外的输出。

在调用了daemonize()函数之后,守护进程就开始后台运行了。通常情况下,守护进程会执行一些长期运行的任务,如网络连接、消息队列、数据备份等等。

该文章会更新,欢迎大家批评指正。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器