python使用fork实现守护进程的方法

os模块中的fork方法可以创建一个子进程。相当于克隆了父进程

os.fork()

子进程运行时,os.fork方法会返回0;

而父进程运行时,os.fork方法会返回子进程的PID号。

所以可以使用PID来区分两个进程:

 #!/usr/bin/env python
 #coding=utf8

 from time import sleep
 import os

 try:
 pid = os.fork()
 except OSError, e:
 pass

 sleep(30)

运行代码,查看进程:

[root@localhost ~]# python test2.py &

[1] 2464

[root@localhost ~]# ps -l

F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD

4 S 0 2379 2377 0 80 0 - 28879 wait pts/1 00:00:00 bash

0 S 0 2464 2379 0 80 0 - 31318 poll_s pts/1 00:00:00 python

1 S 0 2465 2464 0 80 0 - 31318 poll_s pts/1 00:00:00 python

0 R 0 2466 2379 0 80 0 - 37227 - pts/1 00:00:00 ps​

可以看出第二条python进程就是第一条的子进程。

如刚刚所说os.fork()方法区分子进程和父进程

#-*- coding:utf-8 -*-

from time import sleep
import os

print('start+++++++++++++')
#创建子进程之前声明的变量
source = 10
try:
    pid = os.fork()
    print('pid=',pid)
    if pid == 0: #子进程
        print("this is child process.")
        source = source - 1 #在子进程中source减1
    else: #父进程
        print("this is parent process." )
    print(source)
except (OSError,e):
    pass
print('END---------------')
  

面代码中,在子进程创建前,声明了一个变量source,然后在子进程中减1,最后打印出source的值,显然父进程打印出来的值应该为10,子进程打印出来的值应该为9。

[root@localhost ~]# python test3.py
start+++++++++++++
pid= 2550
this is parent process.
10
END---------------
pid= 0
this is child process.
9
END---------------​
  

简单守护进程例子:

def main():
  ''' 程序要执行的逻辑代码 '''
  pass

# 创建守护进程函数
def createDaemon():
  ''' 第一块(创建第一个子进程) '''
  # fork 第一个子进程(如果fork成功,父进程自杀,只留下第一个子进程继续向下运行)
  try:
    if os.fork() > 0:
      sys.exit(0)
  except OSError, error:
    print '(fork第一个子进程失败)fork #1 failed: %d (%s)' % (error.errno, error.strerror)
    sys.exit(1)
  ''' 第一块结束 '''

  ###### 第一个进程创建成功后,它的ppid = 1,已是一个守护里程了,但有些功能上还是有被限制。
  ###### 所以下面再来创建一个子进程。第二次创建的子进程限制就没那多了,有可能没有,所以最安全。
  ###### 下面来创建第二个子进程。 

  os.chdir('/') # 把第一个子进程的工作目录切换到 / (根目录)
  os.setsid() # 第一个子进程取得程序的权限
  os.umask(0) # 第一个子进程取得工作目录的所有操作(目录的rwx)

  ''' 第二块(创建第二个子进程) '''
  # fork 第二个子进程(如果fork成功,第一个子进程自杀,只留下新创建的第二个子进程)
  try:
    pid = os.fork()
    if pid > 0:
      print 'Daemon PID %d' % pid
      sys.exit(0)
  except OSError, error:
    print '(fork第二个子进程失败)fork #2 failed: %d (%s)' % (error.errno, error.strerror)
    sys.exit(1)
  ''' 第二块结束 '''

  ####### 通过上面两个 try 语句块,只留下了第二个子进程在运行了。这时第二个子进程的ppid=1。
  ####### 创建的第二个子进程,可以说是一个不受限的守护进程了。

  # 重定向标准IO(因为只有第二个子进程在运行了,所以也就是指定整个程序的输入、输出、错误流)

  # sys.stdout.flush() # 清除程序运行空间的输出流
  # sys.stderr.flush() # 清除程序运行空间的错误流

  # inputS = file("/dev/null", 'r')  # 定义一个 inputS 文件对象
  # outputS = file("/dev/null", 'a+') # 定义一个 outputS 文件对象
  # errorS = file("/dev/null", 'a+', 0) # 定义一个 errorS 文件对象

  # os.dup2(inputS.fileno(), sys.stdin.fileno()) # 把程序的输入流重定向到上面定义的 inputS 文件对象上。
  # os.dup2(so.fileno(), sys.stdout.fileno()) # 把程序的 输出流 重定向到上面定义的 outputS 文件对象上。
  # os.dup2(se.fileno(), sys.stderr.fileno()) # 把程序的 错误流 重定向到上面定义的 errorS 文件对象上。

  main() # main函数为真正程序逻辑代码

