本文主要参考自:linux系统编程之进程(八):守护进程详解及创建,daemon()使用

一、概述

Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。

守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。

守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

守护进程的名称通常以d结尾,比如sshd、xinetd、crond等。

二、守护进程的创建

首先我们需要理解一些基本概念:

  • 进程组(process group): 一个或多个进程的集合,每个进程都有一个进程组ID,这个ID就是进程组长的进程ID
  • 会话期(session): 一个或多个进程组的集合,每个会话有唯一一个会话首进程(session leader),会话ID为会话首进程ID
  • 控制终端(controlling terminal) :每一个会话可以有一个单独的控制终端,与控制终端连接的会话首进程就是控制进程(controlling process)。 这时候,与当前终端交互的就是前台进程组,其他的都是后台进程组。

创建守护进程的过程中会用到一个关键函数:setsid(),这个函数用于创建一个新的会话期。

给出 setsid() 的 Linux 描述

#include <unistd.h>pid_t setsid(void);DESCRIPTION setsid()  creates a new session if the calling process is not a process group leader.  The calling process is the leader of  the  new  session, the  process group leader of the new process group, and has no control- ling tty.  The process group ID and session ID of the  calling  process are set to the PID of the calling process.  The calling process will be the only process in this new process group and in this new session.RETURN VALUE On success, the (new) session ID of the calling  process  is  returned. On  error,  (pid_t) -1  is  returned,  and errno is set to indicate the error.

进程调用 setsid()函数会:

首先请注意:只有当该进程不是一个进程组长时,才会成功创建一个新的会话期。

(1)摆脱原会话的控制。该进程变成新会话期的首进程

(2)摆脱原进程组。成为一个新进程组的组长

(3)摆脱终端控制。如果在调用 setsid() 前,该进程有控制终端,那么与该终端的联系被解除。 如果该进程是一个进程组的组长,

此函数返回错误。

创建守护进程的的一般步骤:

1、fork()创建子进程,父进程exit()退出

这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

2、在子进程中调用 setsid() 函数创建新的会话

在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

3、再次 fork() 一个子进程并让父进程退出。

现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端,可以通过 fork() 一个子进程,该子进程不是会话首进程,该进程将不能重新打开控制终端。退出父进程。

4、在子进程中调用 chdir() 函数,让根目录 ”/” 成为子进程的工作目录

这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。

5、在子进程中调用 umask() 函数,设置进程的文件权限掩码为0

文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。

6、在子进程中关闭任何不需要的文件描述符

同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。

7、守护进程退出处理

当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

一张简单的图可以完美诠释之前几个步骤:

以下程序是创建一个守护进程,然后利用这个守护进程每隔一分钟向daemon.log文件中写入当前时间,当守护进程收到 SIGQUIT 信号后退出。

#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>static bool flag = true;
void create_daemon();
void handler(int);int main()
{time_t t;int fd;create_daemon();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/mick/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(60);}return 0;
}
void handler(int sig)
{printf("I got a signal %d\nI'm quitting.\n", sig);flag = false;
}
void create_daemon()
{pid_t pid;pid = fork();if(pid == -1){printf("fork error\n");exit(1);}else if(pid){exit(0);}if(-1 == setsid()){printf("setsid error\n");exit(1);}pid = fork();if(pid == -1){printf("fork error\n");exit(1);}else if(pid){exit(0);}chdir("/");int i;for(i = 0; i < 3; ++i){close(i);}umask(0);return;
}

注意守护进程一般需要在 root 权限下运行。

通过

ps -ef | grep 'daemon'

可以看到:

root     26454  2025  0 14:20 ?        00:00:00 ./daemon

并且产生了 daemon.log,里面是这样的时间标签

Thu Dec  8 14:35:11 2016
Thu Dec  8 14:36:11 2016
Thu Dec  8 14:37:11 2016

最后我们想退出守护进程,只需给守护进程发送 SIGQUIT 信号即可

sudo kill -3 26454 

再次使用 ps 会发现进程已经退出。


三、利用库函数 daemon()创建守护进程

其实我们完全可以利用 daemon() 函数创建守护进程,其函数原型:

#include <unistd.h>int daemon(int nochdir, int noclose);DESCRIPTION         The daemon() function is for programs wishing to detach themselvesfrom the controlling terminal and run in the background as systemdaemons.If nochdir is zero, daemon() changes the process's current workingdirectory to the root directory ("/"); otherwise, the current workingdirectory is left unchanged.If noclose is zero, daemon() redirects standard input, standardoutput and standard error to /dev/null; otherwise, no changes aremade to these file descriptors.RETURN VALUE         (This function forks, and if the fork(2) succeeds, the parent calls_exit(2), so that further errors are seen by the child only.)  Onsuccess daemon() returns zero.  If an error occurs, daemon() returns-1 and sets errno to any of the errors specified for the fork(2) andsetsid(2).

