Periksa untuk melihat apakah skrip python sedang berjalan

101

Saya memiliki daemon python yang berjalan sebagai bagian dari aplikasi web saya / Bagaimana saya dapat dengan cepat memeriksa (menggunakan python) apakah daemon saya sedang berjalan dan, jika tidak, meluncurkannya?

Saya ingin melakukannya dengan cara itu untuk memperbaiki error apa pun pada daemon, sehingga skrip tidak harus dijalankan secara manual, skrip akan berjalan secara otomatis segera setelah dipanggil dan kemudian tetap berjalan.

Bagaimana cara memeriksa (menggunakan python) jika skrip saya berjalan?

Josh Hunt
sumber
Apakah Anda yakin tidak ingin proses Anda terlalu membuat proses lainnya tetap ditulis dalam python?
ojblass
Cobalah Tendo, buat instance tunggal dari skrip Anda, oleh karena itu skrip tidak akan berjalan jika sudah berjalan. github.com/pycontribs/tendo
JasTonAChair
Ini bukan tugas daemon Anda, ini adalah tugas dari aplikasi "atas" yang meluncurkan daemon Anda. Gunakan systemd atau alat lain seperti supervisord. Jangan mengandalkan pid yang ditulis ke file. Jika Anda tidak dapat menggunakan systemd / supervisord, gunakan penguncian untuk tidak memastikan itu tidak dijalankan dua kali.
guettli

Jawaban:

92

Jatuhkan pidfile di suatu tempat (misalnya / tmp). Kemudian Anda dapat memeriksa untuk melihat apakah proses sedang berjalan dengan memeriksa apakah PID dalam file tersebut ada. Jangan lupa untuk menghapus file saat Anda mematikannya dengan bersih, dan periksa saat Anda memulai.

#/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)

Kemudian Anda dapat memeriksa apakah proses sedang berjalan dengan memeriksa apakah konten /tmp/mydaemon.pid adalah proses yang sudah ada. Monit (disebutkan di atas) dapat melakukan ini untuk Anda, atau Anda dapat menulis skrip shell sederhana untuk memeriksanya menggunakan kode pengembalian dari ps.

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

Untuk kredit tambahan, Anda dapat menggunakan modul atexit untuk memastikan bahwa program Anda membersihkan pidfile-nya dalam keadaan apa pun (saat dimatikan, pengecualian dimunculkan, dll.).

Dan Udey
sumber
6
jika program telah rusak, os.unlink () tidak akan dijalankan dan program tidak akan berjalan lagi, karena file tersebut ada. Baik ?
Yuda Prawira
2
Benar, bagaimanapun ini mungkin perilaku yang diharapkan. Jika pidfile ada tetapi PID di dalamnya tidak berjalan, itu menunjukkan non-graceful shutdown, yang berarti aplikasi crash. Itu memberi tahu Anda bahwa ada masalah, dan untuk memeriksa log. Seperti disebutkan, modul atexit juga dapat menangani ini, dengan asumsi bug tidak ada dalam interpreter Python itu sendiri.
Dan Udey
7
Meski solusi sederhana, hal ini rentan terhadap kondisi balapan. Jika dua contoh skrip dieksekusi pada waktu yang hampir bersamaan, mungkin saja itu if os.path.isfile(pidfile)bernilai false untuk keduanya, menyebabkan keduanya menulis file kunci dan terus berjalan.
Cerin
6
pids juga digunakan kembali oleh sistem operasi. Jadi positif palsu mungkin saja terjadi.
aychedee
12
Bagi mereka yang menemukan ini sekarang, perhatikan bahwa di python 3 file()telah dihapus dan Anda harus menggunakan open()sebagai gantinya. Selain itu, bahkan jika Anda menggunakan versi 2.7 Anda harus menggunakan open()over file()seperti yang dijelaskan di sini: docs.python.org/2/library/functions.html#file (Dan ya, jika Anda menggunakan python kembali sekitar 2.2, saran resmi adalah sebaliknya. Rupanya mereka berubah pikiran.)
jpk
154

Sebuah teknik yang berguna pada sistem Linux menggunakan soket domain:

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 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)