if __name__ == "__main__":
  if platform.system() == "Linux":
      createDaemon()
    else:
      sys.exit()

  

带控制参数的例子:

编写守护进程的基类,用于继承:

# coding: utf-8

import os
import sys
import time
import atexit
import signal

class Daemon:
  def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    self.stdin = stdin
    self.stdout = stdout
    self.stderr = stderr
    self.pidfile = pidfile

  def daemonize(self):
    if os.path.exists(self.pidfile):
      raise RuntimeError('Already running.')

    # First fork (detaches from parent)
    try:
      if os.fork() > 0:
        raise SystemExit(0)
    except OSError as e:
      raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))

    os.chdir('/')
    os.setsid()
    os.umask(0o22)

    # Second fork (relinquish session leadership)
    try:
      if os.fork() > 0:
        raise SystemExit(0)
    except OSError as e:
      raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))

    # Flush I/O buffers
    sys.stdout.flush()
    sys.stderr.flush()

    # Replace file descriptors for stdin, stdout, and stderr
    with open(self.stdin, 'rb', 0) as f:
      os.dup2(f.fileno(), sys.stdin.fileno())
    with open(self.stdout, 'ab', 0) as f:
      os.dup2(f.fileno(), sys.stdout.fileno())
    with open(self.stderr, 'ab', 0) as f:
      os.dup2(f.fileno(), sys.stderr.fileno())

    # Write the PID file
    with open(self.pidfile, 'w') as f:
      print(os.getpid(), file=f)

    # Arrange to have the PID file removed on exit/signal
    atexit.register(lambda: os.remove(self.pidfile))

    signal.signal(signal.SIGTERM, self.__sigterm_handler)

  # Signal handler for termination (required)
  @staticmethod
  def __sigterm_handler(signo, frame):
    raise SystemExit(1)

  def start(self):
    try:
      self.daemonize()
    except RuntimeError as e:
      print(e, file=sys.stderr)
      raise SystemExit(1)

    self.run()

  def stop(self):
    try:
      if os.path.exists(self.pidfile):
        with open(self.pidfile) as f:
          os.kill(int(f.read()), signal.SIGTERM)
      else:
        print('Not running.', file=sys.stderr)
        raise SystemExit(1)
    except OSError as e:
      if 'No such process' in str(e) and os.path.exists(self.pidfile):
        os.remove(self.pidfile)

  def restart(self):
    self.stop()
    self.start()

  def run(self):
  #继承类重写该方法
    pass

  编写自己的类:

 #导入刚刚编写的基类
 from daemon import Daemon
 #继承
 class MyTestDaemon(Daemon):
 #重写run方法,就是你要后台运行的函数
   def run(self):
   #后台运行的函数,比如shell输出到自己定义的文件
     sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid()))
     while True:
       sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime()))
       sys.stdout.flush()

       time.sleep(5)

 if __name__ == '__main__':
   PIDFILE = '/tmp/daemon-example.pid'
   LOG = '/tmp/daemon-example.log'
   daemon = MyTestDaemon(pidfile=PIDFILE, stdout=LOG, stderr=LOG)

   if len(sys.argv) != 2:
     print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr)
     raise SystemExit(1)

   if 'start' == sys.argv[1]:
     daemon.start()
   elif 'stop' == sys.argv[1]:
     daemon.stop()
   elif 'restart' == sys.argv[1]:
     daemon.restart()
   else:
     print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr)
     raise SystemExit(1)

关于两次fork

第二个fork不是必须的,只是为了防止进程打开控制终端。

