3

select私はIOを多重化するためにそのようなものを使用する簡単なプログラムを持っています。
「サーバー」プロセスを中断するために、SIGINTに反応する sig_handler を統合しました。

メモリが割り当てられるたびに、包含メソッドはそれ自体または呼び出しメソッドを解放します。

使用valgrindすると、一部の割り当てが解放されていないことがわかります。
その必要はないかもしれませんが、信号を処理する最善の方法を知りたいです。STRG + Cを押しても呼び出しが呼び出されない
ようです。 したがって、私の最初のアプローチであるブレーク条件でループを終了するのは無意味です。free

プログラム全体を閉じる前に、すべてをクリーンアップする可能性はありますか?

ヒントやアドバイスをありがとう。

4

3 に答える 3

12

Valgrind はメモリ リークを見つけるための単なるツールであり、アドバイスに耳を傾ける必要があるオラクルではありません。プログラムを「Valgrind クリーン」にすることは価値のある目標ですが、手に負えなくなってはいけません。プログラムについていくつか質問してみてください。

  1. SIGINTorまたは何かを受け取ったとき、プログラムは何かをする必要がありますSIGQUITか? ある種のクリーンシャットダウンを行う必要がありますか?たとえば、サーバーは、開いているすべてのリクエストの処理を終了するか、少なくとも接続されたクライアントにシャットダウン メッセージを送信することを決定する場合があります。

  2. 突然の終了は常に特定のブロックを残しますか? その後、Valgrind からのレポートを無効にすることができます。解放される予定のメモリを解放するために余分な時間を費やす必要はありません。

簡単に言えば、free終了しようとしているプログラムを呼び出す理由は 2 つしかありません。

  1. それが Valgrind メッセージを破棄する最も簡単な方法である場合 (つまり、Valgrind のマニュアルを読まずに)

  2. コードが簡単になる場合。

それ以外の場合は、プログラムの終了時に呼び出さないでfreeください。CPU サイクルを消費するだけです。

SIGINT の処理: SIGINT を処理する 4 つの一般的な方法を考えることができます。

  1. デフォルトのハンドラーを使用します。強くお勧めします。これにより、必要なコード量が最小限になり、異常なプログラム動作が発生する可能性が低くなります。プログラムは単に終了します。

  2. longjmpすぐに終了するために使用します。これは、ヘルメットなしで高速のオートバイに乗るのが好きな人向けです。これは、ライブラリ呼び出しでロシアン ルーレットをプレイするようなものです。推奨されません。

  3. フラグを設定し、メイン ループのpselect/を中断しますppoll。シグナルマスクをいじる必要があるため、これを正しく行うのは面倒です。/のような再入不可能な関数ではなく、pselect/のみを中断したいので、シグナルマスクなどには非常に注意する必要があります。推奨されません。/の代わりに/を使用する必要があります。これは、「p」バージョンがシグナル マスクをアトミックに設定できるためです。またはを使用すると、フラグをチェックした後で/を呼び出す前にシグナルが到着する可能性があります。これは悪いことです。ppollmallocfreepselectppollselectpollselectpollselectpoll

  4. メイン スレッドとシグナル ハンドラの間で通信するためのパイプを作成します。select/への呼び出しには、常にこのパイプを含めてpollください。シグナル ハンドラーは単純に 1 バイトをパイプに書き込み、メイン ループがもう一方の端から 1 バイトを正常に読み取ると、正常に終了します。強くお勧めします。シグナル ハンドラー自体をアンインストールすることもできるため、せっかちなユーザーはCTRL+C2 回ヒットしてすぐに終了できます。

最も簡単で確実な 2 つの方法は、#1 と #4 です。

終了するプログラムにはリークがありません。 実行中のプログラムだけがリークする可能性があります。プログラムが終了すると、すべてのメモリが解放されます (したがって、リークはなくなります)。

于 2012-07-15T00:14:17.600 に答える
1

これが私のシンプルで少し汚れたソリューションです。

#include <signal.h>

volatile bool gContinue; 

void handleCtrlC(int ) {
    gContinue = false;
}

int main () {
    gContinue = true;

    signal(SIGINT, handleCtrlC);

   ... allocate memory ...

    sigset_t sigmask;
    sigemptyset (&sigmask);  

    while (gContinue) {

        /*...*/
        ready = pselect(nfds, &readfds, &writefds, &exceptfds,
            timeout, &sigmask);
        /*...*/
    }

    ... free memory ...

    return 0;
}

編集:ループに pselect を追加しました。

于 2012-07-14T23:22:22.113 に答える
0

への呼び出しの直前にフラグのチェックを追加すれば、Adam Hunt の提案は機能しますselect。そうしないと、通常の処理がチェックをスキップする可能性が高いため、直前である必要があります。そのため、次に実際のイベントが返さEINTRれるまで終了が遅れます。select

while (gContinue) {
    /*set up some stuff for next select call*/
    do {
        if (gContinue == 0) break;
        nfds = select(...);
    } while (nfds == -1 && errno == EINTR);
    /*handle select return*/
}

編集:select Dietrich Epp は、フラグ チェックと への呼び出しによってのみ閉じることができる呼び出しの間に競合状態があることを指摘していますpselectpselectと非常によく似てselectいますが、主な違いは、最後のパラメーターがマスクとして使用され、ブロックするシグナルを決定することです。pselectしたがって、以下のコード サンプルは、フラグ チェックと呼び出しの間の競合を閉じます。

sigset_t emptyset, blockset, origset;
sigemptyset(&emptyset);
sigemptyset(&blockset);
sigaddset(&blockset, SIGINT);

while (gContinue) {
    /*...*/
    sigprocmask(SIG_BLOCK, &blockset, &origset);
    do {
        if (gContinue == 0) break;
        nfds = pselect(..., &emptyset);
    } while (nfds == -1 && errno == EINTR);
    sigprocmask(SIG_SETMASK, &origset, NULL);
    /*...*/
};

atexitもう 1 つの方法は、割り当てられたすべての要素をグローバル データ構造に登録して、インストールしたハンドラーによって解放できるようにすることです。割り当てを解除するコードになった場合は、最初にグローバル データ構造から登録解除します。

m = malloc(sz);
register_allocation(m);
/*...*/
unregister_allocation(m);
free(m);

そして使用atexit

void cleanup_allocations () {
    /*...*/
}

atexit(cleanup_allocations);
于 2012-07-14T23:51:51.447 に答える