Python betiğinin çalışıp çalışmadığını kontrol edin


101

Web uygulamamın bir parçası olarak çalışan bir python arka plan programım var / Arka plan programımın çalışıp çalışmadığını nasıl hızlı bir şekilde kontrol edebilirim (python kullanarak) ve değilse nasıl başlatabilirim?

Daemon'un herhangi bir çökmesini düzeltmek için bunu bu şekilde yapmak istiyorum ve böylece komut dosyasının manuel olarak çalıştırılması gerekmez, çağrılır çağrılmaz otomatik olarak çalışacak ve ardından çalışmaya devam edecektir.

Komut dosyamın çalışıp çalışmadığını nasıl kontrol edebilirim (python kullanarak)?


İşleminizin de diğer işlemlerinizi python'da yazılı tutmayacağından emin misiniz?
ojblass

Tendo'ya gidin, betiğinizin tek bir örneğini oluşturur, bu nedenle betik zaten çalışıyorsa çalışmaz. github.com/pycontribs/tendo
JasTonAChair

Bu sizin artalan sürecinizin işi değildir, bu, arka plan programınızı başlatan "üst" uygulamanın işidir. Systemd veya süpervizör gibi başka bir araç kullanın. Bir dosyaya yazılmış bir pid'e güvenmeyin. Systemd / supervisord kullanamazsanız, iki kez çalıştırılmayacağından emin olmak için kilitlemeyi kullanın.
guettli

Yanıtlar:


93

Bir yere bir pidfile bırakın (örneğin / tmp). Ardından, dosyadaki PID'nin mevcut olup olmadığını kontrol ederek işlemin çalışıp çalışmadığını kontrol edebilirsiniz. Temiz bir şekilde kapattığınızda dosyayı silmeyi ve başlattığınızda kontrol etmeyi unutmayın.

#/usr/bin/env python

import os
import sys

pid = str(os.getpid())
pidfile = "/tmp/mydaemon.pid"

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
file(pidfile, 'w').write(pid)
try:
    # Do some actual work here
finally:
    os.unlink(pidfile)

Daha sonra /tmp/mydaemon.pid içeriğinin mevcut bir işlem olup olmadığını kontrol ederek işlemin çalışıp çalışmadığını kontrol edebilirsiniz. Monit (yukarıda bahsedilmiştir) bunu sizin için yapabilir veya ps'den dönüş kodunu kullanarak sizin için kontrol etmek için basit bir kabuk betiği yazabilirsiniz.

ps up `cat /tmp/mydaemon.pid ` >/dev/null && echo "Running" || echo "Not running"

Ekstra kredi için, programınızın her koşulda (öldürüldüğünde, istisnalar ortaya çıktığında, vb.) Pid dosyasını temizlediğinden emin olmak için atexit modülünü kullanabilirsiniz.


7
program bozulmuşsa, os.unlink () çalışmaz ve dosya var olduğu için program tekrar çalışmaz. sağ ?
Yuda Prawira

2
Doğru, ancak bu beklenen bir davranış olabilir. Pidfile mevcutsa ancak içindeki PID çalışmıyorsa, bu sorunsuz olmayan bir kapatmayı gösterir, bu da uygulamanın kilitlendiği anlamına gelir. Bu, bir sorun olduğunu bilmenizi ve günlükleri kontrol etmenizi sağlar. Belirtildiği gibi atexit modülü, hatanın Python yorumlayıcısının kendisinde olmadığını varsayarak bununla da ilgilenebilir.
Dan Udey

7
Basit bir çözüm olmasına rağmen, bu bir yarış durumuna karşı hassastır. Betiğin iki örneği aynı anda çalıştırılırsa, if os.path.isfile(pidfile)her ikisi için de false olarak değerlendirilebilir, bu da onların hem kilit dosyasını yazmasına hem de çalışmaya devam etmesine neden olur.
Cerin

6
pids ayrıca işletim sistemi tarafından yeniden kullanılır. Yani yanlış pozitifler mümkündür.
aychedee

12
Bunu şimdi bulanlar için, python 3'te file()kaldırıldığını ve open()bunun yerine kullanmanız gerektiğini unutmayın . Ayrıca, 2.7'de olsanız bile kullanmalısınız open()üzerinde file(): burada açıklandığı gibi docs.python.org/2/library/functions.html#file (Ve geri etrafında 2.2 piton kullanılırsa evet, resmi danışma tersi oldu. Görünüşe göre fikirlerini değiştirmişler.)
jpk

155

Bir Linux sisteminde kullanışlı olan bir teknik, etki alanı soketlerini kullanmaktır:

import socket
import sys
import time

