Python 创建守护进程的示例


Posted in Python onSeptember 29, 2020

考虑如下场景:你编写了一个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)

概括一下守护进程的编写步骤:

  1. fork出子进程,退出父进程
  2. 子进程变更工作目录(chdir)、文件权限掩码(umask)、进程组和会话组(setsid)
  3. 子进程fork孙子进程,退出子进程
  4. 孙子进程刷新缓冲,重定向标准输入/输出/错误(一般到/dev/null,意即丢弃)
  5. (可选)pid写入文件

理解几个要点

为什么要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)。

守护进程与后台进程

通过&符号,可以把命令放到后台执行。它与守护进程是不同的:

  1. 守护进程与终端无关,是被init进程收养的孤儿进程;而后台进程的父进程是终端,仍然可以在终端打印
  2. 守护进程在关闭终端时依然坚挺;而后台进程会随用户退出而停止,除非加上nohup
  3. 守护进程改变了会话、进程组、工作目录和文件描述符,后台进程直接继承父进程(shell)的

换句话说:守护进程就是默默地奋斗打拼的有为青年,而后台进程是默默继承老爸资产的富二代。

以上就是Python 创建守护进程的示例的详细内容,更多关于Python 创建守护进程的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
使用PYTHON接收多播数据的代码
Mar 01 Python
Python中的tuple元组详细介绍
Feb 02 Python
Python作用域用法实例详解
Mar 15 Python
Python中__init__.py文件的作用详解
Sep 18 Python
python实现RabbitMQ的消息队列的示例代码
Nov 08 Python
详解Python数据可视化编程 - 词云生成并保存(jieba+WordCloud)
Mar 26 Python
python中aioysql(异步操作MySQL)的方法
Apr 11 Python
Python3 执行系统命令并获取实时回显功能
Jul 09 Python
python过滤中英文标点符号的实例代码
Jul 15 Python
django框架用户权限中的session缓存到redis中的方法
Aug 06 Python
Django-xadmin+rule对象级权限的实现方式
Mar 30 Python
python判断变量是否为列表的方法
Sep 17 Python
Python 解析xml文件的示例
Sep 29 #Python
Python 字典一个键对应多个值的方法
Sep 29 #Python
python 获取字典特定值对应的键的实现
Sep 29 #Python
Python3 pyecharts生成Html文件柱状图及折线图代码实例
Sep 29 #Python
Python爬取微信小程序通用方法代码实例详解
Sep 29 #Python
详解如何修改python中字典的键和值
Sep 29 #Python
提高python代码运行效率的一些建议
Sep 29 #Python
You might like
什么是短波收听SWL
2021/03/01 无线电
Yii PHP Framework实用入门教程(详细介绍)
2013/06/18 PHP
PHP date()函数警告: It is not safe to rely on the system解决方法
2014/08/20 PHP
php实现递归抓取网页类实例
2015/04/03 PHP
php使用curl实现ftp文件下载功能
2017/05/16 PHP
PHP使用file_get_contents发送http请求功能简单示例
2018/04/29 PHP
在网页中使用document.write时遭遇的奇怪问题
2010/08/24 Javascript
javascript实现上传图片并预览的效果实现代码
2011/04/11 Javascript
基于jquery的滚动鼠标放大缩小图片效果
2011/10/27 Javascript
页面回到顶部的三种实现(锚标记,js)
2012/10/01 Javascript
js限制textarea每行输入字符串长度的代码
2012/10/31 Javascript
JQuery实现鼠标滑过显示导航下拉列表
2013/09/12 Javascript
JQuery datepicker 用法详解
2015/12/25 Javascript
全面了解JS中的匿名函数
2016/06/29 Javascript
浅谈js常用内置方法和对象
2016/09/24 Javascript
vue router 用户登陆功能的实例代码
2019/04/24 Javascript
JS异步处理的进化史深入讲解
2019/08/25 Javascript
微信小程序自定义纯净模态框(弹出框)的实例代码
2020/03/09 Javascript
JS实现长图上下滚动效果
2020/03/19 Javascript
vue实现匀速轮播效果
2020/06/29 Javascript
vue内置组件keep-alive事件动态缓存实例
2020/10/30 Javascript
python删除文件夹下相同文件和无法打开的图片
2019/07/16 Python
在Pytorch中计算自己模型的FLOPs方式
2019/12/30 Python
Python 字节流,字符串,十六进制相互转换实例(binascii,bytes)
2020/05/11 Python
python爬虫把url链接编码成gbk2312格式过程解析
2020/06/08 Python
HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线
2015/01/01 HTML / CSS
KEEN美国官网:美国人气户外休闲鞋品牌
2021/03/09 全球购物
意大利和国际奢侈品牌购物网站:Suitnegozi.com
2021/01/15 全球购物
英文简历中的自荐信范文
2013/12/14 职场文书
超市商业计划书
2014/05/04 职场文书
党支部书记四风问题整改措施
2014/09/24 职场文书
党员对照检查材料整改措施思想汇报
2014/09/26 职场文书
2014年党风建设工作总结
2014/11/19 职场文书
简历自我评价模板
2015/03/11 职场文书
社会心理学学习心得体会
2016/01/22 职场文书
人生一定要学会的三样东西:放下、忘记、珍惜
2019/08/21 职场文书