博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux守护进程设计规范及python实现
阅读量:2397 次
发布时间:2019-05-10

本文共 5323 字,大约阅读时间需要 17 分钟。

由于需要为OpenStack中的虚拟机设计监控Agent,因此需要一个稳妥、可靠并简单的守护进程实现作为基础框架,故研究了一下Linux系统中的守护进程。

首先,守护进程是一类在后台执行,生命周期较长的进程,它一般随系统启动运行,在系统关闭的时候停止。翻译了一下《Advanced Programming in The Unix Environment  Section》的第13.3小节,此小节是一个关于守护进程的设计规范,罗列了守护进程设计实现的几大原则:

 

翻译《AdvancedProgrammingin The Unix Environment》Section 13.3 Page 583

 

守护进程编码规范:

        本小节将介绍一些守护进程的基本编码规范,这些规范将阻止守护进程与当前环境产生一些不必要的交互。本节将通过一个函数daemonize实现这些规范。

 

1.     首先要做的被称为 umask这一步骤会将文件创建掩码重置为0。这一步的原因是守护进程继承(inherited)得到的文件掩码有可能会拒绝某些特定的文件操作权限。如果守护进程想要创建文件,那有可能它需要设置特定的文件操作权限。例如,如果守护进程想要创建允许组读和写(group-readand group-write)权限的文件,但继承得到的文件创建掩码屏蔽了这个权限,则创建操作不会成功。

2.    调用 fork 并使父进程退出(exit)。这一步骤的目的在于。首先,如果守护进程是通过一个简单的shell命令建立的,那么在父进程终止的时候shell会认为命令已经结束了继而结束守护进程。其次,子进程继承得到父进程的groupID同时也获得了一个新的进程号,所以我们必须得保证子进程不能担任groupleader的角色,这是下一步setsid 操作的前提。

注:此步是使得程在后台运行。

3.    调用 setsid 创建一个新的会话。有三个步骤将会执行:(a)进程将成为这个新会话的sessionleader角色,(b)此进程将会成为一个新的进程组的groupleader,(c)此进程将不会有控制终端。

注:此步将使得程脱离控制端、登以及

·     在基于SystemV的系统中,有建议再一次调用fork 并使父进程退出。而新产生的进程将会成为真正的守护进程。这一步骤将保证守护进程不是一个sessionleader,进而阻止它获取一个控制终端。或者另一种阻止守护进程获取控制终端的方案是任意时刻打开一个终端设备的时候确保指定O_NOCTTY

注:此步将禁止程重新打开控制端。

4.    将当前的工作目录切换到系统根目录下。因为从父进程集成来的当前工作目录可能是一个被挂载的文件系统。因为守护进程通常是直到系统重启的时候才会退出,如果守护进程的工作目录在一个挂载的文件系统上,那么这个文件系统就不能被卸载(unmounted)。

·      有的守护进程可能会将当前的工作目录切换到一些特定的路径,在这些路径下它们将完成它们的工作。例如,lineprinter spoolingdaemons 通常将工作目录切换为spool目录。

5.    一些不必要的文件描述符将会被关闭。这个步骤将阻止守护进程保持从父进程集成到的任何已经打开的文件描述符(也可能是shell或其他进程)。我们可以使用 open_max 函数或 getrlimit 函数来查找当前优先级最高的文件描述符并关闭此描述符之下的所有其他描述符。

注:保持打开的描述符将会占用系统资源并可能使某些文件不能被卸

6.    有一些守护进程将打开文件描述符0, 1, 2 指向 /dev/null ,这样一来所有试图从标准输入、输出及错误读取守护进程信息的操作都不能成功。因为守护进程当前已经不与任何终端设备相关联,没有地方显示其输出或接受用户的输入。即使守护进程是从一个交互式session创建的,守护进程也将运行在后台,任何终端的登录与终止将不会影响守护进程。如果有其他用户通过当前的终端登录,我们也不希望守护进程的输出出现在终端上,并且该用户的任何输入也不会被守护进程接收。

 

总结起来就是:

1) 第一次fork将会创建父-子进程,同时使得父进程退出保证守护进程能够运行在后台。

2) 通过setsid步骤使得进程与控制终端、登录会话以及进程组脱离。

