しばらく前に、自動 S/MIME 処理用の単純な SMTP ゲートを書きましたが、今度はテストに取り掛かります。メール サーバーでは一般的であるように、メイン プロセスは着信接続ごとに子プロセスを fork します。作成する子プロセスの数を制限することは良い習慣です。
負荷が高い (多数のクライアントからの多数の接続が同時に発生している) 場合、子プロセスが正しくカウントされていないように見えます。問題は、子プロセスが終了したときにカウンターが減少することです。負荷が高い状態が数分続くと、カウンターが実際の子プロセスの数よりも大きくなります (つまり、5 分後には 14 になりますが、何もありません)。
すでにいくつかの調査を行いましたが、何も機能しませんでした。ゾンビプロセスはすべてリープされているので、SIGCHLD
ハンドリングは問題ないようです。同期の問題かもしれないと思ったのですが、ミューテックスを追加して変数の型をvolatile sig_atomic_t
(今のままで)変えても変化なし。シグナルマスキングでも問題ないので、 を使って全シグナルをマスキングしてみsigfillset(&act.sa_mask)
ました。
waitpid()
ときどき奇妙な PID 値 (172915914 のように非常に大きい) を返すことに気付きました。
質問といくつかのコード。
- 他のプロセス (つまり
init
) がそれらの一部を取得している可能性はありますか? - 終了後にプロセスがゾンビにならないことはありますか? 自動的に刈り取ることはできますか?
- 修正方法は?多分それらを数えるより良い方法はありますか?
で子をフォークする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
ます。