def get_lock(process_name):
    # Without holding a reference to our socket somewhere it gets garbage
    # collected when the function exits
    get_lock._lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)

    try:
        # The null byte (\0) means the socket is created 
        # in the abstract namespace instead of being created 
        # on the file system itself.
        # Works only in Linux
        get_lock._lock_socket.bind('\0' + process_name)
        print 'I got the lock'
    except socket.error:
        print 'lock exists'
        sys.exit()


get_lock('running_test')
while True:
    time.sleep(3)

Atomiktir ve işleminiz bir SIGKILL gönderilirse kilit dosyalarının ortalıkta olması sorununu ortadan kaldırır.

Sen edebilirsiniz belgelerinde okumaksocket.close çöp toplama o yuva otomatik olarak kapatılır.


21
Gelecekteki Google çalışanları için bir not: Bu kod, Linux'a özgü (genel olarak posix değil) "soyut soketler" kullanır. Bununla
georg

6
Bu harika ve geriye kalan aptal dosyalar bırakmıyor. Keşke buna daha fazla oy verebilseydim.
Hiro2k

4
Harika. Ama lock_socket neden global olarak tanımlandı merak ediyorum. Test ettim ve lock_socket global olarak tanımlanmadıysa, kilitleme sistemi birden fazla işlemi çalıştırırken çalışmıyor. Neden? lock_socket tanımlıdır ve sadece get_lock fonksiyonunda kullanılır. Neden küresel tanımlanması gerekiyor?
Alptugay

7
Bunu yazmayalı epey oldu ... ve hafızam bulanık. Ama bence çöpleri topladığı ve aksi takdirde soket kapandığı için. Bunun gibi bir şey.
aychedee

8
Boş bayt ( \0), soketin dosya sistemi üzerinde yaratılmak yerine soyut ad alanında yaratıldığı anlamına gelir.
aychedee

23

Pid kütüphane tam olarak bunu yapabilirsiniz.

from pid import PidFile

with PidFile():
  do_something()

Ayrıca, pidfile'ın var olduğu ancak işlemin çalışmadığı durumu da otomatik olarak ele alır.


Bu GÜZEL ÇALIŞIR. Ubuntu'da çalışması için sadece root olarak çalıştırılması gerekiyor. +1
Jimmy

11
@Jimmy, örneğin with PidFile(piddir='/home/user/run/'), pid dosyasını izinlere sahip olduğunuz yere koymak için farklı bir dizin kullanmak için yapabilirsiniz . O zaman root olarak çalıştırmanıza gerek yok
Decko

Burada açıklandığı gibi geçici dizini kullanmanın piddir için iyi bir seçenek olacağını düşünüyorum.
Rishi Latchmepersad

@RishiLatchmepersad gettempdir kullanmak iyi bir fikir değildir, çünkü bu, her çağrıda pid kontrolünü bozacak benzersiz bir dizin verecektir. Komut dosyası her çalıştığında dizinin aynı olması gerekir.
Decko

Teşekkürler! İstisna ortaya çıksa bile iyi çalışır.
Zhou Hongbo

11

Elbette Dan'den alınan örnek olması gerektiği gibi çalışmayacak.

Aslında, komut dosyası çökerse, bir istisna oluşturursa veya pid dosyasını temizlemezse, komut dosyası birden çok kez çalıştırılacaktır.

Aşağıdakileri başka bir web sitesinden öneriyorum:

Bu, zaten mevcut bir kilit dosyası olup olmadığını kontrol etmek içindir.

\#/usr/bin/env python
import os
import sys
if os.access(os.path.expanduser("~/.lockfile.vestibular.lock"), os.F_OK):
        #if the lockfile is already there then check the PID number
        #in the lock file
        pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "r")
        pidfile.seek(0)
        old_pid = pidfile.readline()
        # Now we check the PID from lock file matches to the current
        # process PID
        if os.path.exists("/proc/%s" % old_pid):
                print "You already have an instance of the program running"
                print "It is running as process %s," % old_pid
                sys.exit(1)
        else:
                print "File is there but the program is not running"
                print "Removing lock file for the: %s as it can be there because of the program last time it was run" % old_pid
                os.remove(os.path.expanduser("~/.lockfile.vestibular.lock"))

Bu, kilit dosyasına bir PID dosyası koyduğumuz kodun bir parçasıdır.

pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "w")
pidfile.write("%s" % os.getpid())
pidfile.close()

Bu kod, çift yürütmeden kaçınarak, mevcut çalışan işleme kıyasla pid değerini kontrol edecektir.

Umarım yardımcı olur.


