linux python守护进程,Python实现Linux下守护进程的编写方法

本文实例讲述了Python实现Linux下守护进程的编写方法,分享给大家供大家参考,相信对于大家的Python程序设计会起到一定的帮助作用。具体方法如下:

1. 调用fork()以便父进程可以退出,这样就将控制权归还给运行你程序的命令行或shell程序。需要这一步以便保证新进程不是一个进程组头领进程(process group leader)。下一步,‘setsid()',会因为你是进程组头领进程而失败。进程调用fork函数时,操作系统会新建一个子进程,它本质上与父进程完全相同。子进程从父进程继承了多个值的拷贝,比如全局变量和环境变量。两个进程唯一的区别就是fork的返回值。child(子)进程接收返回值为0,而父进程接收子进程的pid作为返回值。调用fork函数后,两个进程并发执行同一个程序,首先执行的是调用了fork之后的下一行代码。父进程和子进程既并发执行,又相互独立;也就是说,它们是“异步执行”的。

2. 调用‘setsid()' 以便成为一个进程组和会话组的头领进程。由于一个控制终端与一个会话相关联,而且这个新会话还没有获得一个控制终端,我们的进程没有控制终端,这对于守护程序来说是一件好事。

3. 再次调用‘fork()'所以父进程(会话组头领进程)可以退出。这意味着我们,一个非会话组头领进程永远不能重新获得控制终端。

4. 调用‘chdir("/")'确认我们的进程不保持任何目录于使用状态。不做这个会导致系统管理员不能卸装(umount)一个文件系统,因为它是我们的当前工作目录。 [类似的,我们可以改变当前目录至对于守护程序运行重要的文件所在目录]

5. 调用‘umask(0)'以便我们拥有对于我们写的任何东西的完全控制。我们不知道我们继承了什么样的umask。 [这一步是可选的](译者注:这里指步骤5,因为守护程序不一定需要写文件)

6. 调用‘close()'关闭文件描述符0,1和2。这样我们释放了从父进程继承的标准输入,标准输出,和标准错误输出。我们没办法知道这些文描述符符可能已经被重定向去哪里。注意到许多守护程序使用‘sysconf()'来确认‘_SC_OPEN_MAX'的限制。‘_SC_OPEN_MAX'告诉你每个进程能够打开的最多文件数。然后使用一个循环,守护程序可以关闭所有可能的文件描述符。你必须决定你需要做这个或不做。如果你认为有可能有打开的文件描述符,你需要关闭它们,因为系统有一个同时打开文件数的限制。

7. 为标准输入,标准输出和标准错误输出建立新的文件描述符。即使你不打算使用它们,打开着它们不失为一个好主意。准确操作这些描述符是基于各自爱好;比如说,如果你有一个日志文件,你可能希望把它作为标准输出和标准错误输出打开,而把‘/dev/null'作为标准输入打开;作为替代方法,你可以将‘/dev/console'作为标准错误输出和/或标准输出打开,而‘/dev/null'作为标准输入,或者任何其它对你的守护程序有意义的结合方法。(译者注:一般使用dup2函数原子化关闭和复制文件描述符。

实现代码如下:

# Core modules

importatexit

importos

importsys

importtime

importsignal

classDaemon(object):

"""

A generic daemon class.

Usage: subclass the Daemon class and override the run() method

"""

def __init__(self,pidfile,stdin=os.devnull,stdout=os.devnull,stderr=os.devnull,home_dir='.',umask=022,verbose=1):

self.stdin = stdin

self.stdout = stdout

self.stderr = stderr

self.pidfile = pidfile

self.home_dir = home_dir

self.verbose = verbose

self.umask = umask

self.daemon_alive = True

def daemonize(self):

"""

Do the UNIX double-fork magic,see Stevens' "Advanced

Programming in the UNIX Environment" for details (ISBN 0201563177)

"""

try:

pid = os.fork()

if pid > 0:

# Exit first parent

sys.exit(0)

except OSError,e:

sys.stderr.write(

"fork #1 Failed: %d (%s)\n" % (e.errno,e.strerror))

sys.exit(1)

# Decouple from parent environment

os.chdir(self.home_dir)

os.setsid()

os.umask(self.umask)

# Do second fork

try:

pid = os.fork()

if pid > 0:

# Exit from second parent

sys.exit(0)

except OSError,e:

sys.stderr.write(

"fork #2 Failed: %d (%s)\n" % (e.errno,e.strerror))

sys.exit(1)

if sys.platform != 'darwin': # This block breaks on OS X

# Redirect standard file descriptors

sys.stdout.flush()

sys.stderr.flush()

si = file(self.stdin,'r')

so = file(self.stdout,'a+')

if self.stderr:

se = file(self.stderr,'a+',0)

else:

se = so

os.dup2(si.fileno(),sys.stdin.fileno())

os.dup2(so.fileno(),sys.stdout.fileno())

os.dup2(se.fileno(),sys.stderr.fileno())

def sigtermhandler(signum,frame):

self.daemon_alive = False

signal.signal(signal.SIGTERM,sigtermhandler)

signal.signal(signal.SIGINT,sigtermhandler)

if self.verbose >= 1:

print "Started"

# Write pidfile

atexit.register(

self.delpid) # Make sure pid file is removed if we quit

pid = str(os.getpid())

file(self.pidfile,'w+').write("%s\n" % pid)

def delpid(self):

os.remove(self.pidfile)

def start(self,*args,**kwargs):

"""

Start the daemon

"""

if self.verbose >= 1:

print "Starting..."

# Check for a pidfile to see if the daemon already runs

try:

pf = file(self.pidfile,'r')

pid = int(pf.read().strip())

pf.close()

except IOError:

pid = None

except SystemExit:

pid = None

if pid:

message = "pidfile %s already exists. Is it already running?\n"

sys.stderr.write(message % self.pidfile)

sys.exit(1)

# Start the daemon

self.daemonize()

self.run(*args,**kwargs)

def stop(self):

"""

Stop the daemon

"""

if self.verbose >= 1:

print "Stopping..."

# Get the pid from the pidfile

pid = self.get_pid()

if not pid:

message = "pidfile %s does not exist. Not running?\n"

sys.stderr.write(message % self.pidfile)

# Just to be sure. A ValueError might occur if the PID file is

# empty but does actually exist

if os.path.exists(self.pidfile):

os.remove(self.pidfile)

return # Not an error in a restart

# Try killing the daemon process

try:

i = 0

while 1:

os.kill(pid,signal.SIGTERM)

time.sleep(0.1)

i = i + 1

if i % 10 == 0:

os.kill(pid,signal.SIGHUP)

except OSError,err:

err = str(err)

if err.find("No such process") > 0:

if os.path.exists(self.pidfile):

os.remove(self.pidfile)

else:

print str(err)

sys.exit(1)

if self.verbose >= 1:

print "Stopped"

def restart(self):

"""

Restart the daemon

"""

self.stop()

self.start()

def get_pid(self):

try:

pf = file(self.pidfile,'r')

pid = int(pf.read().strip())

pf.close()

except IOError:

pid = None

except SystemExit:

pid = None

return pid

def is_running(self):

pid = self.get_pid()

print(pid)

return pid and os.path.exists('/proc/%d' % pid)

def run(self):

"""

You should override this method when you subclass Daemon.

It will be called after the process has been

daemonized by start() or restart().

"""

感兴趣的读者可以调试运行一下本文实例代码,相信会有新的收获。

总结

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值