16

私が作成したのではなく、CPU の負荷が高く不安定なプロセスを生成して通信するプログラムがあります。アプリがクラッシュSIGKILLしたり、.

このトピックが以前に取り上げられたことは知っていますが、説明されているすべての方法を試しましたが、どれもテストに耐えられないようです.

端末は常にそれを行うので、それが可能でなければならないことを私は知っています。端末で何かを実行して端末を強制終了すると、常に端末が停止します。

atexit私はダブルフォークとを試しptysました。atexitには機能しませんsigkill。ダブルフォークはまったく機能しません。Pythonをptys使用して作業する方法が見つかりませんでした。

今日、私は について知りましたprctl(PR_SET_PDEATHSIG, SIGKILL)。これは、親プロセスが終了したときに、子プロセスが自分自身を強制終了する方法であるべきです。で使用しようとしましたpopenが、まったく効果がないようです。

import ctypes, subprocess
libc = ctypes.CDLL('/lib/libc.so.6')
PR_SET_PDEATHSIG = 1; TERM = 15
implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM)
subprocess.Popen(['gnuchess'], preexec_fn=implant_bomb)

上記では、子が作成され、親が終了します。gnuchess今、あなたはa を受け取って死ぬことを期待するでしょうがSIGKILL、そうではありません。100% の CPU を使用しているプロセス マネージャーでまだ見つけることができます。

私の ? の使用に何か問題があるかどうか誰か教えてもらえますprctlか? または、端末がどのように子供たちを殺すことができるか知っていますか?

4

8 に答える 8

13

何年も経っていることは知っていますが、この問題に対する簡単な (少しハックな) 解決策を見つけました。親プロセスから、すべての呼び出しを prctl() を呼び出してから exec() を呼び出す非常に単純な C プログラムにラップすると、Linux でこの問題が解決します。私はそれを「yeshup」と呼んでいます:

#include <linux/prctl.h>
#include <signal.h>
#include <unistd.h>

int main(int argc, char **argv) {
     if(argc < 2)
          return 1;
     prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
     return execvp(argv[1], &argv[1]);
}

Python (またはその他の言語) から子プロセスを生成する場合、「yeshup gnuchess [引数]」を実行できます。親プロセスが強制終了されると、すべての子プロセスに適切に SIGHUP が与えられる (必要がある) ことがわかります。

これが機能するのは、fork() とは異なり、Linux は execvp が呼び出された後でも prctl への呼び出しを尊重する (クリアしない) (これにより、yeshup プロセスが gnuchess プロセスまたはそこで指定したコマンドに効果的に「変換」される) ためです。

于 2012-11-08T00:58:05.797 に答える
6

prctlは、prctl を呼び出しているこのプロセスに対してPR_SET_DEATHSIGのみ設定できます。この特定のプロセスの子を含む他のプロセスに対しては設定できません。私が指しているマニュアルページがこれを表現する方法は、「この値はfork()でクリアされます」です-もちろん、他のプロセスが生成される方法です(Linuxおよび他のUnix-y OSで)。fork

サブプロセスで実行したいコードを制御できない場合 (基本的に、gnuchess例の場合のように)、最初に別の小さな「モニター」プロセスを生成し、そのすべてを追跡する役割を持つことをお勧めします。兄弟(親プロセスは、兄弟を生成するときに兄弟のpidについてモニターに知らせることができます)、共通の親が死亡したときにキラーシグナルを送信します(モニターはそのためにポーリングする必要があり、選択したNに対してN秒ごとにウェイクアップします親がまだ生きているかどうかを確認するにはselect、ループ内で N 秒のタイムアウトで親からの詳細情報を待つために使用します)。

些細なことではありませんが、そのようなシステム タスクはしばしばそうではありません。端末は (プロセスグループの「制御端末」の概念を介して) 異なる方法でそれを行いますが、もちろん、子がそれをブロックすることは簡単です (ダブルフォーク、nohupなど)。

于 2009-12-11T00:16:41.117 に答える
3

実際、あなたの元のアプローチは私にとってはうまく機能することがわかりました-これは、私がテストした正確なサンプルコードです。

echoer.py

#!/bin/env python

import time
import sys
i = 0
try:
    while True:
        i += 1
        print i
        time.sleep(1)
except KeyboardInterrupt:
    print "\nechoer caught KeyboardInterrupt"
    exit(0)

親Proc​​.py

#!/bin/env python

import ctypes
import subprocess
import time

libc = ctypes.CDLL('/lib64/libc.so.6')
PR_SET_PDEATHSIG = 1
SIGINT = 2
SIGTERM = 15

def set_death_signal(signal):
    libc.prctl(PR_SET_PDEATHSIG, signal)

def set_death_signal_int():
    set_death_signal(SIGINT)

def set_death_signal_term():
    set_death_signal(SIGTERM)

#subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_term)
subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_int)
time.sleep(1.5)
print "parentProc exiting..."
于 2013-07-11T18:05:43.020 に答える
1

フラグを後(および前)にPR_SET_PDEATHSIG設定したにもかかわらず、フラグがクリアされているかどうか疑問に思っているので、ドキュメントからはクリアされるべきではないようです。forkexec

その理論をテストするには、次のことを試すことができます。同じコードを使用して、Cで記述されたサブプロセスを実行し、基本的にprctl(PR_GET_PDEATHSIG, &result)は結果を呼び出して出力するだけです。

もう1つ試してみることができます。を呼び出すときに、arg3、arg4、およびarg5に明示的なゼロを追加しますprctl。すなわち:

>>> implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM, 0, 0, 0)
于 2012-10-04T12:06:11.947 に答える
1

execv の後に setuid を呼び出すと、子供はシグナルを受信できないため、考慮すべきセキュリティ制限がいくつかあります。この制限の完全なリストはこちら

幸運を !
/モハメド

于 2014-06-03T16:11:13.903 に答える
1

ダブルフォークは制御端末から切り離すためだと思っていました。どのように使用しようとしているのかわかりません。

これはハックですが、いつでも「ps」を呼び出して、強制終了しようとしているプロセス名を検索できます。

于 2009-12-11T00:02:36.997 に答える
1

次のようなものを使用して「クリーンアップ」する非常に厄介な方法を見てきましたps xuawww | grep myApp | awk '{ print $1}' | xargs -n1 kill -9

クライアント プロセスは、ポップされた場合、SIG_PIPE をキャッチして終了する可能性があります。これには多くの方法がありますが、実際には多くの要因に依存します。子にいくつかの ping コード (親への ping) をスローすると、死亡時に SIG_PIPE が発行されることを確認できます。キャッチした場合は、終了する必要があります。これが正しく機能するには、双方向通信が必要です...または、通信の発信元としてクライアントに対して常にブロックする必要があります。子を変更したくない場合は、これを無視してください。

実際の Python インタープリターが segfault することを想定していないと仮定すると、各 PID をシーケンスに追加し、終了時に kill することができます。これは、終了してもキャッチされない例外に対しても安全である必要があります。Pythonには、終了コードを実行する機能があります...クリーンアップのために。

より安全な厄介な方法を次に示します。各子 PID を、マスター プロセス (別のファイル) を含むファイルに追加します。ファイルのロックを使用します。マスター pid の flock() 状態を監視するウォッチドッグ デーモンを構築します。ロックされていない場合は、子 PID リスト内のすべての PID を削除します。起動時に同じコードを実行します。

さらに厄介なことに、上記のように PID をファイルに書き込み、サブシェルでアプリを呼び出します。(./myMaster; ./killMyChildren)

于 2009-12-11T00:10:22.503 に答える