护进程:通常被定义为一个后台进程,而且它不属于任何一个终端会话 (terminal session)。许多系统服务由守护程序实施;如网络服务,打印等。 下面这篇文章给大家分享了 Python 是如何实现守护进程的方法示例, 需要的朋友可以参考借鉴。
Python 是一种面向对象、解释型计算机程序设计语言,由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。Python 语法简洁而清晰,具有丰富和强大的类库。它常被昵称为胶水语言,它能够把用其他语言制作的各种模块(尤其是 C/C++)很轻松地联结在一起。
场景设置:
你编写了一个 python 服务程序,并且在命令行下启动,而你的命令行会话又被终端所控制,python 服务成了终端程序的一个子进程。因此如果你关闭了终端,这个命令行程序也会随之关闭。
要使你的 python 服务不受终端影响而常驻系统,就需要将它变成守护进程。
守护进程就是 Daemon 程序,是一种在系统后台执行的程序,它独立于控制终端并且执行一些周期任务或触发事件,通常被命名为 "d" 字母结尾,如常见的 httpd、syslogd、systemd 和 dockerd 等。
代码实现
python 可以很简洁地实现守护进程,下面先给出代码和相应注释:
- # coding=utf8
- import os
- import sys
- import atexit
- def daemonize(pid_file=None):
- """
- 创建守护进程
- :param pid_file: 保存进程id的文件
- :return:
- """
- # 从父进程fork一个子进程出来
- pid = os.fork()
- # 子进程的pid一定为0,父进程大于0
- if pid:
- # 退出父进程,sys.exit()方法比os._exit()方法会多执行一些刷新缓冲工作
- sys.exit(0)
- # 子进程默认继承父进程的工作目录,最好是变更到根目录,否则回影响文件系统的卸载
- os.chdir('/')
- # 子进程默认继承父进程的umask(文件权限掩码),重设为0(完全控制),以免影响程序读写文件
- os.umask(0)
- # 让子进程成为新的会话组长和进程组长
- os.setsid()
- # 注意了,这里是第2次fork,也就是子进程的子进程,我们把它叫为孙子进程
- _pid = os.fork()
- if _pid:
- # 退出子进程
- sys.exit(0)
- # 此时,孙子进程已经是守护进程了,接下来重定向标准输入、输出、错误的描述符(是重定向而不是关闭, 这样可以避免程序在 print 的时候出错)
- # 刷新缓冲区先,小心使得万年船
- sys.stdout.flush()
- sys.stderr.flush()
- # dup2函数原子化地关闭和复制文件描述符,重定向到/dev/nul,即丢弃所有输入输出
- with open('/dev/null') as read_null, open('/dev/null', 'w') as write_null:
- os.dup2(read_null.fileno(), sys.stdin.fileno())
- os.dup2(write_null.fileno(), sys.stdout.fileno())
- os.dup2(write_null.fileno(), sys.stderr.fileno())
- # 写入pid文件
- if pid_file:
- with open(pid_file, 'w+') as f:
- f.write(str(os.getpid()))
- # 注册退出函数,进程异常退出时移除pid文件
- atexit.register(os.remove, pid_file)
概括一下守护进程的编写步骤:
理解几个要点
为什么要 fork 两次
第一次 fork,是为了脱离终端控制的魔爪。父进程之所以退出,是因为终端敲击键盘、或者关闭时给它发送了信号;而 fork 出来的子进程,在父进程自杀后成为孤儿进程,进而被操作系统的 init 进程接管,因此脱离终端控制。
所以其实,第二次 fork 并不是必须的(很多开源项目里的代码就没有 fork 两次)。只不过出于谨慎考虑,防止进程再次打开一个控制终端。因为子进程现在是会话组长了(对话期的首次进程),有能力打开控制终端,再 fork 一次,孙子进程就不能打开控制终端了。
文件描述符
Linux 是 "一切皆文件",文件描述符是内核为已打开的文件所创建的索引,通常是非负整数。进程通过文件描述符执行 IO 操作。
默认情况下,0 代表标准输入,1 代表标准输出,2 代表标准错误。
umask 权限掩码
我们知道,在 Linux 中,任何一个文件都有读(read)、写(write)和执行(execute)的三种使用权限。其中,读的权限用数字 4 代表,写权限是 2,执行权限是 1。命令 ls -l 可以查看文件权限,r/w/x 分别表示具有读 / 写 / 执行权限。
任何文件,也都有用户(User), 用户组(Group), 其他组(Others)三种身份权限。一般用 3 个数字表示文件权限,例如 754:
7,是 User 权限,即文件拥有者权限
5,是 Group 权限,拥有者所在用户组的组员所具有的权限
4,是 Others 权限,即其他组用户的权限啦
而 umask 是为了控制默认权限,防止新建文件或文件夹具有全权。
系统一般默认为 022(使用命令 umask 查看),表示默认创建文件的权限是 644,文件夹是 755。你应该可以看出它们的规律,就是文件权限和 umask 的相加结果为 666(笑),文件夹权限和 umask 的相加结果为 777。
进程组
每个进程都属于一个进程组(PG,Process Group),进程组可以包含多个进程。
进程组有一个进程组长(Leader),进程组长的 ID(PID, Process ID)就作为整个进程组的 ID(PGID,Process Groupd ID)。
会话组
登陆终端时,就会创造一个会话,多个进程组可以包含在一个会话中。而创建会话的进程,就是会话组长。
已经是会话组长的进程,不可以再调用 setsid() 方法创建会话。因此,上面代码中,子进程可以调用 setsid(),而父进程不能,因为它本身就是会话组长。
另外,sh(Bourne Shell)不支持会话机制,因为会话机制需要 shell 支持工作控制(Job Control)。
守护进程与后台进程
通过 & 符号,可以把命令放到后台执行。它与守护进程是不同的:
换句话说:守护进程就是默默地奋斗打拼的有为青年,而后台进程是默默继承老爸资产的富二代。
总结
来源: http://www.phperz.com/article/17/0321/324821.html