后台和挂起的进程 - 在C中实现作业控制Shell

时间:2018-05-16 18:04:37

标签: c linux shell unix job-control

我在Linux中用C实现作业控制Shell,作为与操作系统相关的主题的项目。我有一个main()函数执行子进程管理,帮助一个链接列表,如下所示,其中存储了后台和暂停的作业信息:

typedef struct job_
{
    pid_t pgid; /* group id = process lider id */
    char * command; /* program name */
    enum job_state state;
    struct job_ *next; /* next job in the list */
} job;

每次子进程退出或停止时,都会向父进程发送SIGCHLD以通知该进程。然后,我有一个如此处所示的信号处理程序,对于该作业状态链表的每个节点,检查该节点中表示的进程是否已退出,如果已经退出,则该节点将从链表中删除。 以下是SIGCHLD处理程序的代码,其中' job_list'是存储信息的链接列表:

void mySIGCHLD_Handler(int signum) {
    block_SIGCHLD();
    if (signum == 17) {
        job *current_node = job_list->next, *node_to_delete = NULL;
        int process_status, process_id_deleted;

        while (current_node) {

            /* Wait for a child process to finish.
            *    - WNOHANG: return immediately if the process has not exited
            */
            waitpid(current_node->pgid, &process_status, WNOHANG);

            if (WIFEXITED(process_status) != 0) {
                node_to_delete = current_node;
                current_node = current_node->next;
                process_id_deleted = node_to_delete->pgid;
                if (delete_job(job_list, node_to_delete)) {
                printf("Process #%d deleted from job list\n", process_id_deleted);
                } else {
                    printf("Process #%d could not be deleted from job list\n", process_id_deleted);
                }
            } else {
                current_node = current_node->next;
            }
        }
    }
    unblock_SIGCHLD();
}

问题是,当调用处理程序时,一些不应该删除的条目因为它们代表的进程没有退出而被删除,当它们不应该被删除时。任何人都知道为什么会这样吗?

谢谢,抱歉你的失去时间:(

1 个答案:

答案 0 :(得分:3)

我在这段代码中看到了很多问题,但是可能存在一个直接的问题:

        waitpid(current_node->pgid, &process_status, WNOHANG);
        if (WIFEXITED(process_status) != 0) {

waitpid(pid, &status, WNOHANG)因为进程尚未退出而返回时,它不向status写入任何内容,因此后续的if分支在垃圾上。在假设waitpid有意义之前,您需要检查status的实际返回值。

最重要的其他问题是:

  • 允许内核只发送一个SIGCHLD来告诉您已退出多个进程。获得SIGCHLD后,您需要在循环中调用waitpid(0, &status, WNOHANG),直到它告诉您没有其他进程需要等待,并且您需要处理(没有双关语) all < / em>它告诉您的退出流程ID。

  • 从异步信号处理程序调用{​​{1}}或printf是不安全的。将已终止的进程添加到延迟任务列表中。确保在使用该列表的主循环代码中阻止SIGCHLD。

  • 不要在处理程序中阻止和解除阻塞free;这有一个不可避免的竞争条件。相反,让内核通过正确设置信号处理程序,以原子方式为您执行此操作:使用SIGCHLD并且不要将sigaction放入SA_NODEFER。 ( sa_flags放入SA_RESTART,除非您有充分理由不这样做。)

  • 字面数17应该是信号常数sa_flags历史记录中所有Unix上的某些信号编号都是稳定的,但SIGCHLD不是其中之一。