什么是守护进程?
守护进程就是linux 中的后台服务进程。它是一个生存周期较长的进程,通常独立于控制终端并且周期地执行某种任务或等待某些待处理的事件。
如何查看守护进程
在终端上执行 ps -axj
- a 表示不仅显示当前用户的进程,还显示其他用户所拥有的进程。
- x 显示没有控制终端的进程状态。
- j 显示与作业相关的信息:会话ID、进程组ID、控制终端以及终端进程组ID
而另一个相关的 命令 ps -efj 也可以查看守护进程。
系统进程依赖于操作系统实现。父进程ID为0的各进程通常是内核进程。它们作为系统引导装入过程的一部分而启动( init 是个例外,它是一个由内核在引导装入时启动的用户层次的命令。)内核进程是特殊的,通常于系统的整个生命周期中。它们以超级用户特权执行,无控制终端,无命令行。
常见的linux内核进程以及它用途:
对于需要在进程上下文执行工作但却不被用户层进程上下文调用的每一个内核组件,通常有它自己的内核守护进程。
- kswapd0 守护进程也称为内存换页守护进程。它支持虚拟内存子系统在经过一段时间后将脏页面慢慢地写入磁盘来回收这些页面。
- flush 守护进程在可用内存达到一个最小阀值时将脏页面冲洗到磁盘。它也定期地将脏页面冲洗回磁盘来减少在系统出现故障时发生数据丢失。
- sync_supers 守护进程定期地将文件系统元数据冲洗至磁盘
- rpcbind 守护进程提供将远程过程调用。
- cron 守护进程在定期安排的日期和时间执行命令。在Linx 中一般都会有 crontab ,按照一定的格式,可以在里面添加你想要定期执行的命令。
守护进程的编写
- 首先要做的是调用 umask 将文件模式创建屏蔽字设置为一个已知值(通常是0)。因为继承而来的文件模式创建屏蔽字可能会限制某些权限。
- 调用 fork,然后使父进程exit。保证子进程不是一个进程组的组长进程。
- 调用 setsid() 创建一个新会话。a: 成为新会话的首进程 b: 成为一个新进程组的组长进程 c: 没有控制终端
- 将当前工作目录更改为根目录。从父进程那继承过来的当前目录可能在一个挂载的文件系统中。
- 关闭不在需要的文件描述符。使用 open_max 函数或 getrlimit 函数来判定最高文件描述符,并关闭直到该值的所有描述符
- 某些守护进程打开 /dev/null 使其具有文件描述符0、1和2,这样,任何一个试图读标准输入、写标准输出或标准错误的库例程都不会产生任何效果。
daemon.c
//
// Created by 陈国威 on 2017/12/10.
//
#include "../apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>
void daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/**
* clear file creation mask;
*/
umask(0); //(1)设置文件模式创建屏蔽字
/*
* Get maximun number of file descriptions
*/
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) //获取文件描述符的最大值
perror("can't get file limit");
/*
* Become a session leader to lose controlling TTY
*/
if ((pid = fork()) < 0) (2)调用 fork,然后使父进程退出
perror("can't fork");
else if (pid != 0)
exit(0); /* 退出父进程 */
setsid(); //(3)创建一个新会话
/*
* Ensure future opens won't allocate controlling TTY
*/
sa.__sigaction_u.__sa_handler = SIG_IGN; // 忽略信号处理程序
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
perror("can't ignore SIGHUP");
if ((pid = fork()) < 0)
perror("can't fork");
else if (pid != 0)
exit(0);
/*
* Change the current working directory to the root so
* we won't prevent file system from being
*/
if (chdir("/") < 0) //(4)将当前工作目录更为根目录
perror("can't change directory to /");
/*
* close all open file discriptors
*/
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for(i = 0; i < rl.rlim_max; i++) //(5)关闭所有文件描述符
close(i);
/*
* Attach file descriptors 0, 1, and 2 to /dev/null
*/
fd0 = open("/dev/null", O_RDWR); //(6)打开/dev/null使其具有文件描述符 0、1、2
fd1 = dup(0);
fd2 = dup(0);
/*
* Initialize the log file
*/
openlog(cmd, LOG_CONS, LOG_DAEMON); //将守护进程的错误记录 后面会有介绍
if (fd0 != 0 || fd1 != 1 || fd2 != 2)
{
syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
}
int main(int arg , char **argv)
{
printf("start daemoni\n");
daemonize(argv[0]);
printf("sleep(10)");
sleep(10);
exit(0);
}
守护进程的出错记录