【问题标题】:Background and suspended processes - Implementing a Job Control Shell in C后台进程和挂起进程 - 在 C 中实现作业控制外壳
【发布时间】:2018-10-26 21:25:58
【问题描述】:

我正在 Linux 中以 C 语言实现作业控制外壳,作为操作系统相关主题的项目。我有一个执行子进程管理的 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();
}

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

感谢您,并为您失去的时间感到抱歉:(

【问题讨论】:

    标签: c linux shell unix job-control


    【解决方案1】:

    我在这段代码中看到了很多问题,但最直接的问题可能就在这里:

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

    waitpid(pid, &status, WNOHANG)因为进程没有退出而返回时,它不会向status写任何东西,所以后面的if是在垃圾上分支的。在假设status 有意义之前,您需要检查waitpid 的实际返回值。

    其他最重要的问题是:

    • 内核只允许发送一个SIGCHLD来告诉你有几个进程已经退出。当您收到SIGCHLD 时,您需要循环调用waitpid(0, &status, WNOHANG),直到它告诉您没有更多的进程等待,并且您需要处理(没有双关语)所有它告诉您的已退出进程 ID。

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

    • 不要在处理程序中阻止和解除阻止SIGCHLD自己;有一个不可避免的竞争条件。相反,让内核自动地为你做这件事,通过正确设置你的信号处理程序:使用sigaction 并且不要将SA_NODEFER 放入sa_flags。 (SA_RESTART 放入sa_flags,除非你有充分的理由不这样做。)

    • 文字数字 17 应该是信号常量 SIGCHLD一些信号编号在整个历史上的所有 Unix 中都是稳定的,但 SIGCHLD 不是其中之一。

    【讨论】:

    • 这样一个惊人的答案!这就是我猜的问题,现在一切正常。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2015-06-19
    • 2016-11-04
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    相关资源
    最近更新 更多