Board logo

标题: 那些永不消逝的进程-4 [打印本页]

作者: look_w    时间: 2017-12-19 20:07     标题: 那些永不消逝的进程-4

永不消逝的进程 v2:总而言之,有了上一章的理论铺垫,笔者将清单 5 中的例程进化了一次,如清单 7 所示:
清单 7 永不消逝的进程 v2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#! /usr/bin/python

import time
import sys
import os
import logging


def child_process():
    logging.info("child process's pid: %d" % os.getpid())
    while (1):
        logging.info("child's still alive.")
        time.sleep(1)


def fork_and_exit_parent_proc():
    # 因为 multiprocessing.Process 的既定设计是只在子进程中运行 target 参数所指向的函数对象
     # 因此这里我们必须回归传统的 fork
    pid = os.fork()
    if pid > 0:
        os._exit(0)


def become_daemon(target):
    # 1. 第一次 fork
    fork_and_exit_parent_proc()
    # 2. 创建新会话
    os.setsid()
    # 3. 第二次 fork
    fork_and_exit_parent_proc()
    # 4. 将工作目录切换至 '/'
    os.chdir('/')
    # 5. 重定向标准输入、输出、错误至/dev/null
    fd = os.open(os.devnull, os.O_RDWR)
    os.dup2(fd, sys.stdin.fileno())
    os.dup2(fd, sys.stdout.fileno())
    os.dup2(fd, sys.stderr.fileno())
    # 6. 因为标准输出不可用,这里笔者又额外定义了一个 log 文件以接收守护进程的输出
    logging.basicConfig(filename='/var/log/mylog.log', level=logging.INFO)

    target()


def main():
    becomeDaemon(target=child_process)

if __name__ == '__main__':
    main()




在清单 7 中,笔者并未直接调用 daemon(),这主要是因为 python 的标准库中并未包含对 daemon()的直接封装(其实 python                中也提供了其他方案,笔者下文中会有提及)。此外,由于上述实现中有很多调用都需要系统管理员权限,因此必须要以 root 或者 sudoer 的身份才可以执行。
读到这里让我们再回到上一章末尾处的思考:服务级的守护进程纵有千般好,毕竟需要用到系统管理员级的权限;而屏蔽 SIGHUP                纵有千般不是,一个的普通用户权限即可驱动——故而技术无贵贱,不同技术适用于不同场合而已。
最后,除去上述步骤之外,还有一些上述代码中并未实现,但对于守护进程大有裨益的工作,笔者也罗列如下:
总而言之守护进程的实现要考虑到非常多的因素,毕竟你实现的是一个长时间在操作系统的后台蹦跶的程序,哪怕有一点点小的要素没有考虑到都会导致系统运行效率的降低、死机甚至被居心叵测的黑客实施攻击。虽然                daemon()这样的通用 API 一定程度上减轻了我们的工作量,但是前面还是会有很多额外的坑在等待着我们,所谓路漫漫其修远兮……
回到                python现在让我们再回到本文最开始的地方:笔者最初的目的其实只是实现一个通用且稳定的守护进程,但是现在,看看清单 5 中简陋的的 v1 版和清单 7 中偷工减料的 v2                版,不禁仰声长叹:难道就没有一个能让人乐得逍遥且又面面俱到的 v3 版么?
Python 的标准库中并没有封装 glibc 中的 daemon()函数,这个笔者在前文有提到过,但这不代表社区中没人考虑过这个问题:PEP-3143就详细探讨了一个守护进程库的解决方案。
PEP-3143 的设计非常简明扼要:一个 DaemonContext 类就可以基本可以提供开发者们需要的一切,其主要接口和属性定义如下表所示:
表 1 DaemonContext                    类的定义一切看起来都挺美好的,唯一的缺陷是:这个 PEP 被 defer 了,直到今天也没有进入标准库。不过不要紧,因为这个 PEP 还有一个参考实现                    python-daemon,在pip中很容易找到源码。
终章:永不消逝的进程 v3凭借 python-daemon,我们的永不消逝的进程终又可以再进化一层,笔者将修改后的源码列于清单 8 之中,作为本章的结尾以飨读者:
清单 8 永不消逝的进程 v3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#! /usr/bin/python

import time
import os
import logging
import daemon


def child_process():
    logging.info("child process's pid: %d" % os.getpid())
    while (1):
        logging.info("child's still alive.")
        time.sleep(1)


def main():
    # DaemonContext 实现了__enter__() 和__exit__(),因此我们可以一句话搞定整个 daemon context
    with daemon.DaemonContext():
        # daemon 目前不支持 log,所以这部分工作只能我们手动初始化
        logging.basicConfig(filename='/var/log/mylog.log', level=logging.INFO)
        child_process()

if __name__ == '__main__':
    main()




结束语后台守护进程是 Linux/Unix 系统中非常重要的"地下工作者"。本文从 Linux/Unix                的进程组和会话的机制入手,详细的介绍了基于这些机制之上的两种截然不同的实现守护进程的手法。在深入解读这些奇淫巧技的同时,笔者也更希望读完本文的朋友们能够触类旁通,对                Linux/Unix 系统的进程间关系能有更深一层的认识。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0