守护进程

守护进程

概念

守护进程(daemon),见其名知其义,是默默守护在后台的进行中的程序。

守护进程没有终端,所以当终端建立的session结束时,不会结束掉守护进程。

创建

那么如何来创建守护进程,简单的说,就是在终端创建一个不拥有终端的进程。

通过终端建立一个session的时候,建立与控制终端连接的session首进程被称为控制进程,这个session拥有前台进程组和后台进程组,当终端键入中断/退出键时,会把中断/退出信号发到所有的前台进程组的所有进程。所以创建守护进程,就要把session创建的进程变为后台进程。

首先说一下进程组的概念,每个进程都属于一个进程组,进程组的组号=进程组组长的PID,一个进程只能为自己或者子进程设置进程组id。

创建守护进程的操作

  1. 创建子进程,父进程退出

  2. 创建新session

  3. 改变进程工作目录为根目录

  4. 设置umask

  5. 关闭文件描述符

  6. 把守护进程标准输入标准输入标准错误指向/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("失败")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值