打开一个控制终端的条件是该进程必须是session leader。第一次fork,setsid之后,子进程成为session leader,进程可以打开终端;第二次fork产生的进程,不再是session leader,进程则无法打开终端。

也就是说,只要程序实现得好,控制程序不主动打开终端,无第二次fork亦可。

代码实现

# coding: utf-8

import os
import sys
import time
import atexit
import signal

class Daemon:
  def __init__(self, pidfile='/tmp/daemon.pid', stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
    self.stdin = stdin
    self.stdout = stdout
    self.stderr = stderr
    self.pidfile = pidfile

  def daemonize(self):
    if os.path.exists(self.pidfile):
      raise RuntimeError('Already running.')

    # First fork (detaches from parent)
    try:
      if os.fork() > 0:
        raise SystemExit(0)
    except OSError as e:
      raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))

    os.chdir('/')
    os.setsid()
    os.umask(0o22)

    # Second fork (relinquish session leadership)
    try:
      if os.fork() > 0:
        raise SystemExit(0)
    except OSError as e:
      raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))

    # Flush I/O buffers
    sys.stdout.flush()
    sys.stderr.flush()

    # Replace file descriptors for stdin, stdout, and stderr
    with open(self.stdin, 'rb', 0) as f:
      os.dup2(f.fileno(), sys.stdin.fileno())
    with open(self.stdout, 'ab', 0) as f:
      os.dup2(f.fileno(), sys.stdout.fileno())
    with open(self.stderr, 'ab', 0) as f:
      os.dup2(f.fileno(), sys.stderr.fileno())

    # Write the PID file
    with open(self.pidfile, 'w') as f:
      print(os.getpid(), file=f)

    # Arrange to have the PID file removed on exit/signal
    atexit.register(lambda: os.remove(self.pidfile))

    signal.signal(signal.SIGTERM, self.__sigterm_handler)

  # Signal handler for termination (required)
  @staticmethod
  def __sigterm_handler(signo, frame):
    raise SystemExit(1)

  def start(self):
    try:
      self.daemonize()
    except RuntimeError as e:
      print(e, file=sys.stderr)
      raise SystemExit(1)

    self.run()

  def stop(self):
    try:
      if os.path.exists(self.pidfile):
        with open(self.pidfile) as f:
          os.kill(int(f.read()), signal.SIGTERM)
      else:
        print('Not running.', file=sys.stderr)
        raise SystemExit(1)
    except OSError as e:
      if 'No such process' in str(e) and os.path.exists(self.pidfile):
        os.remove(self.pidfile)

  def restart(self):
    self.stop()
    self.start()

  def run(self):
    pass

使用测试

import os
import sys
import time

from daemon import Daemon

class MyTestDaemon(Daemon):
  def run(self):
    sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid()))
    while True:
      sys.stdout.write('Daemon Alive! {}\n'.format(time.ctime()))
      sys.stdout.flush()

      time.sleep(5)

if __name__ == '__main__':
  PIDFILE = '/tmp/daemon-example.pid'
  LOG = '/tmp/daemon-example.log'
  daemon = MyTestDaemon(pidfile=PIDFILE, stdout=LOG, stderr=LOG)

  if len(sys.argv) != 2:
    print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr)
    raise SystemExit(1)

  if 'start' == sys.argv[1]:
    daemon.start()
  elif 'stop' == sys.argv[1]:
    daemon.stop()
  elif 'restart' == sys.argv[1]:
    daemon.restart()
  else:
    print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr)
    raise SystemExit(1)
[daemon] python test.py start                     23:45:42
[daemon] cat /tmp/daemon-example.pid                 23:45:49
8532
[daemon] ps -ef|grep 8532 | grep -v grep               23:46:07
 502 8532   1  0 11:45下午 ??     0:00.00 python test.py start
