1

しばらく前に、自動 S/MIME 処理用の単純な SMTP ゲートを書きましたが、今度はテストに取り掛かります。メール サーバーでは一般的であるように、メイン プロセスは着信接続ごとに子プロセスを fork します。作成する子プロセスの数を制限することは良い習慣です。

負荷が高い (多数のクライアントからの多数の接続が同時に発生している) 場合、子プロセスが正しくカウントされていないように見えます。問題は、子プロセスが終了したときにカウンターが減少することです。負荷が高い状態が数分続くと、カウンターが実際の子プロセスの数よりも大きくなります (つまり、5 分後には 14 になりますが、何もありません)。

すでにいくつかの調査を行いましたが、何も機能しませんでした。ゾンビプロセスはすべてリープされているので、SIGCHLDハンドリングは問題ないようです。同期の問題かもしれないと思ったのですが、ミューテックスを追加して変数の型をvolatile sig_atomic_t(今のままで)変えても変化なし。シグナルマスキングでも問題ないので、 を使って全シグナルをマスキングしてみsigfillset(&act.sa_mask)ました。

waitpid()ときどき奇妙な PID 値 (172915914 のように非常に大きい) を返すことに気付きました。

質問といくつかのコード。

  1. 他のプロセス (つまりinit) がそれらの一部を取得している可能性はありますか?
  2. 終了後にプロセスがゾンビにならないことはありますか? 自動的に刈り取ることはできますか?
  3. 修正方法は?多分それらを数えるより良い方法はありますか?

で子をフォークするmain():

volatile sig_atomic_t sproc_counter = 0;    /* forked subprocesses counter */

/* S/MIME Gate main function */
int main (int argc, char **argv)
{
    [...]

    /* set appropriate handler for SIGCHLD */
    Signal(SIGCHLD, sig_chld);

    [...]

    /* SMTP Server's main loop */
    for (;;) {

        [...]

        /* check whether subprocesses limit is not exceeded  */
        if (sproc_counter < MAXSUBPROC) {
            if ( (childpid = Fork()) == 0) {    /* child process */
                Close(listenfd);                /* close listening socket */
                smime_gate_service(connfd);     /* process the request */
                exit(0);
            }
            ++sproc_counter;
        }
        else
            err_msg("subprocesses limit exceeded, connection refused");

        [...]
    }
    Close(connfd);  /* parent closes connected socket */
}

信号処理:

Sigfunc *signal (int signo, Sigfunc *func)
{
    struct sigaction    act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    if (signo == SIGALRM) {
#ifdef  SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;   /* SunOS 4.x */
#endif
    }
    else {
#ifdef  SA_RESTART
        act.sa_flags |= SA_RESTART;     /* SVR4, 44BSD */
#endif
    }
    if (sigaction(signo, &act, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}

Sigfunc *Signal (int signo, Sigfunc *func)
{
    Sigfunc *sigfunc;

    if ( (sigfunc = signal(signo, func)) == SIG_ERR)
        err_sys("signal error");
    return sigfunc;
}

void sig_chld (int signo __attribute__((__unused__)))
{
    pid_t pid;
    int stat;

    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
        --sproc_counter;
        err_msg("child %d terminated", pid);
    }
    return;
}

: 大文字で始まるすべての関数 ( 、など) は、小文字の友達 ( 、 など) と同じように動作し、同じように動作しFork()ますが、より優れたエラー処理を備えています。ステータス。Close()Signal()fork()close()signal()

注 2 : Debian Testing( kernel v3.10.11) を使用して実行およびコンパイルしgcc 4.8.2ます。

4

2 に答える 2