Перевірте, чи працює сценарій python


100

У мене веб-додаток працює пітон-пімон / Як я можу швидко перевірити (за допомогою python), чи працює мій демон, а якщо ні, то запустити його?

Я хочу зробити це таким чином, щоб виправити будь-які збої демона, і тому сценарій не потрібно запускати вручну, він автоматично запускається, як тільки він буде викликаний, а потім продовжує працювати.

Як я можу перевірити (використовуючи python), чи працює мій сценарій?


Ви впевнені, що не хочете, щоб ваш процес був так само тримати ваш інший процес записаним у python?
ojblass

Перейти на Tendo, створює однотонний екземпляр вашого сценарію, тому сценарій не запускається, якщо він вже запущений. github.com/pycontribs/tendo
JasTonAChair

Це не робота вашого демона, це робота "верхньої" програми, яка запускає ваш демон. Використовуйте systemd або інший інструмент, як нагляд. Не покладайтеся на pid, записаний у файл. Якщо ви не можете використовувати systemd / supervisord, використовуйте блокування, щоб переконатися, що воно не виконується двічі.
guettli

Відповіді:


92

Закиньте кудись pidfile (наприклад, / tmp). Потім ви можете перевірити, чи працює процес, перевіривши, чи існує PID у файлі. Не забувайте видаляти файл при чистому вимкненні та перевіряйте його під час запуску.

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

Потім ви можете перевірити, чи працює процес, перевіривши, чи вміст /tmp/mydaemon.pid є існуючим процесом. Monit (згаданий вище) може зробити це за вас, або ви можете написати простий скрипт оболонки, щоб перевірити його для вас, використовуючи код повернення з ps.

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

Щоб отримати додатковий кредит, ви можете використовувати модуль atexit, щоб переконатися, що ваша програма очищає свій pidfile за будь-яких обставин (коли вони вбиті, винятки викликані тощо).


6
якщо програма порушена, os.unlink () не виконується, і програма не запуститься знову, оскільки файл існує. правильно?
Юда Правіра

2
Правильно, проте це може бути очікуваною поведінкою. Якщо pidfile існує, але PID всередині не працює, це вказує на не витончене відключення, що означає, що програма вийшла з ладу. Це дозволяє вам знати, що існує проблема, і перевірити журнали. Як уже згадувалося, модуль atexit також може подбати про це, якщо припустити, що помилка відсутня в самому інтерпретаторі Python.
Ден Удей

7
Хоча це просте рішення, це сприйнятливо до перегонів. Якщо два екземпляри сценарію виконуються приблизно в один і той же час, можливо, вони if os.path.isfile(pidfile)можуть оцінити помилкові для обох, змусивши їх обидва записати файл блокування і продовжити виконання.
Серін

6
Pids також повторно використовуються операційною системою. Тож можливі помилкові позитиви.
айчедея

12
Для тих, хто зараз виявляє це, зауважте, що в python 3 file()було видалено, і вам слід скористатися open()замість цього. Крім того, навіть якщо ви на 2.7 , ви повинні використовувати open()більш , file()як описано тут: docs.python.org/2/library/functions.html#file (І так, якщо ви використовували пітон назад близько 2,2 офіційного ради була протилежністю. Мабуть, вони передумали.)
jpk

154

Для техніки, зручної в системі Linux, використовується використання доменних сокетів:

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)

Це атомно і дозволяє уникнути проблеми розміщення файлів блокування, якщо ваш процес надсилається SIGKILL

Ви можете прочитати в документації,socket.close що розетки автоматично закриваються, коли сміття збирається.


20
Примітка для майбутніх googlers: у цьому коді використовуються "абстрактні сокети", які є специфічними для Linux (а не позиції взагалі). Більше про це: blog.eduardofleury.com/archives/2007/09/13
georg

6
Це приголомшливо, і це не залишає дурних затяжних файлів. Бажаю, щоб я міг більше підтримати це.
Hiro2k

4
Дивовижно. Але мені цікаво, чому саме lock_socket визначається глобальним. Я перевірив, і якщо lock_socket не визначено глобально, система блокування не працює при запуску декількох процесів. Чому? lock_socket визначається та використовується лише у функції get_lock. Чому це потрібно визначити глобальним?
Альптугай

7
Минув час, коли я це написав ... і пам'ять у мене туманна. Але я думаю, це було тому, що він збирає сміття і розетка закривається в іншому випадку. Щось схоже.
айчедея

8
Нульовий байт ( \0) означає, що сокет створюється в абстрактному просторі імен, а не створюється в самій файловій системі.
айчедея

22

PID бібліотека може зробити саме це.

from pid import PidFile

with PidFile():
  do_something()

Він також автоматично обробляє випадок, коли pidfile існує, але процес не працює.


Це працює КРАСНО. Її просто потрібно запустити як root, щоб запустити Ubuntu. +1
Джиммі