Ini adalah atom dan menghindari masalah memiliki file kunci tergeletak di sekitar jika proses Anda dikirim SIGKILL

Anda dapat membaca di dokumentasi untuksocket.close soket itu secara otomatis ditutup saat sampah dikumpulkan.

aychedee
sumber
20
Catatan untuk calon karyawan Google: kode ini menggunakan "soket abstrak", yang khusus untuk Linux (bukan posix pada umumnya). Lebih lanjut tentang ini: blog.eduardofleury.com/archives/2007/09/13
georg
6
Ini luar biasa, dan tidak meninggalkan file bodoh yang tertinggal. Seandainya saya bisa lebih suka ini.
Hiro2k
4
Hebat. Tapi saya bertanya-tanya mengapa lock_socket didefinisikan secara global. Saya menguji dan jika lock_socket tidak didefinisikan secara global, sistem penguncian tidak berfungsi saat menjalankan banyak proses. Mengapa? lock_socket didefinisikan dan hanya digunakan dalam fungsi get_lock. Mengapa harus didefinisikan secara global?
Alptugay
7
Sudah lama sejak saya menulis ini ... dan ingatan saya kabur. Tapi saya pikir itu karena sampah dikumpulkan dan soket ditutup sebaliknya. Sesuatu seperti itu.
aychedee
8
Byte null ( \0) berarti soket dibuat di namespace abstrak alih-alih dibuat di sistem file itu sendiri.
aychedee
22

The pid perpustakaan dapat melakukan hal ini.

from pid import PidFile

with PidFile():
  do_something()

Ini juga akan secara otomatis menangani kasus di mana pidfile ada tetapi prosesnya tidak berjalan.

Decko
sumber
Ini bekerja dengan INDAH. Itu hanya harus dijalankan sebagai root agar bisa berjalan di Ubuntu. +1
Jimmy
11
@Jimmy dapat Anda lakukan, misalnya with PidFile(piddir='/home/user/run/')menggunakan direktori yang berbeda untuk meletakkan file pid di mana Anda memiliki izin. Maka Anda tidak perlu menjalankannya sebagai root
Decko
Saya berpikir bahwa menggunakan direktori temp seperti yang dijelaskan di sini akan menjadi pilihan yang baik untuk piddir.
Rishi Latchmepersad
@RishiLatchmepersad Menggunakan gettempdir bukanlah ide yang bagus karena akan memberikan direktori unik pada setiap panggilan yang akan merusak pemeriksaan pid. Direktori harus sama setiap kali skrip dijalankan.
Decko
11

Tentu saja contoh dari Dan tidak akan berfungsi sebagaimana mestinya.

Memang jika script crash, memunculkan eksepsi, atau tidak membersihkan file pid, script tersebut akan dijalankan berkali-kali.

Saya menyarankan yang berikut ini berdasarkan dari situs web lain:

Ini untuk memeriksa apakah sudah ada file kunci

\#/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"))

Ini adalah bagian dari kode tempat kami meletakkan file PID di file kunci

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

Kode ini akan memeriksa nilai pid dibandingkan dengan proses yang sedang berjalan, menghindari eksekusi ganda.

Saya harap ini akan membantu.

Shylock
sumber
3
Satu harus menggunakan os.kill(old_pid, 0), yang seharusnya lebih portabel di UNIX. Ini akan meningkat OSErrorjika tidak ada PID seperti itu atau milik pengguna yang berbeda.
drdaeman
1
Ketahuilah bahwa menggunakan / proc / <pid> untuk memeriksa suatu proses sangat tidak portabel dan hanya akan berfungsi dengan baik di Linux.
Dan Udey
10

Ada paket yang sangat bagus untuk memulai ulang proses di UNIX. Salah satu yang memiliki tutorial bagus tentang membangun dan mengonfigurasinya adalah monit . Dengan beberapa tweaking Anda dapat memiliki teknologi yang terbukti kokoh menjaga daemon Anda.

ojblass
sumber
Saya setuju, jangan menemukan kembali roda, ada banyak cara untuk membuat daemonisasi aplikasi Anda termasuk memulai ulang jika mati, meluncurkan jika tidak berjalan, dll.
davr
9

