1

マルチスレッド プラグイン ベースのアプリケーションを作成しています。私はプラグインの作者ではありません。そのため、プラグインのセグメンテーション違反が原因でメイン アプリケーションがクラッシュすることを回避したいと考えています。出来ますか?または、プラグインのクラッシュによって、メインのアプリケーションのステータスも確実に損なわれますか? 私の「実際の」アプリケーションはqtライブラリに強く基づいているため、qtを使用してスケッチプログラムを作成しました。ご覧のとおり、割り当てられていない QString でトリミングされた関数を呼び出してスレッドを強制的にクラッシュさせました。シグナル ハンドラは正しく呼び出されますが、スレッドが強制終了された後、メイン アプリケーションもクラッシュします。私は何か間違ったことをしましたか?または、前に言ったように、私がやろうとしていることは達成できませんか? この簡略化されたバージョンのプログラムでは、プラグインの使用を避け、スレッドのみを使用していることに注意してください。プラグインを導入すると、新しいクリティカル レベルが追加されます。私は考えます。一歩一歩進んでいきたい。そして、全体として、私の目標が実現可能かどうかを理解したいと思っています。誰もが私に与えようとするどんな種類の助けや提案にも感謝します.

#include <QString>
#include <QThread>
#include<csignal>
#include <QtGlobal>
#include <QtCore/QCoreApplication>


class MyThread : public QThread
{
public:
    static void sigHand(int sig)
    {
        qDebug("Thread crashed");
        QThread* th = QThread::currentThread();
        th->exit(1);
    }

    MyThread(QObject * parent = 0)
    :QThread(parent)
    {
        signal(SIGSEGV,sigHand);
    }

    ~MyThread()
    {
        signal(SIGSEGV,SIG_DFL);
        qDebug("Deleted thread, restored default signal handler");
    }

    void run()
    {
        QString* s;
        s->trimmed();
        qDebug("Should not reach this point");
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    MyThread th(&a);
    th.run();
    while (th.isRunning());
    qDebug("Thread died but main application still on");
    return a.exec();
}
4

1 に答える 1

1

私は現在同じ問題に取り組んでおり、Google経由でこの質問を見つけました。

ソースが機能しない理由はいくつかあります。

  • 新しいスレッドはありません。スレッドは、QThread::start を呼び出した場合にのみ作成されます。代わりに、メイン スレッドで run メソッドを実行する MyThread::run を呼び出します。

  • QThread::exit を呼び出してスレッドを停止します。これは、スレッドを直接停止することは想定されていませんが、(qt) シグナルをスレッド イベント ループに送信し、停止を要求します。スレッドもイベント ループもないため、関数の効果はありません。run メソッドを記述しても qt イベント ループが作成されないため、QThread::start を呼び出したとしても機能しません。任意の QThread で exit を使用できるようにするには、最初に QThread::exec を呼び出す必要があります。
    ただし、とにかく QThread::exit は間違った方法です。SIGSEGV を防ぐには、イベント ループで (qt) シグナルを受信した後ではなく、すぐにスレッドを呼び出す必要があります。したがって、一般的には眉をひそめますが、この場合は QThread::terminate を呼び出す必要があります

  • しかし、QThread::currentThread、QThread::exit、または QThread::terminate などの複雑な関数をシグナル ハンドラから呼び出すのは安全ではないと言われているため、そこでそれらを呼び出してはいけません。

  • スレッドはシグナル ハンドラの後でまだ実行されているため (QThread::terminate でさえ十分に速くスレッドを強制終了するかどうかはわかりません)、シグナル ハンドラは呼び出し元の場所に終了し、SIGSEGV の原因となった命令を再実行します。次の SIGSEGV が発生します。

したがって、別のアプローチを使用しました。シグナルハンドラーは、命令アドレスを含むレジスタを別の関数に変更します。これは、シグナルハンドラーが終了した後、クラッシュする命令の代わりに実行されます。お気に入り:

void signalHandler(int type, siginfo_t * si, void* ccontext){
    (static_cast<ucontext_t*>(ccontext))->Eip = &recoverFromCrash;
}

struct sigaction sa;
memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = &signalHandler; 
sigaction(SIGSEGV, &sa, 0);

その後、recoverFromCrash 関数は通常、SIGSEGV の原因となっているスレッドで呼び出されます。シグナルハンドラはすべてのスレッドからすべての SIGSEGV に対して呼び出されるため、関数はどのスレッドで実行されているかを確認する必要があります。

ただし、実行中のスレッドによっては他のものが存在する可能性があるため、単純にスレッドを強制終了することは安全だとは考えていませんでした。そのため、それを強制終了する代わりに、無限ループで実行させました (CPU 時間を浪費しないようにスリープを呼び出します)。次に、プログラムが閉じられると、グローバル変数が設定され、スレッドが終了します。(recover 関数は決して返ってはならないことに注意してください。そうしないと、SIGSEGV の原因となった関数に実行が戻るためです)

一方、メインスレッドから呼び出されると、新しいイベント ループが開始され、プログラムが実行されます。

if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
    //sub thread
    QThread* t = QThread::currentThread();
    while (programIsRunning) ThreadBreaker::sleep(1);
    ThreadBreaker::forceTerminate();
} else {
    //main thread
    while (programIsRunning) {
        QApplication::processEvents(QEventLoop::AllEvents);
        ThreadBreaker::msleep(1);
    }
    exit(0);
}

QThread の msleep、sleep、および setTerminationEnabled (終了前に呼び出す必要がある) は保護されており、recover 関数から呼び出すことができないため、ThreadBreaker は QThread の簡単なラッパー クラスです。

しかし、これはあくまでも基本的なイメージです。心配すべきことは他にもたくさんあります。 )、デバッグメッセージを表示します(スタックトレースを取得してみてください、なぜgdbを呼び出すとXサーバーがクラッシュするのか、glibcバックトレースを呼び出すとプログラムがクラッシュするのか疑問に思います)...

于 2012-12-21T12:56:27.457 に答える