3
os.kill(old_pid, 0)UNIX'ler arasında daha taşınabilir olması gereken biri kullanılmalıdır . OSErrorBöyle bir PID yoksa veya farklı bir kullanıcıya aitse yükselecektir .
drdaeman

1
Bir işlemi kontrol etmek için / proc / <pid> kullanmanın son derece taşınabilir olmadığını ve yalnızca Linux üzerinde güvenilir bir şekilde çalışacağını unutmayın.
Dan Udey

10

UNIX'te işlemleri yeniden başlatmak için çok iyi paketler var. Oluşturma ve yapılandırma hakkında harika bir öğreticiye sahip olanlardan biri de monit . Bazı ince ayarlarla, arka plan programınızı devam ettiren sağlam ve kanıtlanmış bir teknolojiye sahip olabilirsiniz.


Ben ölürse yeniden başlatmayı dahil uygulamanızı Artalanda yolları ton vs vs çalışmıyorsa eğer başlatılması vardır, tekerleği yeniden icat değil, anlaşmak
Davr

8

Çözümüm, Windows ve ubuntu linux üzerinde test edilen işlem ve komut satırı argümanlarını kontrol etmektir.

import psutil
import os

def is_running(script):
    for q in psutil.process_iter():
        if q.name().startswith('python'):
            if len(q.cmdline())>1 and script in q.cmdline()[1] and q.pid !=os.getpid():
                print("'{}' Process is already running".format(script))
                return True

    return False


if not is_running("test.py"):
    n = input("What is Your Name? ")
    print ("Hello " + n)

@Nst yanıtının yanında, bu daha iyi yanıttır.
shgnInc

5

Sayısız seçenek var. Bir yöntem, sizin için bu tür çağrıları gerçekleştiren sistem çağrılarını veya python kitaplıklarını kullanmaktır. Diğeri ise basitçe aşağıdaki gibi bir süreci ortaya çıkarmaktır:

ps ax | grep processName

ve çıktıyı ayrıştırın. Pek çok insan bu yaklaşımı seçiyor, bence bu mutlaka kötü bir yaklaşım değil.


processName betiğimin dosya adını içerir mi?
Josh Hunt

bu süreci nasıl başlattığınıza bağlıdır
ojblass

örneğin: ps ax | grep python
Kullanıcı

3

Kendim de çözüm arayan bu eski soruyla karşılaştım.

Psutil kullanın :

import psutil
import sys
from subprocess import Popen

for process in psutil.process_iter():
    if process.cmdline() == ['python', 'your_script.py']:
        sys.exit('Process found: exiting.')

print('Process not found: starting it.')
Popen(['python', 'your_script.py'])

Bu komut dosyası sudo olarak çalıştırılmalıdır, aksi takdirde erişim reddedildi hatası alırsınız.
DoesData

Ayrıca, listedeki gibi komuttan komut dosyanıza argümanlar iletirseniz, tüm bu argümanlar da olacaktır.
DoesData

2

Şeytanları yönetmek için büyük bir Süpervizör hayranıyım . Python'da yazılmıştır, bu yüzden Python ile nasıl etkileşimde bulunulacağına veya Python'dan nasıl genişletileceğine dair birçok örnek vardır. Amaçlarınız için XML-RPC işlem kontrol API'sı iyi çalışmalıdır.


2

Bu diğer sürümü deneyin

def checkPidRunning(pid):        
    '''Check For the existence of a unix pid.
    '''
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

# Entry point
if __name__ == '__main__':
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp", __program__+".pid")

    if os.path.isfile(pidfile) and checkPidRunning(int(file(pidfile,'r').readlines()[0])):
            print "%s already exists, exiting" % pidfile
            sys.exit()
    else:
        file(pidfile, 'w').write(pid)

    # Do some actual work here
    main()

    os.unlink(pidfile)

1

Kendi PID dosya çözümünüzü geliştirmek yerine (düşündüğünüzden daha fazla incelik ve köşe durumu vardır), süpervizöre bir göz atın - bu, iş kontrolünü ve arka plan programı davranışlarını mevcut bir Python etrafına sarmayı kolaylaştıran bir süreç kontrol sistemidir. senaryo.



0
ps ax | grep processName

pycharm'da hata ayıklama betiği varsa daima çıkılır

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName

0

bunu dene:

#/usr/bin/env python
import os, sys, atexit

try:
    # Set PID file
    def set_pid_file():
        pid = str(os.getpid())
        f = open('myCode.pid', 'w')
        f.write(pid)
        f.close()

    def goodby():
        pid = str('myCode.pid')
        os.remove(pid)

    atexit.register(goodby)
    set_pid_file()
    # Place your code here