3) 第二次fork将确保进程重新打开控制终端,并且产生子-孙进程,而子进程退出后孙进程将成为真正的守护进程。

4) 其他还有一些诸如工作目录设置、关闭文件描述符、设置文件创建掩码之类的操作。

 

最后看一个python实现的例子,代码来自:http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/

#!/usr/bin/env pythonimport sys, os, time, atexitfrom signal import SIGTERM class Daemon:	"""	A generic daemon class.		Usage: subclass the Daemon class and override the run() method	"""	def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):		self.stdin = stdin		self.stdout = stdout		self.stderr = stderr		self.pidfile = pidfile		def daemonize(self):		"""		do the UNIX double-fork magic, see Stevens' "Advanced 		Programming in the UNIX Environment" for details (ISBN 0201563177)		http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16		"""		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("/") 		os.setsid() 		os.umask(0) 			# 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) 			# redirect standard file descriptors		sys.stdout.flush()		sys.stderr.flush()		si = file(self.stdin, 'r')		so = file(self.stdout, 'a+')		se = file(self.stderr, 'a+', 0)		os.dup2(si.fileno(), sys.stdin.fileno())		os.dup2(so.fileno(), sys.stdout.fileno())		os.dup2(se.fileno(), sys.stderr.fileno())			# write pidfile		atexit.register(self.delpid)		pid = str(os.getpid())		file(self.pidfile,'w+').write("%s\n" % pid)		def delpid(self):		os.remove(self.pidfile)	def start(self):		"""		Start the daemon		"""		# 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			if pid:			message = "pidfile %s already exist. Daemon already running?\n"			sys.stderr.write(message % self.pidfile)			sys.exit(1)				# Start the daemon		self.daemonize()		self.run()	def stop(self):		"""		Stop the daemon		"""		# Get the pid from the pidfile		try:			pf = file(self.pidfile,'r')			pid = int(pf.read().strip())			pf.close()		except IOError:			pid = None			if not pid:			message = "pidfile %s does not exist. Daemon not running?\n"			sys.stderr.write(message % self.pidfile)			return # not an error in a restart		# Try killing the daemon process			try:			while 1:				os.kill(pid, SIGTERM)				time.sleep(0.1)		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)	def restart(self):		"""		Restart the daemon		"""		self.stop()		self.start()	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().		"""
Daemon.daemonize()方法就是执行了上述的步骤。如果需要使用此守护进程类,请继承Daemon类并自行实现run方法。

#!/usr/bin/env pythonimport sys, timefrom daemon import Daemonclass MyDaemon(Daemon):	def run(self):		while True:			time.sleep(1)if __name__ == "__main__":	daemon = MyDaemon('/tmp/daemon-example.pid')	if len(sys.argv) == 2:		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"			sys.exit(2)		sys.exit(0)	else:		print "usage: %s start|stop|restart" % sys.argv[0]		sys.exit(2)

转载地址:http://uwfob.baihongyu.com/

你可能感兴趣的文章
使用pg_stat_statement监控pgsql遇到的问题
查看>>
ASM 翻译系列第十弹:ASM Internal ASM DISK header
查看>>
ASM 翻译系列第一弹:基础知识 ASM AU,Extents,Mirroring 和 Failgroups
查看>>
MySQL排序内部原理探秘
查看>>
ASM 翻译系列第八弹:ASM Internal ASM file extent map
查看>>
利用sys schema解决一次诡异的语句hang问题
查看>>
数据库容器化|未来已来
查看>>
容器化RDS|计算存储分离架构下的 IO 优化
查看>>
控制文件不一致引发的“血案”
查看>>
MySQL 5.7复制配置不规范修改导致的坑(一)
查看>>
MySQL8.0——Resource Group(资源组)
查看>>
基于Oracle的私有云架构探析(连载二)
查看>>
ASM 翻译系列第九弹:高级知识ASM Toolbox
查看>>
MySQL分区如何迁移
查看>>
基于Oracle的私有云架构探析(连载三)
查看>>
ASM 翻译系列第十三弹:ASM 高级知识 - Forcing the issue
查看>>
DBA福音 | 如何无视数据量快速搭建测试数据库
查看>>
如何清除创建失败的索引
查看>>
Oracle压缩黑科技(一)—基础表压缩
查看>>
容器化RDS—计算存储分离架构下的“Split-Brain”
查看>>