Chinaunix首页 | 论坛 | 博客
  • 博客访问: 344778
  • 博文数量: 46
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 562
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-14 13:32
个人简介

先知者为师

文章分类

全部博文(46)

文章存档

2016年(1)

2015年(6)

2014年(20)

2013年(19)

我的朋友

分类: Windows平台

2015-02-02 11:46:16

u
Python做windows服务(多进程服务),并结束多进程

    本篇文章可能对win下的vc,vc++,.net 开发者很容易实现,但是用python来说不是很好实现。至少我找了N多资料都没有很好
的解决方案。
    以前在linux平台关闭多进程时很简单,linux提供的机制很方便,利用信号就可以搞定,本人不是Win_developer,公司的项目是需要支持跨平台的,
采用python写的服务端,以便跨平台使用。

    python做windows服务,google用不了,只能百度和360搜索了,个人认为360搜索技术类的比百度还好点,并非广告^.^。
搜索到的资料基本就一种,贴下代码:

点击(此处)折叠或打开

  1. import win32event
  2. import win32service
  3. import win32serviceutil
  4. class ProductCollectWin32Service (win32serviceutil.ServiceFramework):
  5.     _svc_name_ = "pythonService"
  6.     _svc_display_name_ = "pythonService"
  7.     def __init__(self, args):
  8.         win32serviceutil.ServiceFramework.__init__(self, args)
  9.         self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
  10.     def SvcStop(self):
  11.         # 先告诉SCM停止这个过程
  12.         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  13.         # 设置事件
  14.         win32event.SetEvent(self.hWaitStop)
  15.     def SvcDoRun(self):
  16.         # 等待服务被停止
  17.         win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
  18. if __name__=='__main__':
  19.     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 ")
下面是完整的代码

点击(此处)折叠或打开

  1. import win32serviceutil
  2. import win32service
  3. import win32event
  4. import time,sys
  5. import mmap

  6.     
  7. class python_service(win32serviceutil.ServiceFramework):
  8.     _svc_name_ = "Python sevice"
  9.     _svc_display_name_ = "A Python Server"

  10.     def __init__(self, args):

  11.         win32serviceutil.ServiceFramework.__init__(self, args)

  12.         # Create an event which we will use to wait on.
  13.         # The "service stop" request will set this event.
  14.         self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
  15.         self.mm = None
  16.     def SvcStop(self):
  17.         # Before we do anything, tell the SCM we are starting the stop process.
  18.         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  19.         # And set my event.
  20.         self.mm.seek(0)
  21.         self.mm.write("quite")
  22.         self.mm.close()


  23.     
  24.     def SvcDoRun(self):
  25.         #what to do#
  26.         os.chdir(sys.path[0])#切换工作路径
  27.         
  28.         self.mm = mmap.mmap(-1,1024,access=mmap.ACCESS_WRITE, tagname='test')
  29.         self.mm.seek(0)
  30.         self.mm.write("start")
  31.         os.system("python main.py ")
  32.  
  33.         win32event.WaitForSingleObject(self.hWaitStop,0) # win32event.INFINITE)

  34. if __name__=='__main__':
  35.     if sys.platform.startswith('win'):
  36.         pass #multiprocessing.freeze_support()
  37.     else:
  38.         os.exit(-1)
  39.     win32serviceutil.HandleCommandLine(python_service)
在启动时 SvcDoRun 分配共享内存 并写入数据"start",其实开始可以不写。只需要结束的时候,写个"quite",当然写什么都无所谓,因为你在main.py
里是需要比较字符串的,你可以给字符串加密之类的 这是后话,暂且不表~
main.py

点击(此处)折叠或打开

  1. mm = mmap.mmap(-1,1024,access=mmap.ACCESS_WRITE, tagname='test')

  2. def isquite():
  3.     mm.seek(0)
  4.     r = mm.read(5)
  5. #    esl_init.log.info("mm.read : %s" % r)
  6.     if r == "quite":
  7.         return True
  8.     else:
  9.         return False
  10. if (platform.system() == "Windows") and isquite():
  11.                 mm.close()
  12.                 raise KeyboardInterrupt,"bay!"
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.

阅读(8579) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~