守护进程
概念
守护进程(daemon),见其名知其义,是默默守护在后台的进行中的程序。
守护进程没有终端,所以当终端建立的session结束时,不会结束掉守护进程。
创建
那么如何来创建守护进程,简单的说,就是在终端创建一个不拥有终端的进程。
通过终端建立一个session的时候,建立与控制终端连接的session首进程被称为控制进程,这个session拥有前台进程组和后台进程组,当终端键入中断/退出键时,会把中断/退出信号发到所有的前台进程组的所有进程。所以创建守护进程,就要把session创建的进程变为后台进程。
首先说一下进程组的概念,每个进程都属于一个进程组,进程组的组号=进程组组长的PID,一个进程只能为自己或者子进程设置进程组id。
创建守护进程的操作
-
创建子进程,父进程退出
-
创建新session
-
改变进程工作目录为根目录
-
设置umask
-
关闭文件描述符
-
把守护进程标准输入标准输入标准错误指向/dev/null
这些步骤中,最关键的是1,2步骤,其他都是非必要步骤,做了更好不做也能成功。
详细讲解下每个步骤:
1. 创建子进程,父进程退出
如果守护进程是shell命令启动的,父进程的终止会让shell认为这条命令已经执行完毕。fork出的子进程是新的进程ID,保证了子进程不是一个进程组的组长进程(PID!=PGID),为调用setsid提供了条件(必须非进程组组长调用,否则报错)。
2.创建session
1步骤的子进程调用setsid创建一个新会话,由于会话对终端具有独占性,setsid之后,该进程脱离了原来的会话和终端,这样该进程变为了新会话的首进程,
变成了新进程组的组长,没有控制终端
3.改变进程工作目录为根目录
当前工作目录可能在一个挂载的文件系统中,会影响到其他用户卸载。
4. 设置umask
可能有人不知道umask是什么,比如我。
先介绍下umask。
umask值用于设置用户在创建文件时的默认权限,当我们在系统中创建目录或文件时,目录或文件所具有的默认权限就是由umask值决定的。
对于root用户,他的umask值是022。当root用户创建目录时,默认的权限就是用最大权限777去掉相应位置的umask值权限,即对于所有者不必去掉任何权限,对于所属组要去掉w权限,对于其他用户也要去掉w权限,所以目录的默认权限就是755;当root用户创建文件时,默认的权限则是用最大权限666去掉相应位置的umask值,即文件的默认权限是644。
通常守护进程设置为umask(0),因为fork出来的子进程会继承父进程的umask值,若没有读写权限就会很麻烦。
5.关闭文件描述符
子进程可能有从父进程继承一些已经打开了的文件,这些打开的文件可能永远不会被守护进程读写,并且守护进程不拥有终端,终端输入的字符也不会达到守护进程,所以文件描述符失去了意义,为了避免浪费资源,应该被关闭。
6.把标准输入标准输入标准错误指向/dev/null
守护进程没有终端,用户也不希望在终端看到守护进程的输出,也不希望自己的输入被守护进程读取。这样设置就不会对守护进程产生效果了。
创建代码
#include "apue.h" /*这个头文件unix高级编程官网可下载*/
#include <syslog.h>
#inclue <fcntl.h>
#include <sys/resource.h>
void daemonize(const char *cmd)
{
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimt rl;
struct sigaction sa;
/*设置umask*/
umask(0);
/**/
if (getrlimt(RLIMIT_NOFILE, &rl) < 0)
err_quit("%s: can't get file limit", cmd);
/*变成一个会话的首进程,失去控制终端*/
if ((pid == fork()) < 0)
err_quit("%s: can't fork", cmd);
else if (pid !=0) /*父进程*/
exit(0);
setsid();
/*改变工作目录*/
if (chdir("/") < 0)
err_quit("%s: can't change directory to /", cmd);
/*关闭文件描述符*/
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i=0; i<rl.rlim_max; i++)
close(i);
/*把标准输入输出错误链接到/dev/null*/
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
/*初始化log文件*/
openlog(cmd, LOG_CONS, LOG_DAMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2){
syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
}
用python实现守护进程
import os
import sys
import time
res = os.fork()
if res != 0:
print("子进程ID:", res)
sys.exit(0)
elif res == 0:
print("子进程中")
os.setsid()
f = open("/dev/null", "w")
sys.stdout = f
sys.stderr = f
os.umask(0)
os.chdir("/")
while True:
time.sleep(1)
else:
raise Exception("失败")