现在让我们使用 daemon() 函数来再次创建一次守护进程,其实就是用 daemon() 替换掉我们自己的 create_daemon():

#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>static bool flag = true;
void handler(int);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/mick/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(60);}return 0;
}
void handler(int sig)
{printf("I got a signal %d\nI'm quitting.\n", sig);flag = false;
}

没问题,和之前一样。

【Linux编程】守护进程(daemon)详解与创建相关推荐

  1. linux查看守护进程格式,详解Linux中的守护进程

    一.什么是守护进程 Linux系统启动时会启动很多系统服务进程,这些系统服 务进程没有控制终端,不能直接和用户交互.其它进程都是在用户登录或运行程序时创建,在运⾏结束或⽤户注销时终止,但系统服务进程不 ...

  2. 小何讲进程: 编写Linux守护进程方法详解

    守护进程概述 守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程. 它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些事件的发生. 守护进程常常在 ...

  3. Linux下守护进程(daemon)的实现

    文章目录 守护进程 守护进程的创建 守护进程的实现 守护进程 守护进程是一种特殊的孤儿进程,父进程是一号init进程,运行在后台,与终端和登陆会话脱离关系,不受影响. 守护进程通常系统引导的时候启动, ...

  4. 【Linux操作系统】进程概念详解

    目录 冯诺依曼体系结构 操作系统OS 概念 为什么要有OS OS的定位 如何理解管理 系统调用和库函数概念 进程! 概念 PCB 进程VS数据 task_ struct内容分类 通过系统调用创建进程- ...

  5. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    linux系统编程之进程(八):守护进程详解及创建,daemon()使用 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等 ...

  6. Linux系统守护进程详解

    文中有不对或者有不清楚的地方,请大家告诉我,谢谢!   Linux系统守护进程详解 不要关闭下面这几个服务: acpid, haldaemon, messagebus, klogd, network, ...

  7. another mysql daemon,[守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  8. mysql进程daemon_守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而 且提供某种服务,不是对整 ...

  9. linux Shell(脚本)编程入门实例讲解详解

    linux Shell(脚本)编程入门实例讲解详解 为什么要进行shell编程 在Linux系统中,虽然有各种各样的图形化接口工具,但是sell仍然是一个非常灵活的工具.Shell不仅仅是命令的收集, ...

最新文章

  1. jQueryEasyUI 的入门
  2. java实现的18位×××格式验证算法
  3. 自动注册 IIS6 的 MIME 类型
  4. avalov+require实现tab栏
  5. 硬核!尽量避免 BUG 手法
  6. 微服务部署:蓝绿部署、滚动部署、灰度发布、金丝雀发布
  7. conn.execute参数
  8. 2019 好笔友-见字如面
  9. ASP.NET Compilation and Deployment
  10. iOS --- DIY文件名批量修改
  11. SDK 和 API 有什么区别
  12. c语言 判断一个图是否全连通_基于云平台的全链路大规模网络连通性检测系统详解...
  13. vsc写vue生成基本代码快捷键_基于vue2.X的webpack基本配置,教你手动撸一个webpack4的配置...
  14. php 5.6.29,源码编译安装PHP 5.6.29
  15. 从学校到职场的路有多远
  16. 敏捷开发中的MoSCoW优先级排序方法
  17. 企业信息化建设(Enterprise Informationization Construction)
  18. c语言存储转置矩阵,C语言实现矩阵转置(附带源码)
  19. 阿里云短信发送 错误 请检查模板内容与模板参数是否匹配
  20. 【自学与引导】 大数据技术 技能课程总结篇

热门文章

  1. word/ppt中如何插入11以上带圆圈的数字序号
  2. Google Android软件架构
  3. redis也可以根据经纬度查询附近的元素以及计算两个经纬度的距离???
  4. elementUI checkbox选中回显操作注意事项
  5. 最近国外LEAD收款情况
  6. 使用scoop下载Annie时提示couldnt find manifest for annie
  7. matlab cftool光滑曲线导出为什么就不光滑了_快速傅里叶变换(FFT)中为什么要“补零”?...
  8. 利用Webrtc-streamer展示rstp视频流
  9. 密码学---数字签名和认证协议---数字签名的基本概念
  10. 中职计算机应用专业课堂教学,新时期中职计算机专业课堂教学的创新应用