Python实现Windows服务的添加、删除、启动、关闭

本文详细介绍如何使用Python在Windows环境下创建、安装、启动、停止及卸载服务,包括管理员权限下运行CMD指令及环境变量持久化修改等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考资料

《bat 做Windows服务》

《Python实现环境变量持久化修改》

《Python实现管理员权限运行CMD指令》

 

务必先看参考资料

 

# !/usr/bin/python3
# coding: utf-8
import os
import time
import traceback

from tool import bat, reg


class Service(object):

    def __init__(self, name, dir, app):
        # 服务名称
        self.name = name
        # 服务程序路径
        self.dir = dir
        # 服务启动程序.bat或.exe
        self.app = app

        cwd = os.getcwd()
        self.instsrv = os.path.join(cwd, r"\extra\instsrv.exe")
        self.srvany = os.path.join(cwd, r"\extra\srvany.exe")
    
    def install(self):
        try:
            self.remove()

            print("Install service ..")
            # 管理员权限运行CMD命令
            bat.runAdmin("%s %s %s" % (self.instsrv, self.name, self.srvany))
            # 添加服务相关参数到注册表
            reg.addSrvParam(self.name, self.dir, self.app)
            print("%s install success" % self.name)
        except Exception as e:
            traceback.print_exc()
            raise e

    def status(self):
        status = os.popen("sc query %s" % self.name).read()
        if "未安装" in status:
            return -1
        elif "START_PENDING" in status:
            return 0
        if "RUNNING" in status:
            return 1
        elif "STOP_PENDING" in status:
            return 2
        elif "STOPPED" in status:
            return 3
        else:
            return 4

    def auto(self):
        try:
            # 管理员权限运行CMD指令
            bat.runAdmin("sc config %s start = auto" % self.name)
        except Exception as e:
            raise e

    def start(self):
        status = self.status()
        if -1 == status:
            print("%s is uninstalled" % self.name)
            return
        elif 0 == status:
            print("%s is starting .." % self.name)
            return
        elif 1 == status:
            print("%s is running" % self.name)
            return
        elif 2 == status:
            print("%s is stopping .." % self.name)
            time.sleep(10)
            while 2 == self.status():
                time.sleep(10)

            print("Start service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc start %s" % self.name)
            print("%s startup success" % self.name)
        elif 3 == status:
            print("%s is stopped" % self.name)

            print("Start service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc start %s" % self.name)
            print("%s startup success" % self.name)
        else:
            print("%s is in other status" % self.name)

    def stop(self):
        status = self.status()
        if -1 == status:
            print("%s is uninstalled" % self.name)
            return
        elif 0 == status:
            print("%s is starting .." % self.name)
            time.sleep(10)
            while 0 == self.status():
                time.sleep(10)

            print("Stop Service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc stop %s" % self.name)
            print("%s shutdown success" % self.name)
        elif 1 == status:
            print("%s is running" % self.name)

            print("Stop Service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc stop %s" % self.name)
            print("%s shutdown success" % self.name)
        elif 2 == status:
            print("%s is stopping .." % self.name)
            return
        elif 3 == status:
            print("%s is stopped" % self.name)
            return
        else:
            print("%s is in other status" % self.name)

    def restart(self):
        status = self.status()
        if -1 == status:
            print("%s is uninstalled" % self.name)
            return
        elif 0 == status:
            print("%s is starting .." % self.name)
            return
        elif 1 == status:
            try:
                print("Stop Service ..")
                # 管理员权限运行CMD指令
                bat.runAdmin("sc stop %s" % self.name)
            except:
                print(str(traceback.format_exc()))

            print("Start service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc start %s" % self.name)
            print("%s startup success" % self.name)
        elif 2 == status:
            print("%s is stopping .." % self.name)
            time.sleep(10)
            while 2 == self.status():
                time.sleep(10)

            print("Start service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc start %s" % self.name)
            print("%s startup success" % self.name)
        elif 3 == status:
            print("%s is stopped" % self.name)

            print("Start service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("sc start %s" % self.name)
            print("%s startup success" % self.name)
        else:
            print("%s is in other status" % self.name)

    def remove(self):
        try:
            status = self.status()
            if -1 == status:
                print("%s is uninstalled" % self.name)
                return

            self.stop()

            print("Remove service ..")
            # 管理员权限运行CMD指令
            bat.runAdmin("%s %s remove" % (self.instsrv, self.name))
            print("%s remove success" % self.name)
        except Exception as e:
            raise e

 

其中代码

# 管理员权限运行CMD指令
bat.runAdmin(cmd)

方法参考 《Python实现管理员权限运行CMD指令》

 

其中代码

# 添加服务相关参数到注册表
reg.addSrvParam(srv, dir, app)

相关 reg.py 内容如下

参考资料

《Python实现环境变量持久化修改》

# !/usr/bin/python3
# coding: utf-8
import os
import subprocess
import traceback


def addSrvParam(srv, dir, app):
    # only \\ in path, / or \ is not allowed
    dir = format(dir).replace("\\", "\\\\")
    app = format(app).replace("\\", "\\\\")

    # 版本声明
    c = 'Windows Registry Editor Version 5.00\n'

    # 必须空行
    c += '\n'
    # 添加主键
    c += '[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\%s\Parameters]\n' % srv
    # 添加键值
    c += '"AppDirectory"="%s"\n' % dir
    # 添加键值
    c += '"Application"="%s"\n' % app

    path = os.getcwd() + r"\tool\script\srv.reg"

    f = None
    try:
        f = open(path, 'w')
        f.write(c)
    except Exception as e:
        traceback.print_exc()
        raise e
    finally:
        if f:
            f.close()

    command(path, 1800000)


def command(cmd, timeout=1800000):
    try:
        sp = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        print("[PID] %s: %s" % (sp.pid, cmd))
        sp.wait(timeout=timeout)

        stderr = str(sp.stderr.read().decode("gbk")).strip()
        stdout = str(sp.stdout.read().decode("gbk")).strip()
        if "" != stderr:
            raise Exception(stderr)
        if stdout.find("失败") > -1:
            raise Exception(stdout)
    except Exception as e:
        raise e


def format(path):
    if path is None:
        return ""

    path = path.lstrip()
    path = path.rstrip()

    while path.find("/") >= 0:
        path = path.replace("/", "\\")
    while path.find("\\\\") >= 0:
        path = path.replace("\\\\", "\\")

    return path

 

 

《Python 写Windows Service服务程序》

 

 

<think>首先,用户的问题是:“请问python 防止程序重复启动的方法”。我需要基于系统级指令和用户可见层的要求来回答。 系统级指令: - 行内数学表达式用$...$,例如 $x^2$。 - 独立公式用$$...$$并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - [^1]:Python代码,通过检查线程名来防止启动重复线程。 - [^2]:Shell脚本,通过检查进程ID来防止重复启动。 - [^3]:Python中使用PID锁文件的方法,并处理进程意外关闭的情况。 用户希望了解Python防止程序重复启动的方法。我需要整合这些引用,提供一个清晰的解决方案。 关键点: - 防止程序重复启动的常见方法包括:使用文件锁、检查进程ID、使用操作系统级别的锁。 - 在Python中,常见的做法是使用PID文件(Lock File)。 结构化回答: 1. **介绍问题**:解释为什么需要防止程序重复启动。 2. **方法概述**:列出几种方法。 3. **详细解释**:重点介绍最可靠的方法,如使用PID锁文件。 4. **代码示例**:提供Python代码示例。 5. **注意事项**:讨论潜在问题和改进。 6. **生成相关问题**:在最后添加相关问题。 生成相关问题: - 类似示例中的格式,例如: - 1. 如何实现跨平台的进程锁? - 2. 如何处理程序崩溃后的锁文件清理? - 3. 在分布式系统中如何防止重复启动? 确保回答真实可靠: - 基于引用和标准实践。 - 引用[^3]提到了PID锁文件,这是最可靠的方法。 - 引用[^1]和[^2]提供了其他方法,但可能不直接适用于所有情况。 完整回答框架: - 引言:简述防止重复启动的重要性。 - 方法1:使用PID锁文件(推荐)。 - 解释原理。 - 提供Python代码。 - 方法2:检查进程列表(类似引用[^1]和[^2])。 - 讨论优缺点。 - 方法3:使用操作系统提供的锁(如fcntl或msvcrt)。 - 总结:推荐最佳实践。 - 相关问题。 代码示例: - 基于引用[^3],实现PID锁文件。 - 包括检查进程是否存活。 引用标识: - 在相关段落末尾添加[^1], [^2], [^3]。 数学表达式:用户问题中没有数学内容,但如果有变量等,用$...$。例如,如果提到进程ID,可以用$PID$,但可能不必要。 最终回答用中文。</think>### Python 防止程序重复启动的方法 在开发Python应用程序时,防止程序被多次启动(即避免重复实例)是常见需求,尤其是在后台服务或定时任务中。如果程序重复启动,可能导致资源冲突、数据损坏或性能下降。本文将介绍几种可靠的方法,包括使用PID锁文件、检查进程列表和操作系统锁。所有方法都基于Python标准库或跨平台模块,确保真实可靠。 #### 1. **使用PID锁文件(推荐方法)** PID锁文件是最健壮的方案,它通过创建一个文件记录当前进程的ID(PID),并在启动时检查该文件是否有效(包括进程是否存活)。如果程序意外崩溃,锁文件能自动清理。 **原理**: - 程序启动时,检查锁文件是否存在。 - 如果存在,读取文件中的PID,并验证该PID对应的进程是否还在运行。 - 如果进程已终止,删除旧锁文件并创建新锁;否则,提示程序已在运行并退出。 - 使用`os`和`psutil`模块(第三方模块,需安装)实现进程检查。 **Python代码示例**: ```python import os import sys import psutil # 需安装: pip install psutil LOCK_FILE = "/tmp/my_program.pid" # 锁文件路径,可根据系统调整 def create_lock(): """创建PID锁文件""" pid = os.getpid() with open(LOCK_FILE, 'w') as f: f.write(str(pid)) def remove_lock(): """删除锁文件""" if os.path.exists(LOCK_FILE): os.unlink(LOCK_FILE) def is_program_running(): """检查锁文件对应的进程是否存活""" if not os.path.exists(LOCK_FILE): return False try: with open(LOCK_FILE, 'r') as f: pid = int(f.read().strip()) # 检查PID是否在运行 return psutil.pid_exists(pid) and psutil.Process(pid).name() == "python" except (ValueError, psutil.NoSuchProcess): return False if __name__ == "__main__": if is_program_running(): print("程序已在运行,请勿重复启动!") sys.exit(1) try: create_lock() # 主程序逻辑 print("程序启动成功,执行中...") # 示例:模拟长时间运行 import time time.sleep(30) finally: remove_lock() # 确保程序退出时删除锁 ``` **优点**:跨平台(Linux/Windows/macOS),处理意外崩溃可靠[^3]。 **缺点**:需安装`psutil`;如果程序被强制终止(如kill -9),锁文件可能残留,需手动清理或添加定时任务。 #### 2. **检查进程列表(轻量级方法)** 通过扫描系统进程列表,判断当前程序是否已运行。适用于简单场景,但不如PID锁文件健壮。 **原理**: - 使用`psutil`或`subprocess`检查所有Python进程的命令行参数。 - 如果找到匹配的进程名或参数,则判定程序已启动。 **Python代码示例**(基于引用[^1]优化): ```python import psutil import sys PROGRAM_NAME = "my_program.py" # 程序文件名 def is_instance_running(): """检查是否有相同程序的进程在运行""" for proc in psutil.process_iter(['name', 'cmdline']): try: # 检查进程命令行是否包含程序名 if 'python' in proc.info['name'].lower() and any(PROGRAM_NAME in arg for arg in proc.info['cmdline']): if proc.pid != os.getpid(): # 排除当前进程自身 return True except (psutil.NoSuchProcess, psutil.AccessDenied): continue return False if __name__ == "__main__": if is_instance_running(): print("程序已在运行,请勿重复启动!") sys.exit(1) print("程序启动成功,执行中...") # 主程序逻辑 ``` **优点**:无需额外文件,实现简单。 **缺点**:进程名可能冲突(如多个Python脚本);Windows兼容性较差;不处理崩溃残留[^2]。 #### 3. **使用操作系统锁(高级方法)** 利用操作系统提供的文件锁(如Linux的`fcntl`或Windows的`msvcrt`),在文件级别加锁。 **原理**: - 打开一个文件并尝试获取独占锁。 - 如果锁已被占用,说明程序已在运行。 **Python代码示例**(跨平台实现): ```python import os import sys import fcntl # Linux/macOS try: import msvcrt # Windows except ImportError: pass LOCK_FILE = "/tmp/my_program.lock" # 锁文件路径 def acquire_lock(): """获取文件锁""" f = open(LOCK_FILE, 'w') try: if os.name == 'posix': # Linux/macOS fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) else: # Windows msvcrt.locking(f.fileno(), msvcrt.LK_NBLCK, 1) return f except (BlockingIOError, OSError): f.close() return None if __name__ == "__main__": lock_file = acquire_lock() if not lock_file: print("程序已在运行,请勿重复启动!") sys.exit(1) try: print("程序启动成功,执行中...") # 主程序逻辑 import time time.sleep(30) finally: if lock_file: lock_file.close() # 释放锁 ``` **优点**:高效,操作系统级别保障。 **缺点**:Windows实现复杂;程序崩溃时锁可能不释放(需结合PID检查)[^3]。 ### 总结与建议 - **推荐方法**:优先使用PID锁文件(方法1),它结合了文件锁和进程检查,健壮性最高。 - **注意事项**: - 在分布式系统中,需使用分布式锁(如Redis或ZooKeeper)。 - 定期清理旧锁文件(例如在程序启动添加超时检查)。 - 测试不同操作系统:Linux/macOS使用`fcntl`,Windows需适配。 通过以上方法,您可以有效防止Python程序重复启动,确保资源安全和性能稳定[^1][^2][^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值