wiki.zope.jp
ZopeSignalHandling
   
RecentChanges WikiHelp WikiPractice JumpSearch

Zopeのシグナル処理

YOSHIDA Satoshi <cheeseshop __at__ syd.jp>

この文章は自宅サーバのWikiにて公開していましたが、 操作ミスにより消してしまいました。いくつかリンクが張られているため、 バックアップから探しだしたテキストをここに公開しておきます。

2003-10-17

Zope 2.6から、UNIX系のZopeはシグナルを送ることで 「データベースのパッキング」および「ログのreopen (ログファイルをバックアップのため 移動させた後、新しいログファイルを作成すること) 」ができるようになった。

(訂正) データベースのパッキングは、現在のZODBトランザクションで Zopeプロセスがパッキングを行うとすべての非同期操作ができなくなることから、 バージョン2.6.0b2以降シグナルでは操作できないようになっています。

しかしこの機能は、一部のOSでは動かなかった。

中神さんの解決方法は「シングルプロセスで動かす」または「Z2.pidに子プロセスのidを書き込む」というもの。

でも、ログのreopenができないだけでZopeの運用状態を変えたり Z2.pidの仕様を変えたりするのは実用としては無理がある。

中神さんのページには「親プロセス・子プロセスとも同じハンドラが 登録されているので、子プロセスに送らないと動かない」と書かれていたが、 実際には親プロセス(プロセスマネージャ側)にSIGUSR2を送った場合でも、 SignalPasserを経由して子プロセスにも同じシグナルが渡されるような仕組みになっていた。

        (SIGUSR2が親プロセスに送られる)
                :
                ↓
          [親プロセスのSIGUSR2ハンドラ]
          SignalPasser() ... 子プロセスに同じシグナルを送る
                :
                :
        (SIGUSR2が子プロセスに送られる)
                :
                ↓
          [子プロセスのSIGUSR2ハンドラ]
          signalHandler() ... 登録されたハンドラを逆順に呼び出す
                ↓
          logfileReopenHandler()
                ↓
          sys.__lg.reopen() ... ログオブジェクトのreopen()メソッドを実行

しかしZope 2.6は、親プロセスが子プロセスに受け渡すシグナルの種類を 「SIGUSR2」といった名前ではなく「12」という番号で指定していた。 このためLinuxとは異なるシグナル番号を使っているOSでは正常に動作しなかった。

(一方、子プロセスのsignalHandlerは「SIGUSR2」と名前で定義しているので、 子プロセス側に直接SIGUSR2シグナルを送信すれば正しく動作する)

      ==========  ==========  ==============  ========
      シグナル名   Linux-x86   Solaris-sparc   FreeBSD
      ==========  ==========  ==============  ========
      SIGUSR1     10          16              30
      SIGUSR2     12          17              31
      ==========  ==========  ==============  ========

この問題を解決するには、lib/python/zdaemon/Daemon.pyに以下のパッチを当てればよい。 自宅のFreeBSDでは「SIGUSR2シグナル→ログreopen」ができることを確認した。

      *** Daemon.py.orig      Wed Nov 13 06:13:59 2002
      --- Daemon.py   Fri Oct 17 16:52:06 2003
      *************** def run(argv, pidfile=''):
      *** 51,57 ****
                        # We're the parent (the daemon process)
                        # pass all "normal" signals along to our child, but don't
                        # respond to them ourselves unless they say "die"!
      !                 interesting = [1, 2, 3, 10, 12, 15]
                        # ie. HUP, INT, QUIT, USR1, USR2, TERM
                        for sig in interesting:
                            signal.signal(sig, SignalPasser(pid))
      --- 51,64 ----
                        # We're the parent (the daemon process)
                        # pass all "normal" signals along to our child, but don't
                        # respond to them ourselves unless they say "die"!
      !                 interesting = [
      !                   signal.SIGHUP,
      !                   signal.SIGINT,
      !                   signal.SIGQUIT,
      !                   signal.SIGUSR1,
      !                   signal.SIGUSR2,
      !                   signal.SIGTERM
      !                 ]
                        # ie. HUP, INT, QUIT, USR1, USR2, TERM
                        for sig in interesting:
                            signal.signal(sig, SignalPasser(pid))

 

2003-10-19 - 余談

この問題を教えてくださった中神さんに感謝します。

FreeBSDを使っているのに私がこの不具合に気づかなかった理由は、Apacheでログを取っていたから。 Zopeの前に立てたApacheアクセスログの方をシェルスクリプトでローテイトさせていて、 Zopeは普段「-l /dev/null」オプションでログを取らない設定でした。

2003-11-18

久しぶりにみたら、Tres Seaver氏に10月21日にacceptされていました(気づくの遅過ぎ‥‥)。

ただZope 2.7はプロセスマネージャの仕組みが劇的に変わっていて、この問題は発生しないようです。 そもそもzdaemon/Daemon.py自体存在しません。

今後は2.7の開発に力を入れていくようで、Zope 2.6のコードを修正するつもりはないみたい。

(補足) Zope 2.7からはプロセスマネージャがzopectlコマンドとして独立しました。 zopectl kill <シグナル番号> でZope本体のプロセスにシグナルを送れます。


Comments

Last edited Mon, 11 Sep 2006 15:21:19 +0900 Edit this page