Перевірте, чи працює сценарій 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.