Python做windows服务(多进程服务),并结束多进程
本篇文章可能对win下的vc,vc++,.net 开发者很容易实现,但是用python来说不是很好实现。至少我找了N多资料都没有很好
的解决方案。
以前在linux平台关闭多进程时很简单,linux提供的机制很方便,利用信号就可以搞定,本人不是Win_developer,公司的项目是需要支持跨平台的,
采用python写的服务端,以便跨平台使用。
python做windows服务,google用不了,只能百度和360搜索了,个人认为360搜索技术类的比百度还好点,并非广告^.^。
搜索到的资料基本就一种,贴下代码:
这个代码就是玩玩而已,对实际需求根本无法使用,所以我们要在此基础上做更改。
下面将写出几点实际需求:
1.实际项目的工作目录(默认启动该代码时,工作目录在C:\Python27\Lib\site-packages\win32,,该目标是pythonservice.exe的执行目录,我们的项目不可能在改目录)
故而我们需要更改工作目录。
2. 启动多进程服务时进程的环境会有变化,比如在16行添加代码
os.chdir(sys.path[0])#切换工作路径 ,该代码将工作目录切掉到你本地代码的目录
os.system("python main.py ") # 启动main服务
当我添加这两句代码后,main服务能正常开启,正常关闭(关闭一会再说)这样是比较完美的(这样运行和你在cmd下运行是一样的,工作的环境也一样),但是无法关闭
3. 在服务里stop
按照步骤2 想在上面图片那样,关闭服务,是无法关闭的,因为你还没处理stop方法.即第10行未处理。
4. 你要想办法关闭了,想要各种机制了
比如你可以将16行改成这样:
import test
proc = multiprocessing.Process(target = test.run, args = (task_data,))
proc.start()
这样test的run模块启动。这样运行我的代码卡在该处:pool.map(action, groups) 具体为什么我也不清楚跟windows机制有关系。
5. 采用采用附加进程(subprocess方案):
服务无法退出(功能一切正常)
6 采用threading模块
采用线程的话,退出很简单,因为都属于pythonservice进程。stop方法什么也不做就可以退出,但是线程的环境是pythonservice进程的环境,不是我们的自己的环境,也就是环境不干净。我们的test服务运行不正常。
进程退出方案。
1.采用python的multiprocessing.Manager
task_data=multiprocessing.Manager().list() #跨进程使用 or dict方法都可以
Manager是可以跨进程通信的,
因为在SvcStop里你 只需往list扔个数据,test能读到就行,你可以这么启动test
proc = multiprocessing.Process(target = test.run, args = (task_data,))
这样test进程就可以使用task_data了。
而test进程是多进程服务,也采取这种方式给他的多进程传递参数,这样当运行到test代码的
multiprocessing.Manager().list()时,会死掉。
本人猜测,multiprocessing.Manager只能由一个进程创建,不能有多个进程随便创建
(本人使用虚拟机xp环境)
2 信号机制
信号在Linux 进程间通讯(IPC)是必须存在的,你只写个printf("Hello World") 也会用到,这是Linux操作系统的机制 但是在windows下信号机制是无法使用的,多数人说有消息机制啊,是的win下的消息是针对窗口的,我记得看win32汇编时提到过,有VM_MESG之类的具体叫什么名字我忘了。反正windows开发者肯定知道我说什么。windows的消息机制是针对窗口的,而不是命令行的。
信号也不能用的通!!!
难道就没办法解决这问题了么! 因为在linux下有共享内存的概念,我猜win下肯定会有这种机制,操作系统的原理是不会变的,操作系统最重要的任务就是承载进程,现代os基本都是多进程的,多进程是需要通讯的,必然会存在共享内存的。
我采用共享内存的方案来使不相干进程的退出,
还记得否,我们上面最完美的方案应属 os.system("python main.py ")
下面是完整的代码
在启动时 SvcDoRun 分配共享内存 并写入数据"start",其实开始可以不写。只需要结束的时候,写个"quite",当然写什么都无所谓,因为你在main.py
里是需要比较字符串的,你可以给字符串加密之类的 这是后话,暂且不表~
main.py
main进程统管多进程检测到写入quite时就会退出,至于如何退出是你们自己的事了,机制就摆在这里。
另外说下服务的运行原理
在类python_service的__init__函数执行完毕后,系统服务就算是启动成功了(启动) 这时windows系统会自动调用SvcDoRun函数,
该函数不可以结束;如果该函数结束就表示服务停止。你的程序不管是放在__init__中以线程方式启动还是放在SvcDoRun函数中调用,必须确保该函数不退出。
如果没有该函数,系统会提示该服务没什么事可作,然后就会停止服务。(正在运行) 当停止该服务时,系统会调用SvcStop函数,
该函数通过某种方式(例如标志位)让SvcDoRun函数退出,服务就算是正常停止了。例子中是通过event事件让SvcDoRun函数停止等待,从而退出的。(停止)
注意:Windows系统关机时,是不会调用终止函数的。 在类win32test中你可以定义其他的方法(函数),用于自己的代码设计;这与普通的python类没有什么不同。
再说下性能方面,python当然没有c的性能高,从共享内存mmap就可以看出,mmap返回的是共享内存的对象,当然这是面向对象的说法,其实mmap返回的更像是文件
其用法跟文件一样,mm.read 、write、tell、 seek等,而c语言中返回的是共享内存的首地址的指针,这个多好,直接操作指针多快!不用再seek等等。
注意用mmap对象时,一定要时刻记住你的文件指针在哪,比如刚申请时文件指针为0,mm.write("quite") 那么之后,文件指针是在5处,而不是在0的位置,这就是我为什么每次写的时候都要seek(0)处,再main读的时候也要seek(0)处,才能读到正确的值。记住mmap返回的是文件,要理解文件系统是如何处理文件的!!!
最后要在SvcDoRun函数的win32event.WaitForSingleObject(self.hWaitStop,0) 第二个参数为0 而不是win32event.INFINITE,后者在等待某个事件的信号,而我们直接执行
os.system("python main.py "),就会执行win32event.WaitForSingleObject(self.hWaitStop,win32event.INFINITE) 没有任何事件会发给WaitForSingleObject,故将第二个参数改为0.
本篇文章可能对win下的vc,vc++,.net 开发者很容易实现,但是用python来说不是很好实现。至少我找了N多资料都没有很好
的解决方案。
以前在linux平台关闭多进程时很简单,linux提供的机制很方便,利用信号就可以搞定,本人不是Win_developer,公司的项目是需要支持跨平台的,
采用python写的服务端,以便跨平台使用。
python做windows服务,google用不了,只能百度和360搜索了,个人认为360搜索技术类的比百度还好点,并非广告^.^。
搜索到的资料基本就一种,贴下代码:
点击(此处)折叠或打开
- import win32event
- import win32service
- import win32serviceutil
- class ProductCollectWin32Service (win32serviceutil.ServiceFramework):
- _svc_name_ = "pythonService"
- _svc_display_name_ = "pythonService"
- def __init__(self, args):
- win32serviceutil.ServiceFramework.__init__(self, args)
- self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
- def SvcStop(self):
- # 先告诉SCM停止这个过程
- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
- # 设置事件
- win32event.SetEvent(self.hWaitStop)
- def SvcDoRun(self):
- # 等待服务被停止
- win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
- if __name__=='__main__':
- win32serviceutil.HandleCommandLine(ProductCollectWin32Service )
下面将写出几点实际需求:
1.实际项目的工作目录(默认启动该代码时,工作目录在C:\Python27\Lib\site-packages\win32,,该目标是pythonservice.exe的执行目录,我们的项目不可能在改目录)
故而我们需要更改工作目录。
2. 启动多进程服务时进程的环境会有变化,比如在16行添加代码
os.chdir(sys.path[0])#切换工作路径 ,该代码将工作目录切掉到你本地代码的目录
os.system("python main.py ") # 启动main服务
当我添加这两句代码后,main服务能正常开启,正常关闭(关闭一会再说)这样是比较完美的(这样运行和你在cmd下运行是一样的,工作的环境也一样),但是无法关闭
3. 在服务里stop
按照步骤2 想在上面图片那样,关闭服务,是无法关闭的,因为你还没处理stop方法.即第10行未处理。
4. 你要想办法关闭了,想要各种机制了
比如你可以将16行改成这样:
import test
proc = multiprocessing.Process(target = test.run, args = (task_data,))
proc.start()
这样test的run模块启动。这样运行我的代码卡在该处:pool.map(action, groups) 具体为什么我也不清楚跟windows机制有关系。
5. 采用采用附加进程(subprocess方案):
服务无法退出(功能一切正常)
6 采用threading模块
采用线程的话,退出很简单,因为都属于pythonservice进程。stop方法什么也不做就可以退出,但是线程的环境是pythonservice进程的环境,不是我们的自己的环境,也就是环境不干净。我们的test服务运行不正常。
进程退出方案。
1.采用python的multiprocessing.Manager
task_data=multiprocessing.Manager().list() #跨进程使用 or dict方法都可以
Manager是可以跨进程通信的,
因为在SvcStop里你 只需往list扔个数据,test能读到就行,你可以这么启动test
proc = multiprocessing.Process(target = test.run, args = (task_data,))
这样test进程就可以使用task_data了。
而test进程是多进程服务,也采取这种方式给他的多进程传递参数,这样当运行到test代码的
multiprocessing.Manager().list()时,会死掉。
本人猜测,multiprocessing.Manager只能由一个进程创建,不能有多个进程随便创建
(本人使用虚拟机xp环境)
2 信号机制
信号在Linux 进程间通讯(IPC)是必须存在的,你只写个printf("Hello World") 也会用到,这是Linux操作系统的机制 但是在windows下信号机制是无法使用的,多数人说有消息机制啊,是的win下的消息是针对窗口的,我记得看win32汇编时提到过,有VM_MESG之类的具体叫什么名字我忘了。反正windows开发者肯定知道我说什么。windows的消息机制是针对窗口的,而不是命令行的。
信号也不能用的通!!!
难道就没办法解决这问题了么! 因为在linux下有共享内存的概念,我猜win下肯定会有这种机制,操作系统的原理是不会变的,操作系统最重要的任务就是承载进程,现代os基本都是多进程的,多进程是需要通讯的,必然会存在共享内存的。
我采用共享内存的方案来使不相干进程的退出,
还记得否,我们上面最完美的方案应属 os.system("python main.py ")
下面是完整的代码
点击(此处)折叠或打开
- import win32serviceutil
- import win32service
- import win32event
- import time,sys
- import mmap
-
- class python_service(win32serviceutil.ServiceFramework):
- _svc_name_ = "Python sevice"
- _svc_display_name_ = "A Python Server"
- def __init__(self, args):
- win32serviceutil.ServiceFramework.__init__(self, args)
- # Create an event which we will use to wait on.
- # The "service stop" request will set this event.
- self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
- self.mm = None
- def SvcStop(self):
- # Before we do anything, tell the SCM we are starting the stop process.
- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
- # And set my event.
- self.mm.seek(0)
- self.mm.write("quite")
- self.mm.close()
-
- def SvcDoRun(self):
- #what to do#
- os.chdir(sys.path[0])#切换工作路径
-
- self.mm = mmap.mmap(-1,1024,access=mmap.ACCESS_WRITE, tagname='test')
- self.mm.seek(0)
- self.mm.write("start")
- os.system("python main.py ")
-
- win32event.WaitForSingleObject(self.hWaitStop,0) # win32event.INFINITE)
- if __name__=='__main__':
- if sys.platform.startswith('win'):
- pass #multiprocessing.freeze_support()
- else:
- os.exit(-1)
- win32serviceutil.HandleCommandLine(python_service)
里是需要比较字符串的,你可以给字符串加密之类的 这是后话,暂且不表~
main.py
点击(此处)折叠或打开
- mm = mmap.mmap(-1,1024,access=mmap.ACCESS_WRITE, tagname='test')
- def isquite():
- mm.seek(0)
- r = mm.read(5)
- # esl_init.log.info("mm.read : %s" % r)
- if r == "quite":
- return True
- else:
- return False
- if (platform.system() == "Windows") and isquite():
- mm.close()
- raise KeyboardInterrupt,"bay!"
另外说下服务的运行原理
在类python_service的__init__函数执行完毕后,系统服务就算是启动成功了(启动) 这时windows系统会自动调用SvcDoRun函数,
该函数不可以结束;如果该函数结束就表示服务停止。你的程序不管是放在__init__中以线程方式启动还是放在SvcDoRun函数中调用,必须确保该函数不退出。
如果没有该函数,系统会提示该服务没什么事可作,然后就会停止服务。(正在运行) 当停止该服务时,系统会调用SvcStop函数,
该函数通过某种方式(例如标志位)让SvcDoRun函数退出,服务就算是正常停止了。例子中是通过event事件让SvcDoRun函数停止等待,从而退出的。(停止)
注意:Windows系统关机时,是不会调用终止函数的。 在类win32test中你可以定义其他的方法(函数),用于自己的代码设计;这与普通的python类没有什么不同。
再说下性能方面,python当然没有c的性能高,从共享内存mmap就可以看出,mmap返回的是共享内存的对象,当然这是面向对象的说法,其实mmap返回的更像是文件
其用法跟文件一样,mm.read 、write、tell、 seek等,而c语言中返回的是共享内存的首地址的指针,这个多好,直接操作指针多快!不用再seek等等。
注意用mmap对象时,一定要时刻记住你的文件指针在哪,比如刚申请时文件指针为0,mm.write("quite") 那么之后,文件指针是在5处,而不是在0的位置,这就是我为什么每次写的时候都要seek(0)处,再main读的时候也要seek(0)处,才能读到正确的值。记住mmap返回的是文件,要理解文件系统是如何处理文件的!!!
最后要在SvcDoRun函数的win32event.WaitForSingleObject(self.hWaitStop,0) 第二个参数为0 而不是win32event.INFINITE,后者在等待某个事件的信号,而我们直接执行
os.system("python main.py "),就会执行win32event.WaitForSingleObject(self.hWaitStop,win32event.INFINITE) 没有任何事件会发给WaitForSingleObject,故将第二个参数改为0.