except KeyboardInterrupt:
    sys.exit(0)

0

İşte daha kullanışlı kod (tam olarak python'un komut dosyasını çalıştırıp çalıştırmadığını kontrol ederek):

#! /usr/bin/env python

import os
from sys import exit


def checkPidRunning(pid):
    global script_name
    if pid<1:
        print "Incorrect pid number!"
        exit()
    try:
        os.kill(pid, 0)
    except OSError:
        print "Abnormal termination of previous process."
        return False
    else:
        ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)
        process_exist = os.system(ps_command)
        if process_exist == 0:
            return True
        else:
            print "Process with pid %s is not a Python process. Continue..." % pid
            return False


if __name__ == '__main__':
    script_name = os.path.basename(__file__)
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp/", script_name+".pid")
    if os.path.isfile(pidfile):
        print "Warning! Pid file %s existing. Checking for process..." % pidfile
        r_pid = int(file(pidfile,'r').readlines()[0])
        if checkPidRunning(r_pid):
            print "Python process with pid = %s is already running. Exit!" % r_pid
            exit()
        else:
            file(pidfile, 'w').write(pid)
    else:
        file(pidfile, 'w').write(pid)

# main programm
....
....

os.unlink(pidfile)

İşte dize:

ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)

"grep" başarılıysa ve "python" işlemi şu anda bir parametre olarak komut dosyanızın adı ile çalışıyorsa 0 döndürür.


0

Sadece bir işlem adı arıyorsanız basit bir örnek var veya yok:

import os

def pname_exists(inp):
    os.system('ps -ef > /tmp/psef')
    lines=open('/tmp/psef', 'r').read().split('\n')
    res=[i for i in lines if inp in i]
    return True if res else False

Result:
In [21]: pname_exists('syslog')
Out[21]: True

In [22]: pname_exists('syslog_')
Out[22]: False

-1

Sorununuzu çözmek için aşağıdaki örneği düşünün:

#!/usr/bin/python
# -*- coding: latin-1 -*-

import os, sys, time, signal

def termination_handler (signum,frame):
    global running
    global pidfile
    print 'You have requested to terminate the application...'
    sys.stdout.flush()
    running = 0
    os.unlink(pidfile)

running = 1
signal.signal(signal.SIGINT,termination_handler)

pid = str(os.getpid())
pidfile = '/tmp/'+os.path.basename(__file__).split('.')[0]+'.pid'

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
else:
    file(pidfile, 'w').write(pid)

# Do some actual work here

while running:
  time.sleep(10)

Bu betiği öneririm çünkü sadece bir kez çalıştırılabilir.


-1

Geçerli betiğin adıyla bir işlem aramak için bash kullanma. Fazladan dosya yok.

import commands
import os
import time
import sys

def stop_if_already_running():
    script_name = os.path.basename(__file__)
    l = commands.getstatusoutput("ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'" % script_name)
    if l[1]:
        sys.exit(0);

Test etmek için ekleyin

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)

Fazladan dosya yok ama 6 ekstra işlem mi?
Alois Mahdal

2
Ya ben ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello've o şeyi çalıştırırsam? ;)
Alois Mahdal

Ben anlamıyorum ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'yapıyor. İsme göre bir işlem aramanız gerekiyorsa neden kullanmıyorsunuz pgrep? Amacı nedir awk '{print $2}'| awk '{print $2}'? Genel olarak, sınırlayıcıyı değiştirmediğiniz sürece bu şekilde arka arkaya iki kez awk çalıştıramazsınız. İlk awk, PID sütunuyla sonuçlanır ... İkinci awk hiçbir sonuç vermez.
Altı

-1

Zaten çalışıyorsa bir komut dosyası başlatmamak için Linux'ta kullandığım şey bu:

import os
import sys


script_name = os.path.basename(__file__)
pidfile = os.path.join("/tmp", os.path.splitext(script_name)[0]) + ".pid"


def create_pidfile():
    if os.path.exists(pidfile):
        with open(pidfile, "r") as _file:
            last_pid = int(_file.read())

        # Checking if process is still running
        last_process_cmdline = "/proc/%d/cmdline" % last_pid
        if os.path.exists(last_process_cmdline):
            with open(last_process_cmdline, "r") as _file:
                cmdline = _file.read()
            if script_name in cmdline:
                raise Exception("Script already running...")

    with open(pidfile, "w") as _file:
        pid = str(os.getpid())
        _file.write(pid)


def main():
    """Your application logic goes here"""


if __name__ == "__main__":
    create_pidfile()
    main()

Bu yaklaşım, harici bir modüle herhangi bir bağımlılık olmadan iyi çalışır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.