代码之家  ›  专栏  ›  技术社区  ›  Arkaik zx81

pclose似乎使进程失败

  •  0
  • Arkaik zx81  · 技术社区  · 6 年前

    此问题是此问题的后续问题: Controlling a C daemon from another program

    我的目标是从另一个程序控制守护进程的执行。
    守护进程的代码非常简单。

    int main()
    {
      printf("Daemon starting ...\n");
      openlog("daemon-test", LOG_PID, LOG_DAEMON);
    
      syslog(LOG_INFO, "Daemon started !\n");
    
      while(1)
      {
        syslog(LOG_INFO, "Daemon alive - pid=%d, pgid=%d\n", getpid(), getpgrp());
        sleep(1);
      }
    
      return EXIT_SUCCESS;
    }
    

    我为这个守护进程实现了一个systemv init脚本,如下所示

    #!/bin/sh
    
    NAME=daemon-test
    DAEMON=/usr/bin/${NAME}
    SCRIPTNAME=/etc/init.d/${NAME}
    USER=root
    RUN_LEVEL=99
    PID_FILE=/var/run/${NAME}.pid
    RETRY=3
    
    start_daemon()
    {
        start-stop-daemon --start --background --name ${NAME} --chuid ${USER} --nicelevel ${RUN_LEVEL} --make-pidfile --pidfile ${PID_FILE} --exec ${DAEMON}
        ret=$?
    
        if [ "$ret" -eq 0 ]; then
            echo "'${NAME}' started"
        elif [ "$ret" -eq 1 ]; then
            echo "'${NAME}' is already running"
        else
            echo "An error occured starting '${NAME}'"
        fi
        return ${ret}
    }
    
    stop_daemon()
    {
        start-stop-daemon --stop --retry ${RETRY} --remove-pidfile --pidfile ${PID_FILE} --name ${NAME} --signal 9
        ret=$?
    
        if [ "$ret" -eq 0 ]; then
            echo "'${NAME}' stopped"
        elif [ "$ret" -eq 1 ]; then
            echo "'${NAME}' is already stopped"
        elif [ "$ret" -eq 2 ]; then
            echo "'${NAME}' not stopped after ${RETRY} tries"
        else
            echo "An error occured stopping '${NAME}'"
        fi
        return ${ret}
    }
    
    status_daemon()
    {
        start-stop-daemon --status --pidfile ${PID_FILE} --name ${NAME}
        ret=$?
    
        if [ "$ret" -eq 0 ]; then
            echo "'${NAME}' is running"
        elif [ "$ret" -eq 1 ]; then
            echo "'${NAME}' stopped but pid file exits"
        elif [ "$ret" -eq 3 ]; then
            echo "'${NAME}' stopped"
        elif [ "$ret" -eq 4 ]; then
            echo "Unable to get '${NAME}' status"
        else
            echo "Unknown status : ${ret}"
        fi
        return ${ret}
    }
    
    case "$1" in
      start)
        echo "Starting '${NAME}' deamon :"
        start_daemon
        ;;
      stop)
        echo "Stopping '${NAME}' deamon :"
        stop_daemon
        ;;
      status)
        echo "Getting '${NAME}' deamon status :"
        status_daemon
        ;;
      restart|reload)
        "$0" stop
        "$0" start
        ;;
      *)
        echo "Usage: $0 {start|stop|status|restart}"
        exit 1
    esac
    
    exit $?
    

    从命令行使用这个脚本来控制守护进程的执行效果良好。


    所以现在的目标是使用另一个C程序中的这个脚本来启动守护进程,并从这个程序控制它的执行。

    我已经实现了一个简单的C程序,它:

    1. 使用“start”参数启动脚本
    2. 等待PID文件创建
    3. 从pid文件读取守护进程的pid
    4. 通过检查文件是否存在,定期检查守护进程是否处于活动状态 /proc/<daemon_pid>/exec
    5. 如果守护进程被杀死,重新启动它

    这就是我面临的问题。只有当我不打电话的时候,程序才能正常工作 pclose .

    这是程序代码

    #define DAEMON_NAME       "daemon-test"
    #define DAEMON_START_CMD  "/etc/init.d/" DAEMON_NAME " start"
    #define DAEMON_STOP_CMD   "/etc/init.d/" DAEMON_NAME " stop"
    #define DAEMON_PID_FILE   "/var/run/" DAEMON_NAME ".pid"
    
    int main()
    {
        char daemon_proc_path[256];
        FILE* daemon_pipe = NULL;
        int daemon_pid = 0;
        FILE* fp = NULL;
        int ret = 0;
        int i = 0;
    
        printf("Launching '%s' program\n", DAEMON_NAME);
        if(NULL == (daemon_pipe = popen(DAEMON_START_CMD, "r")))
        {
            printf("An error occured launching '%s': %m\n", DAEMON_START_CMD);
            return EXIT_FAILURE;
        }
        #ifdef USE_PCLOSE
        else if(-1 == (ret = pclose(daemon_pipe)))
        {
            printf("An error occured waiting for '%s': %m\n", DAEMON_START_CMD);
            return EXIT_FAILURE;
        }
        #endif
        else
        {
            printf("Script exit status : %d\n", ret);
    
            while(0 != access(DAEMON_PID_FILE, F_OK))
            {
                printf("Waiting for pid file creation\n");
                sleep(1);
            }
            if(NULL == (fp = fopen(DAEMON_PID_FILE, "r")))
            {
                printf("Unable to open '%s'\n", DAEMON_PID_FILE);
                return EXIT_FAILURE;
            }
            fscanf(fp, "%d", &daemon_pid);
            fclose(fp);
            printf("Daemon has pid=%d\n", daemon_pid);
            sprintf(daemon_proc_path, "/proc/%d/exe", daemon_pid);
        }
    
        while(1)
        {
            if(0 != access(daemon_proc_path, F_OK))
            {
                printf("\n--- Daemon (pid=%d) has been killed ---\n", daemon_pid);
                printf("Relaunching new daemon instance...\n");
                if(NULL == (daemon_pipe = popen(DAEMON_START_CMD, "r")))
                {
                    printf("An error occured launching '%s': %m\n", DAEMON_START_CMD);
                    return EXIT_FAILURE;
                }
                #ifdef USE_PCLOSE
                else if(-1 == (ret = pclose(daemon_pipe)))
                {
                    printf("An error occured waiting for '%s': %m\n", DAEMON_START_CMD);
                    return EXIT_FAILURE;
                }
                #endif
                else
                {
                    printf("Script exit status : %d\n", ret);
    
                    while(0 != access(DAEMON_PID_FILE, F_OK))
                    {
                        printf("Waiting for pid file creation\n");
                        sleep(1);
                    }
                    if(NULL == (fp = fopen(DAEMON_PID_FILE, "r")))
                    {
                        printf("Unable to open '%s'\n", DAEMON_PID_FILE);
                        return EXIT_FAILURE;
                    }
                    fscanf(fp, "%d", &daemon_pid);
                    fclose(fp);
                    printf("Daemon has pid=%d\n", daemon_pid);
                    sprintf(daemon_proc_path, "/proc/%d/exe", daemon_pid);
                }
            }
            else
            {
                printf("Daemon alive (pid=%d)\n", daemon_pid);
            }
            sleep(1);
        }
    
        return EXIT_SUCCESS;
    }
    

    据我所知 锁相环 应该等待子进程终止,只有当子进程返回时,它才会关闭管道。

    所以我不明白为什么我的实现 锁相环 不打电话就不工作。

    这是带和不带 锁相环 块注释

    没有 锁相环 打电话:

    # ./popenTest 
    Launching 'daemon-test' program
    Script exit status : 0
    Waiting for pid file creation
    Daemon has pid=435
    Daemon alive (pid=435)
    Daemon alive (pid=435)
    Daemon alive (pid=435)
    Daemon alive (pid=435)
    

    锁相环 打电话:

    # ./popenTest 
    Launching 'daemon-test' program
    Script exit status : 36096
    Waiting for pid file creation
    Waiting for pid file creation
    Waiting for pid file creation
    Waiting for pid file creation
    

    如您所见,守护进程从不启动,PID文件也从不创建。

    即使我的程序没有 锁相环 我想了解呼叫的潜在问题 锁相环 .

    为什么使用 锁相环 如果行为良好而不调用程序,会导致程序失败?


    编辑:

    以下是错误案例的更多信息

    errno是 Success
    wifexited宏返回true
    wexitstatus宏返回141

    通过进一步的调试,我已经注意到修改init脚本以将输出记录到一个文件中可以使它工作… 为什么?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Jonathan Leffler    6 年前

    你使用 popen(DAEMON_START_CMD, "r") . 这意味着“守护进程观察程序”正在读取“守护进程启动程序”脚本的标准输出。如果你 pclose() 该管道,脚本将写入标准输出并获取一个sigpipe,因为该管道的读取端已关闭。这是否发生在实际的守护进程启动之前,还存在争论和时间问题。

    不要 pclose()。 管道,直到您知道守护进程启动程序已退出为止。我个人会用 pipe() , fork() execv() (或 exec 功能系列直接。我不认为 popen() 是适合这项工作的工具。但是如果你要用 波本() ,然后读取输入,直到不再获取(EOF),然后使用 pclose()。 安全。您不必打印您所阅读的内容,尽管这样做是常规的和明智的,“守护进程启动程序”脚本会告诉您有用的信息。

    检查进程ID是否仍在运行的经典方法是使用 kill(daemon_pid, 0) . 如果执行的进程具有适当的特权(与进程相同的uid,或 root 特权),这是可行的。如果你不能向PID发送一个激活信号,那就没有帮助了。

    (我想 start-stop-daemon 是一个程序,可能是一个C程序而不是shell脚本,它以守护进程的形式启动另一个程序。我有一个类似的程序 daemonize 它也用于将未专门设计为守护进程的程序转换为作为守护进程运行的程序。许多程序都不能很好地工作,守护进程会考虑什么是守护进程 ls , grep , ps sort 意思是。其他程序可以更明智地作为守护进程运行。)