0

作業プロセスを回復する機能を持つデーモンを書いていますCentOS release 5.7 (Final)

コードの例を次に示します。

#define CHILD_NEED_WORK                 1
#define CHILD_NEED_TERMINATE    2

int ReloadConfig()
{
    ....
    return 0;
}

void DestroyWorkThread()
{...}

int InitWorkThread()
{
     ...
    return 0;
}

int LoadConfig(char* FileName)
{
     ...
    return 0;
}

void SetPidFile(char* Filename)
{
    FILE* f;
    f = fopen(Filename, "w+");
    if (f)
    {
        fprintf(f, "%u\n", getpid());
        fclose(f);
    }
}

int SetFdLimit(int MaxFd)
{
    struct rlimit lim;
    int          status;

    lim.rlim_cur = MaxFd;
    lim.rlim_max = MaxFd;

    status = setrlimit(RLIMIT_NOFILE, &lim);

    return status;
}

//Monitor process
int MonitorProc()
{
    int      pid;
    int      status;
    int      need_start = 1;
    sigset_t sigset;
    siginfo_t siginfo;

    parent_pid = getpid();

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGCHLD);

    sigaddset(&sigset, SIGUSR1);

    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetPidFile(PID_FILE);

    for (;;)
    {
        if (need_start)
        {
            pid = fork();
        }

        need_start = 1;

        if (pid == -1)
        {

        }
        else if (!pid) 
        {
            status = WorkProc();

            exit(status);
        }
        else
        {
            sigwaitinfo(&sigset, &siginfo);

            if (siginfo.si_signo == SIGCHLD)
            {
                wait(&status);

               status = WEXITSTATUS(status);

                 if (status == CHILD_NEED_TERMINATE)
                 {
                     Write("[MONITOR] Child stopped");
                     break;
                 }
                 else if (status == CHILD_NEED_WORK)
                 {
                     Write("[MONITOR] Child restart");
                 }
             }
             else if (siginfo.si_signo == SIGUSR1)
             {
                 kill(pid, SIGUSR1); 
                 need_start = 0;
             }
             else if (siginfo.si_signo == 0) 
             {
                need_start = 0;
                continue;
             }
             else
             {
                 Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
                 kill(pid, SIGTERM);
                 status = 0;
                 break;
             }
         }
     }

     Write("[MONITOR] Stop");

     unlink(PID_FILE);

     return status;
}

//Work process
int WorkProc()
{
    struct sigaction sigact;
    sigset_t         sigset;
    int             signo;
    int             status;

    sigact.sa_flags = SA_SIGINFO;

    sigact.sa_sigaction = signal_error_for_backtrace;

    sigemptyset(&sigact.sa_mask);


    sigaction(SIGFPE, &sigact, 0);
    sigaction(SIGILL, &sigact, 0);
    sigaction(SIGSEGV, &sigact, 0);
    sigaction(SIGBUS, &sigact, 0);

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGUSR1);
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetFdLimit(FD_LIMIT);


    status = InitWorkThread();


    if (!status)
    {
        for (;;)
        {
            sigwait(&sigset, &signo);

            if (signo == SIGUSR1)
            {
                status = ReloadConfig();
                if (status)
                {
                    Write("[DAEMON] Reload config failed");
                }
                else
                {
                    Write("[DAEMON] Reload config OK");
                }
            }
            else
            {
                break;
            }
        }

        DestroyWorkThread();
    }
    else
    {
        Write("[DAEMON] Create work thread failed");
    }

    Write("[DAEMON] Stopped");


    return CHILD_NEED_TERMINATE;
}


int main(int argc , char *argv[])
{

    if (argc != 2)
    {
        printf("Usage: ./test_daemon.conf failed!\n");
        return -1;
    }

    status = LoadConfig(argv[1]);
    if (status) 
    {
        printf("Error: Load config failed\n");
        return -1;
    }

    if (CheckForAnotherInstance())
    {
    printf("Daemon is already running!\n");
    return 1;
    }

    pid = fork();
    if (pid == -1)
    {
        printf("Error: Start Daemon failed (%s)\n", strerror(errno));
        return -1;
    }
    else if (!pid)
    {
        umask(0);
        setsid();

        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        //Monitor process startup
        status = MonitorProc();
        return status;
    }
    else
    {
        return 0;
    }
    return 0;
}

主な作業を生成する作業プロセスと、作業プロセスからのシグナルを待ち、必要なシグナルを受信した場合に再起動する監視プロセスの 2 つのプロセスを使用します。コマンドkill -s SIGCHLDを使用して、親プロセス - 監視プロセス - にシグナルを送信しようとすると、このシグナルが受信されます。

子プロセスを終了しようとすると、親プロセスは SIGCHLD シグナルを受信しません。シグナルを待ち続け、子プロセスがゾンビに変わります。

しかし、親プロセスでユーティリティstraceを使用すると、すべて正常に動作します。子プロセスは正常に終了し、親プロセスは SIGCHLD シグナルを受け取ります。

SIGCHLD シグナルの受信に使用する関数 waitpid() について読みましたが、親プロセスでも別のシグナルを受信したいと考えています。

何か案は?

4

2 に答える 2

0

SIGCHLD を SIG_BLOCK すると、シグナルが受信されなくなります。しかし、これは sigwaitinfo() を使用し続けるため問題ありませんが、wait() を実行するときに siginfo.si_pid を使用できません。 ().

最初に WIFEXITED(status) をチェックせずに WEXITSTATUS() を使用します。wait() のマニュアル ページを参照してください。

モニターと作業プロセスは、後で exec() なしで fork() を実行するのと同じ実行可能ファイルを使用しているように見えます。したがって、子のシグナルハンドラーの状態を復元して、子のコードが正常に動作するように注意してください。たとえば、監視プロセスは親ですか? したがって、子を取得するには fork() を実行してから WorkProc() を呼び出します。WorkProc() 内では、一連のシグナルをブロックします (ただし、SIGCLD はブロックしません)。ただし、MonitorProc() からの sigprocmask(SIG_BLOCK, ...) の実行は、WorkProc() 内で引き続きアクティブになります。

「if (siginfo.si_signo == 0)」が何であるかはわかりません。

したがって、別の主なクエリに対して、WorkProc() 関数を実行しているプロセスから SIGCHLD が配信されない理由は、MonitorProc() 内で既にそのシグナルをブロックしているためです。したがって、この問題を修正するには、sigprocmask() の 3 番目の引数を使用して、元のブロック/ブロック解除マスクを MonitorProc() に保存し、fork() を使用して WorkProc() にジャンプする前に、ブロック/ブロック解除マスクを復元します。

于 2015-05-28T01:01:27.320 に答える