[daemon] tail -f /tmp/daemon-example.log               23:46:20
Daemon started with pid 8532
Daemon Alive! Fri Dec 2 23:45:49 2016
Daemon Alive! Fri Dec 2 23:45:54 2016
Daemon Alive! Fri Dec 2 23:45:59 2016
Daemon Alive! Fri Dec 2 23:46:04 2016
Daemon Alive! Fri Dec 2 23:46:09 2016
Daemon Alive! Fri Dec 2 23:46:14 2016
Daemon Alive! Fri Dec 2 23:46:19 2016
Daemon Alive! Fri Dec 2 23:46:24 2016
Daemon Alive! Fri Dec 2 23:46:29 2016
Daemon Alive! Fri Dec 2 23:46:34 2016

[daemon] python test.py stop                     23:46:36
[daemon] ps -ef|grep 8532 | grep -v grep               23:46:43
(0)

相关推荐

  • Python守护进程(daemon)代码实例

    # -*-coding:utf-8-*- import sys, os '''将当前进程fork为一个守护进程 注意:如果你的守护进程是由inetd启动的,不要这样做!inetd完成了 所有需要做的事情,包括重定向标准文件描述符,需要做的事情只有 chdir() 和 umask()了 ''' def daemonize(stdin='/dev/null',stdout= '/dev/null', stderr= 'dev/null'): '''Fork当前进程为守护进程,重定向标准文件描述符 (

  • python实现的守护进程(Daemon)用法实例

    本文实例讲述了python实现的守护进程(Daemon)用法.分享给大家供大家参考.具体如下: def createDaemon(): "'Funzione che crea un demone per eseguire un determinato programma-"' import os # create - fork 1 try: if os.fork() > 0: os._exit(0) # exit father- except OSError, error: pr

  • Python如何实现守护进程的方法示例

    场景设置: 你编写了一个python服务程序,并且在命令行下启动,而你的命令行会话又被终端所控制,python服务成了终端程序的一个子进程.因此如果你关闭了终端,这个命令行程序也会随之关闭. 要使你的python服务不受终端影响而常驻系统,就需要将它变成守护进程. 守护进程就是Daemon程序,是一种在系统后台执行的程序,它独立于控制终端并且执行一些周期任务或触发事件,通常被命名为"d"字母结尾,如常见的httpd.syslogd.systemd和dockerd等. 代码实现 pyth

  • Python实现Linux下守护进程的编写方法

    本文实例讲述了Python实现Linux下守护进程的编写方法,分享给大家供大家参考,相信对于大家的Python程序设计会起到一定的帮助作用.具体方法如下: 1. 调用fork()以便父进程可以退出,这样就将控制权归还给运行你程序的命令行或shell程序.需要这一步以便保证新进程不是一个进程组头领进程(process group leader).下一步,'setsid()',会因为你是进程组头领进程而失败.进程调用fork函数时,操作系统会新建一个子进程,它本质上与父进程完全相同.子进程从父进程继

  • python daemon守护进程实现

    假如写一段服务端程序,如果ctrl+c退出或者关闭终端,那么服务端程序就会退出,于是就想着让这个程序成为守护进程,像httpd一样,一直在后端运行,不会受终端影响. 守护进程英文为daemon,像httpd,mysqld,最后一个字母d其实就是表示daemon的意思. 守护进程的编写步骤: 1.fork子进程,然后父进程退出,此时子进程会被init进程接管. 2.修改子进程的工作目录,创建新进程组合新会话,修改umask. 3.子进程再次fork一个进程,这个进程可以称为孙子进程,然后子进程退出

  • Python守护进程和脚本单例运行详解

    本篇文章主要介绍了Python守护进程和脚本单例运行,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 一.简介 守护进程最重要的特性是后台运行:它必须与其运行前的环境隔离开来,这些环境包括未关闭的文件描述符.控制终端.会话和进程组.工作目录以及文件创建掩码等:它可以在系统启动时从启动脚本/etc/rc.d中启动,可以由inetd守护进程启动,也可以有作业规划进程crond启动,还可以由用户终端(通常是shell)执行. Python有时需要保证只运行一个脚本实例,以避

  • Python守护进程用法实例分析

    本文实例讲述了Python守护进程用法.分享给大家供大家参考.具体分析如下: 守护进程是可以一直运行而不阻塞主程序退出.要标志一个守护进程,可以将Process实例的daemon属性设置为True.代码如下: import os import time import random import sys from multiprocessing import Process,current_process def daemon(): p = current_process() print "sta

  • 使用Python编写Linux系统守护进程实例

    守护进程(daemon)是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控.此类程序会被以进程的形式初始化.通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下.守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行.–维基百科 守护进程区别于普通用户登陆系统后运行的进程,它是直接由系统初始化,和系统用户没有关系,而用户开启的进程依存与用

  • python使用fork实现守护进程的方法

    os模块中的fork方法可以创建一个子进程.相当于克隆了父进程 os.fork() 子进程运行时,os.fork方法会返回0: 而父进程运行时,os.fork方法会返回子进程的PID号. 所以可以使用PID来区分两个进程: #!/usr/bin/env python #coding=utf8 from time import sleep import os try: pid = os.fork() except OSError, e: pass sleep(30) 运行代码,查看进程: [roo

  • PHP将进程作为守护进程的方法

    本文实例讲述了PHP将进程作为守护进程的方法.分享给大家供大家参考.具体分析如下: php中posix_setsid()的用法 文档解释是"Make the current process a session leader" 参考文档:http://linux.die.net/man/2/setsid 意思就是在一个进程组之间(父进程和子进程)调用这个函数的进程会被选举为进程组的leader 所以让一个进程成为守护进程的方法就是: 1 fork出一个子进程 2 在子进程posix_se

  • Python使用Supervisor来管理进程的方法

    本文实例讲述了Python使用Supervisor来管理进程的方法.分享给大家供大家参考.具体分析如下: Supervisor可以启动.停止.重启*nix系统中的程序.也可以重启崩溃的程序. supervisord的一个守护进程,用于将指定的进程当做子进程来运行. supervisorctl是一个客户端程序,可以查看日志并通过统一的会话来控制进程. 看例子: 我们写了一个py脚本,用于往log文件中记录一条当前的时间. root@ubuntu:/home/zoer# cat daemon.py

  • python 根据pid杀死相应进程的方法

    用python语言实现根据pid杀死相应进程 kill_process.py代码如下 #! /usr/bin/python # -*- coding: utf-8 -*- import os import sys import signal def kill(pid): try: a = os.kill(pid, signal.SIGKILL) # a = os.kill(pid, signal.9) # 与上等效 print '已杀死pid为%s的进程, 返回值是:%s' % (pid, a)

  • 使用python根据端口号关闭进程的方法

    我们知道,做web开发,在调试时需要反复启动整个工程,那么上一个工程占用的端口,在下一次工程启动时就不能用了,因为占用的端口没有释放,但是手动关闭方法是: lsof -i:12345 得到pid后再kill -9 pid 十分麻烦,所以能否在启动python的web工程时顺便把上一次占用的端口给关闭了呢? 当然是有的,下面以flask为例(注意,复制下面的代码时,不要直接鼠标复制,而是使用左上角的view plain来复制,因为csdn博客会把'''+str(port)+'''的右侧显示成5个单

  • .net Core 3.0 WebApi 创建Linux守护进程的方法

    前言 我们一般可以在Linux服务器上执行 dotnet <app_assembly.dll> 命令来运行我们的.net Core WebApi应用.但是这样运行起来的应用很不稳定,关闭终端窗口之后,应用也会停止运行.为了让其可以稳定运行,我们需要让它变成系统的守护进程,成为一种服务一直在系统中运行,出现异常时也能重新启动. Linux系统有自己的守护进程管理工具 Systemd .systemd 是内核启动后的第一个用户进程,PID 为1,是所有其它用户进程的父进程.它直接与内核交互,性能出

  • Windows 和 Linux 上Redis的安装守护进程配置方法

    # Windows 和 Linux 上Redis的安装守护进程配置 Redis 简介 ​ Redis是目前最常用的非关系型数据库(NOSql)之一,常以Key-Value的形式存储.Redis读写速度远远比关系型数据库高(mysql.oracle).所以常用作项目热数据缓存 Windows上的安装配置 1) 在GitHub下载https://github.com/microsoftarchive/redis/releases 2) 解压并安装redis服务以支持守护进程(后台启动) 解压之后目录

随机推荐