os.system可以启动但不能杀死Python进程

时间:2018-03-14 05:01:55

标签: python flask

我无法阻止python脚本使用os.system()运行,但我能够使用os.system()运行它。当我在终端上运行相同的命令来终止进程时,它可以工作。

我有web_plants.py脚本中的代码片段,使用Flask在html中部署它:

@app.route("/auto/water/<toggle>")
def auto_water(toggle):
    running = False
    if toggle == "ON":
        templateData = template(text = "Auto Watering On")
        for process in psutil.process_iter():
            try:
                if process.cmdline()[1] == 'auto_water.py':
                    templateData = template(text = "Already running")
                    running = True
            except:
                pass
        if not running:
            os.system("python auto_water.py")
    else:
        templateData = template(text = "Auto Watering Off")
        os.system("pkill -f water.py")

    return render_template('main.html', **templateData)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

我有一个water.py脚本,其中包含html页面上按钮的auto_water功能。但功能很好,所以我认为这不是问题。

def auto_water(pump_pin = 38):
    init_output(pump_pin)
    print("Reading Soil Humidity values from ADS1115")

    try:
        while True:
            m = adc.read_adc(0, gain=GAIN)
            print('Moisture Level:{0:>6}'.format(m))
            time.sleep(1)
            if m>23000: 
                #GPIO.output(pump_pin, GPIO.LOW)
                print "relay on"
            else:
                #GPIO.output(pump_pin, GPIO.HIGH)
                print "relay off"
    except KeyboardInterrupt:
        GPIO.cleanup()
    except Exception as e:
        print (e)

我有这个auto_water.py文件导入water并调用auto_water()函数来运行它。使用此文件的目的是当我点击&#34;停止&#34;时,能够在文件运行时终止该文件。按钮。

import water

#From the water.py script, call the auto_water function and run it as a process

if __name__ == "__main__":
    water.auto_water()

脚本一直在运行,我必须使用终端中的sudo pkill -f water.py将其终止,以使其停止。但是同样的命令对web_plants.py文件不起作用,当我按下&#34;停止&#34;按钮。然而,当我杀了它时,网页会注册它并显示文本&#34; Auto Watering OFF&#34;如web_plants.py文件中的以下行:

templateData = template(text="Auto Watering Off")
os.system("pkill -f water.py") 

3 个答案:

答案 0 :(得分:2)

您可以使用os.kill()吗?

import os
import signal

os.kill(pid, signal.SIGTERM)

如果您没有pid随时可用,请使用subprocess.check_output获取它。 (如果您正在使用Python 3.5 +,请使用subprocess.run。)

import os
import signal
import subprocess

def get_water_pid():
    return int(subprocess.check_output(['pgrep', '-f', 'water.py']).strip())

def kill_water_process():
    pid = get_water_pid()
    os.kill(pid, signal.SIGTERM)

答案 1 :(得分:2)

假设脚本只是由您的服务器启动,您可以在启动时存储脚本的PID,然后使用它来终止它。

看起来您的现有代码没有在后台启动脚本;它会让Flask线程永远等待os.system返回。这有点奇怪,但我现在也会做同样的事情。

另外,我只会编写裸机函数,您可以根据需要进行Flask,并将PID存储在全局中,您可以将其替换为您正在使用的任何持久存储。

autowater_pid = None

def start():
    global pid
    p = subprocess.Popen(["python", "auto_water.py"])
    pid = p.pid
    p.wait()

def stop():
    global pid
    if pid is not None:
        os.kill(pid)
        pid = None

您可以通过多种方式对此进行扩展。例如,如果我们已经有pid,我们可以跳过开始新副本。或者我们甚至可以检查它是否仍在运行,并且如果它以某种方式死亡则开始一个新的:

def start():
    global pid
    if pid is not None:
        try:
            os.kill(pid, 0) # check that it's still running
            return
        except ProcessLookupError:
            pass
    p = subprocess.Popen(["python", "auto_water.py"])
    pid = p.pid
    p.wait()

但回到那个问题,等待过程完成。你不仅要永久地绑定一个线程,你还要做到这一点,如果Flask进程因任何原因(升级,负载均衡器,未处理的异常,等等)发生故障,脚本将被操作系统杀死。您应该考虑以某种方式守护脚本。这是一个太大的主题,无法包含在答案的旁注中,但您应该能够从搜索开始找到足够的信息(或者,至少,提出一个很好的单独问题来询问SO)。

另一种替代方法 - 守护进程更常用的方法 - 是让脚本本身(或守护程序包装器)在启动时将自己的pid写入已知位置的文件。像这样:

with open('/var/run/auto_water.pid', 'w') as f:
    f.write(f'{os.getpid()}\n')

退出时可以os.remove('/var/run/auto_water.pid')

然后您的服务器可以从文件中读取该数字以了解要杀死的PID。同样,你可以输入代码来处理进程死亡的情况,而不会删除它的pidfile等。

但是,为了正确地做到这一点,你将要阅读锁定pidfiles,处理意外现有文件的正确行为等。

或者,更好的是,只需从PyPI获取一个pid文件处理库。

或者,甚至可能更好,获取一个守护程序处理库来执行守护程序和pidfile以及错误处理。

答案 2 :(得分:1)

系统调用阻止执行。在执行完成之前,该函数不会将任何内容返回给浏览器。

试试这个:

os.system("python auto_water.py&") 

&安培;产生过程并立即返回。