Solusi saya adalah memeriksa proses dan argumen baris perintah Diuji pada windows dan ubuntu linux

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)
kabapy
sumber
Selain jawaban @nst, ini adalah jawaban yang lebih baik.
shgnInc
ini cara terbaik! thx
Hadi hashemi
5

Ada banyak sekali pilihan. Salah satu metode adalah menggunakan panggilan sistem atau pustaka python yang melakukan panggilan seperti itu untuk Anda. Cara lainnya adalah dengan menghasilkan proses seperti:

ps ax | grep processName

dan parsing hasilnya. Banyak orang memilih pendekatan ini, menurut saya pendekatan ini tidak selalu buruk.

BobbyShaftoe
sumber
apakah processName akan menyertakan nama file skrip saya?
Josh Hunt
itu tergantung bagaimana Anda memulai proses Anda
ojblass
misalnya: ps ax | grep python
Pengguna
3

Menemukan sendiri pertanyaan lama ini mencari solusi.

Gunakan psutil :

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'])
NST
sumber
Skrip ini harus dijalankan sebagai sudo atau Anda akan mendapatkan kesalahan akses ditolak.
DoesData
Juga jika Anda memberikan argumen ke skrip Anda dari perintah seperti daftar juga akan memiliki semua argumen itu.
DoesData
2

Saya penggemar berat Supervisor untuk mengelola daemon. Ini ditulis dengan Python, jadi ada banyak contoh bagaimana berinteraksi dengan atau memperluasnya dari Python. Untuk tujuan Anda, API kontrol proses XML-RPC harus bekerja dengan baik.

Matt Bagus
sumber
2

Coba versi lain ini

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)
debuti
sumber
1

Daripada mengembangkan solusi file PID Anda sendiri (yang memiliki lebih banyak kehalusan dan kasus sudut daripada yang Anda kira), lihat supervisord - ini adalah sistem kontrol proses yang memudahkan untuk membungkus kontrol pekerjaan dan perilaku daemon di sekitar Python yang ada naskah.

Chris Johnson
sumber
0

Jawaban lainnya bagus untuk hal-hal seperti cron job, tetapi jika Anda menjalankan daemon, Anda harus memantaunya dengan sesuatu seperti daemontools .

bobpoekert.dll
sumber
0
ps ax | grep processName

jika skrip debug Anda di pycharm selalu keluar

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName
pengguna3366072
sumber
0

coba ini:

#/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)
MrRolling
sumber
0

Berikut adalah kode yang lebih berguna (dengan memeriksa apakah sebenarnya python mengeksekusi skrip):

#! /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)

Ini stringnya:

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

mengembalikan 0 jika "grep" berhasil, dan proses "python" sedang berjalan dengan nama skrip Anda sebagai parameter.

Dmitry Allyanov
sumber
0

Contoh sederhana jika Anda hanya mencari nama proses ada atau tidak:

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
Tomba
sumber
-1

Pertimbangkan contoh berikut untuk menyelesaikan masalah Anda:

#!/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)

Saya menyarankan skrip ini karena hanya dapat dijalankan satu kali.

edisonex.dll
sumber
-1

Menggunakan bash untuk mencari proses dengan nama skrip saat ini. Tidak ada file tambahan.

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);

Untuk menguji, tambahkan

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)
Jerome Jaglale
sumber
Tidak ada file tambahan tetapi 6 proses tambahan?
Alois Mahdal
2
Dan bagaimana jika saya ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'dan menjalankan hal itu? ;)
Alois Mahdal
Saya tidak mengerti apa ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'yang saya lakukan. Jika Anda perlu mencari proses berdasarkan nama, mengapa tidak menggunakan pgrep? Apa tujuan dari awk '{print $2}'| awk '{print $2}'? Secara umum, Anda tidak dapat menjalankan awk dua kali berturut-turut seperti itu kecuali Anda mengubah pembatas. Hasil awk pertama di kolom PID ... Awk kedua tidak akan menghasilkan apa-apa.
Enam
-1

Inilah yang saya gunakan di Linux untuk menghindari memulai skrip jika sudah berjalan:

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()

Pendekatan ini bekerja dengan baik tanpa ketergantungan pada modul eksternal.

SH_Rohit
sumber