LINUX.ORG.RU

Python Signals


0

1

Хочу сделать так, чтобы по сигналу мой демон выдавал своё текущее оперативное состояние (грубо говоря принтовал атрибут класса в файл). Может управляющий сокет лучше? Есть ли сайдэффекты у сигналов и управляющих сокетов, которые обязательно необходимо учитывать?


Сигналы обрабатываются асинхронно, поэтому строго ограничено что в них можно делать. Для простоты лучше считать, что в них можно только присвоить значение переменной типа volatile sig_atomic_t, а действия выносить в главный цикл после проверки переменной (впрочем, можно сигналы обрабатывать через какой-нибудь libevent - там в обработчике можно делать всё в силу правильной обработки сигналов внутри самой библиотеки).

kemm
()

> чтобы по сигналу мой демон выдавал своё текущее оперативное состояние

В чём проблема? Тут всё описано и пример в конце есть http://docs.python.org/library/signal.html

Может управляющий сокет лучше?


это как тебе удобнее

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Есть проблема. Получение статуса по сигналу может убить слип в цикле. Например, у моего демона такой ран:

class MyDaemon(Daemon):
        def run(self):
                while True:
                        self.status = '1'
                        time.sleep(1)
                        self.status = '2'
                        time.sleep(1)
                        self.status = '3'
                        time.sleep(1)

Запускаем, всё работает, слипы по 1 секунде честно ждут. Но, стоит мне в цикле без задержек kill'ом посылать сигналы, слипы отрубаются. Т.е. в статус-файл валится с дикой скоростью 123123123123123, а не 1ждемсекунду2ждемсекунду3ждемсекунду1ждемсекунду2ждемсекунду3 Вот я и задумался. А вдруг еще чего интересного может приключиться с такими-то сайд-эффектами. Особенно на высоконагруженном демоне.

tanenn
() автор топика
Ответ на: комментарий от kemm

Мой обработчик сигнала

def test(self, signum, frame):
    f = open('/tmp/%s_status'% self.__class__.__name__, 'wr+')
    f.write(str(self.status))
    f.close()

Так пойдет, или я не правильно познал дзен реентерабельности?

tanenn
() автор топика
Ответ на: комментарий от tanenn

Тьфублин. Нельзя с утра пораньше отвечать. Прошу прощения, я про C говорил. Как сигналы в питоне сделаны - понятия не имею.

kemm
()
Ответ на: комментарий от true_admin

Убрал демонизацию, теперь это просто скрипт, который после запуска будет принтовать своё состояние в консоль. По ссылке скрипт и описание методологии. http://pastebin.com/qgYF45xd

tanenn
() автор топика
Ответ на: комментарий от true_admin

Это происходит потому что, грубо говоря, вместо self первым аргументом будет signum, т.е. хэндлер должен иметь сигнатуру handler(signum, frame)

Вот так будет работать:

--- ./test.py   2011-04-01 14:20:38.179740003 +0200
+++ ./test2.py  2011-04-01 14:23:17.699740003 +0200
@@ -5,12 +5,14 @@
 import sys, os, time, atexit
 from signal import SIGTERM, SIGHUP
 import os, socket, time, signal, select
- 
+
+def print_status(*args):
+    print "TEST", daemon.status
+
 class Daemon(object):
     def __init__(self, pidfile):
             self.pidfile = pidfile
             self.status = 'test'
-            signal.signal(signal.SIGHUP, self.get_status)
     def get_status(self, signum, frame):
             f = open('/tmp/daemonstatus', 'wr+')
             f.write(str(self.status))
@@ -29,6 +31,7 @@
 
 pid = os.getpid()
 daemon = Daemon(pid)
+signal.signal(signal.SIGHUP, print_status)
 f = open('/tmp/dem_pid', 'wr+')
 f.write(str(pid))
 f.close()
true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Хм… А как можно? Все примеры, что я видел так сделаны.

tanenn
() автор топика
Ответ на: комментарий от true_admin

Чето не прокатило. Дифф накатил, получилось такое.

def print_status(*args):
    print "TEST", daemon.status

class Daemon(object):
    def __init__(self, pidfile):
            self.pidfile = pidfile
            self.status = 'test'
    def get_status(self, signum, frame):
            f = open('/tmp/daemonstatus', 'wr+')
            f.write(str(self.status))
            f.close()
    def Run(self):
            while True:
                    self.status = '1'
                    print self.status
                    time.sleep(1)
                    self.status = '2'
                    print self.status
                    time.sleep(1)
                    self.status = '3'
                    print self.status
                    time.sleep(1)

pid = os.getpid()
daemon = Daemon(pid)
signal.signal(signal.SIGHUP, print_status)
f = open('/tmp/dem_pid', 'wr+')
f.write(str(pid))
f.close()
daemon.Run()

В консоли где запущен скрипт выводит такое с бешенной скоростью

1
TEST 1
2
TEST 2
3
TEST 3
1
TEST 1
2
TEST 2
3
TEST 3
1
TEST 1
2
TEST 2
3
TEST 3
1
TEST 1
2
TEST 2
3
TEST 3
1
TEST 1

В консоли где килл такое. Тоже с бешенной скоростью

3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3

tanenn
() автор топика
Ответ на: комментарий от tanenn

В общем, у меня твой скрипт работает нормально, не вижу причин чтобы не работал. А self первым аргументом неявно передаётся т.к. get_status() это метод и у него есть get_status.__instance__ по которому он знает что такое self.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin
tanenn@tanenn-K52F:~$ python
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56) [GCC 4.4.5] on linux2

uname -a
Linux tanenn-K52F 2.6.38-02063802-generic #201103281246 SMP Mon Mar 28 12:50:24 UTC 2011 x86_64 GNU/Linux
tanenn
() автор топика
Ответ на: комментарий от tanenn

запусти «strace -f -r -s 128 -o /tmp/dem_strace ./dem.py»(у тебя ведь нет в скрипте секретных данных?), вызови этот баг, сразу прибей программу(чтобы логи не разрослись до неприличия) и запости /tmp/dem_strace где-нить в интете(типа ompldr.org). Я сравню с моим стрейсом.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Я об этом говорил. И это неприятность. К примеру есть у меня демон, который в каком-то внутреннем цикле ждет по 60 секунду, чтобы обратиться к базе. Если его опрашивать раз в секунду, то он раз в секунду и будет в базу долбиться, пропуская слипы. Это как-то неправильно. Необходимо опрашивать демон, при этом внутренней логики его работы не меняя.

tanenn
() автор топика
Ответ на: комментарий от true_admin

Он не просто валит, он код

            while True:
                    self.status = '1'
                    time.sleep(1)
                    self.status = '2'
                    time.sleep(1)
                    self.status = '3'
                    time.sleep(1

превращает в

            while True:
                    self.status = '1'
                    self.status = '2'
                    self.status = '3'

tanenn
() автор топика
Ответ на: комментарий от tanenn

А, понял. Это документированная баг(руки бы оторвать кто такое написал). Попробуй использовать gevent для написания демона, он работает как надо:

#!/usr/bin/env python
from gevent import sleep, Greenlet
import gevent
import signal

class Daemon(Greenlet):
    def __init__(self):
        Greenlet.__init__(self)
        gevent.signal(signal.SIGHUP, self.sighandler)

    def sighandler(self, *args):
        open('/tmp/daemonstatus', 'a').write(".")

    def _run(self):
        while True:
            print "test"
            sleep(1)

d = Daemon()
d.start()
d.join()
true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.