Python-守护进程deameon

1. 守护进程

守护进程daemon是一种生存周期很长的进程。它们通常在系统引导时启动,在系统关闭时终止。守护进程是没有终端的,它们一直在后台运行。

守护进程deameon的两个好处:

  1. 开机自动启动,不占用终端
  2. 关闭进程,仍然运行

2. 编程规则

如果需要编写进程守护程序,则需要遵循一些基本规则,具体如下:

  1. 调用umask将文件模式创建屏蔽字设置为一个指定值。因为守护进程如果要创建文件,那么该文件必须指定权限,确保文件权限是自己期望的。
  2. 调用fork,然后使父进程exit,这是使得守护进程不关联终端的前提条件。另外,如果守护进程从终端命令行启动,那么父进程exit会使得shell认为该命令执行完毕从而正常返回。
  3. 调用setsid创建新会话,并丢掉控制终端。
  4. 将进程当前工作目录更改为根目录,因为进程可能启用于一个临时挂载的目录,如果进程一直执行,那么挂载目录就无法卸载。
  5. 关闭不再需要的文件描述符,主要防止守护进程误写。
  6. 打开/dev/null 文件,使得进程具有文件描述符0,1,2,这样做是为了预防守护进程调用的第三方接口或者库组件尝试从标准输入输出读写。

以上6点基本上是编写一个守护进程所必须的,也就是说,如果要编写一个严谨的守护进程,那么最好将上述步骤全部囊括。

3. 示例

下面是一个守护进程deameon:

#!/usr/bin/env python
import sys
import os
import time

'''
手动编写一个daemon进程
'''

def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    try:

        # 创建子进程
        pid = os.fork()

        if pid > 0:
            # 父进程先于子进程exit,会使子进程变为孤儿进程,
            # 这样子进程成功被init这个用户级守护进程收养
            sys.exit(0)

    except OSError as err:
        sys.stderr.write('_Fork #1 failed: {0}\n'.format(err))
        sys.exit(1)

    # 从父进程环境脱离
    # decouple from parent environment
    # chdir确认进程不占用任何目录,否则不能umount
    os.chdir("/")
    # 调用umask(0)拥有写任何文件的权限,避免继承自父进程的umask被修改导致自身权限不足
    os.umask(0)
    # setsid调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离
    os.setsid()

    # 第二次fork
    try:
        pid = os.fork()
        if pid > 0:
            # 第二个父进程退出
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #2 failed: {0}\n'.format(err))
        sys.exit(1)

    # 重定向标准文件描述符
    sys.stdout.flush()
    sys.stderr.flush()

    si = open(stdin, 'r')
    so = open(stdout, 'a+')
    se = open(stderr, 'w')

    # dup2函数原子化关闭和复制文件描述符
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

# 每秒显示一个时间戳
def test():
    sys.stdout.write('Daemon started with pid %d\n' % os.getpid())
    while True:
        now = time.strftime("%X", time.localtime())
        sys.stdout.write(f'{time.ctime()}\n')
        sys.stdout.flush()
        time.sleep(1)

if __name__ == "__main__":
    daemonize('/dev/null','/dev/demo/d1.log','/dev/null')
    test()

注:在window下没有一个函数可以实现UNIX下的fork()函数,其原因是历史造成的.对于UNIX来说它一出生就是多用户的系统,所以它的所有进程都共有一个最原始的父进程init.而windows生下来时是个单用户系统(DOS),不存在这样的概念.所以fork这个函数是UNIX下特有的.

运行以上代码,我们在linux系统终端中查看:
在这里插入图片描述
在这里插入图片描述


参考链接:
1.https://www.jianshu.com/p/fbe51e1147af
2.https://blog.csdn.net/autumn20080101/article/details/51168981

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值