11
@Jimmy ви можете зробити, наприклад, with PidFile(piddir='/home/user/run/')використовувати інший каталог, щоб помістити файл pid туди, де у вас є дозволи. Тоді вам не потрібно запускати його як root
Decko

Я думаю, що використання темп-каталогу, описаного тут, було б хорошим варіантом для piddir.
Ріші Латчперперс

@RishiLatchmepersad Використання gettempdir не було б гарною ідеєю, оскільки це дасть унікальний каталог під час кожного виклику, який порушить перевірку на pid. Каталог повинен бути однаковим щоразу, коли сценарій запускається.
Децько

11

Звичайно, приклад від Dan не вийде як слід.

Дійсно, якщо сценарій завершиться збоєм, збільшить виняток або не очистить pid-файл, сценарій буде запущений кілька разів.

Я пропоную наступне на основі іншого веб-сайту:

Це потрібно перевірити, чи вже існує файл блокування

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

Це частина коду, куди ми поміщаємо PID-файл у файл блокування

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

Цей код перевірятиме значення pid порівняно з існуючим запущеним процесом., Уникаючи подвійного виконання.

Сподіваюся, це допоможе.


3
Слід використовувати os.kill(old_pid, 0), яка має бути більш портативною через UNIXes. Він підвищиться, OSErrorякщо такого PID немає або він належить іншому користувачеві.
drdaeman

1
Майте на увазі, що використання / proc / <pid> для перевірки процесу є надзвичайно не портативним і надійно працюватиме лише в Linux.
Ден Удей

10

Є дуже хороші пакети для перезавантаження процесів в UNIX. Той, хто має чудовий підручник щодо створення та налаштування, це monit . За допомогою певного налаштування ви можете мати надійну перевірену технологію, яка підтримує ваш демон.


Я згоден, не вигадувати колесо, є тонни способів демона додатки , включаючи його перезапуск , якщо він помре, запуск , якщо не працює, і т.д. і т.п.
ДАВР

9

Моє рішення - перевірити наявність аргументів процесу та командного рядка, перевірених на Windows та 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)

Окрім відповіді @nst, це краща відповідь.
shgnInc

це найкращий спосіб! thx
Хаді Хашемі

5

Існує безліч варіантів. Одним із методів є використання системних викликів або бібліотек python, які виконують такі дзвінки для вас. Інший - просто породити такий процес, як:

ps ax | grep processName

і аналізувати вихід. Багато людей обирають такий підхід, на мою думку, це не обов'язково поганий підхід.


Чи буде процесName включати ім'я мого сценарію?
Джош Хант

thet залежить від того, як ви розпочнете свій процес
ojblass

наприклад: ps ax | grep python
Користувач

3

Я наткнувся на це старе питання, шукаючи собі рішення.

Використовуйте 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'])

Цей скрипт повинен використовуватися як sudo, або ви отримаєте помилку в доступі, відхилену.
DoesData

Крім того, якщо ви передасте аргументи на свій скрипт з команди, як у списку, також будуть всі ці аргументи.
DoesData


2

Спробуйте цю іншу версію

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

Замість того, щоб розробити власне рішення PID-файлу (яке має більше тонкощів і кутових випадків, ніж ви могли б подумати), погляньте на нагляд - це система управління процесом, яка дозволяє легко обернути контроль роботи та демон-поведінку навколо існуючого Python сценарій.


0

Інші відповіді чудово підходять для таких речей, як робота з cron, але якщо ви працюєте з демоном, вам слід відстежувати його з чимось на зразок daemontools .


0
ps ax | grep processName

якщо yor скрипт налагодження в pycharm завжди виходить

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

0

спробуйте це:

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

Ось більш корисний код (з перевіркою, чи саме python виконує сценарій):

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

Ось рядок:

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

повертає 0, якщо "grep" є успішним, і процес "python" зараз працює з назвою вашого сценарію як параметр.


0

Простий приклад, якщо ви шукаєте лише існування імені процесу:

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

Розгляньте наступний приклад, щоб вирішити вашу проблему:

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

Я пропоную цей сценарій, оскільки його можна виконати лише один раз.


-1

Використання bash для пошуку процесу з назвою поточного сценарію. Немає зайвого файлу.

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

Щоб перевірити, додайте

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

Немає зайвого файлу, але 6 додаткових процесів?
Alois Mahdal

2
А що робити, якщо я ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'запускаю цю річ? ;)
Алоїс Магдал

Я не розумію, що ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'робить. Якщо вам потрібно шукати процес по імені, то чому б не використовувати pgrep? Яка мета awk '{print $2}'| awk '{print $2}'? Взагалі, ви не можете запускати awk два рази поспіль, якщо не змінити роздільник. Перший awk призводить до стовпця PID ... Другий awk нічого не призведе.
Шість

-1

Це те, що я використовую в Linux, щоб уникнути запуску сценарію, якщо він вже запущений:

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

Такий підхід працює добре без будь-якої залежності від зовнішнього модуля.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.