Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。如vsftpd 、httpd、 sshd、xinetd、mysql服务器,守护进程本质上是一个后台系统服务器(一直在后台运行)。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
创建守护进程模型
- 创建子进程,父进程退出,守护进程中所有工作在子进程中进完成;
- 在子进程中创建新会话,setsid()函数使子进程完全独立出来,脱离终端;
- 改变子进程当前目录为根目录,其它目录也可以,只要不是可卸载的文件系统,如:/mnt下的目录,/mnt下的目录都为挂载目录,一旦这些文件系统突然被拔出,则进程工作目录丢失,进程无法执行;使用chdir()函数更改目录;
- 重设文件权限掩码:umask()函数。防止继承的文件创建屏蔽字umask(继承父进程的)拒绝某些权限,增加守护进程灵活性,一般设置为0002,而创建时文件权限设置为0644;
- 关闭文件描述符,继承的打开文件不会用到,浪费系统资源,无法卸载,一般关闭文件描述符0、1、2,因为守护进程独立终端,不需要交互。通常情况下,不直接将其关闭,而是将其全部重定向到/dev/null文件,使用dup2函数;
- 开始执行守护进程核心工作,即用户自定义部分,一般是一个无限循环中进行定义;
- 守护进程退出处理程序模型 。一般情况下不需要退出,除非有特殊情况。若要退出,可以在循环体内部注册一个信号捕捉函数,退出时向进程发送信号即可。要求捕捉函数能够实现:将守护进程打开的文件都关闭掉;创建的缓冲区都释放掉;占用的锁都释放掉;最后自杀(kill、abort函数等)。
注意:守护进程不受用户登陆或者注销的影响,其一直在后台运行;但是,如果机器关机或重启,则守护进程将会停止运行。如果需要让守护进程在重启后自动运行,可以在用户的shell解释器配置文件中进行配置(.bashrc文件,当前用户的shell配置文件,位于用户家目录),shell解释器每次启动时,该配置文件就会被执行,从而向配置文件中写入的守护进程也就会执行,让守护进程启动。注意,配置文件.bashrc每次配置完成后,需要执行以下该文件:. .bashrc 或者 重启终端,配置才能生效。配置文件时,直接vim打开文件,在末位加上: ./daemon即可,即执行该守护进程。
总结:1. 创建子进程 fork;2. 子进程创建新会话 setsid();3.改变进程的工作目录 chdir();4. 指定文件掩码 umask();5. 将0/1/2重定向 /dev/null dup2();6. 守护进程主逻辑;7. 退出
//守护进程模型
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
void daemonize(void)
{
pid_t pid, ret;
/*
* 成为一个新会话的首进程,失去控制终端
* */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
} else if (pid != 0) /* parent */
exit(0);
ret = setsid();
if (ret == -1) {
perror("setsid");
exit(1);
}
/*
* 改变当前工作目录到/目录下.
* */
if (chdir("/") < 0) {
perror("chdir");
exit(1);
}
/* 设置umask为0 */
umask(0002);
/*
* 重定向0,1,2文件描述符到 /dev/null,因为已经失去控制终端,再操作0,1,2没有意义.
* */
close(STDIN_FILENO);
open("/dev/null", O_RDWR);
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
}
int main(void)
{
daemonize();
while(1); /* 在此循环中可以实现守护进程的核心工作 */
return 0;
}
[root@localhost 01_session_daemon_test]# ./mydaemond
[root@localhost 01_session_daemon_test]# ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 22398 22398 22398 ? -1 Rs 0 0:16 ./mydaemond
可以看见,该进程TTY为 ?,即无终端,状态为Rs。