简体   繁体   English

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

[英]Background and suspended processes - Implementing a Job Control Shell in C

I'm implementing a Job Control Shell in C in Linux as a project for a Operating Systems-related subject. 我正在Linux中以C语言实现作业控制外壳,作为与操作系统相关的主题的项目。 I have a main() function that does child process management, helped with a linked list as shown here in which background and suspended jobs information is stored: 我有一个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;

Every time a child process exits or is stopped, a SIGCHLD is sent to parent process to be informed about that. 每次子进程退出或停止时,都会将SIGCHLD发送给父进程,以告知有关此信息。 Then, I have a signal handler as shown here that for each node of that job status linked list, checks if the process represented in that node has exited and if it did, that node is removed from the linked list. 然后,我有一个信号处理程序,如此处所示,对于该作业状态链表的每个节点,检查该节点中表示的进程是否已经退出,如果已经退出,则将该节点从链表中删除。 Here is the code for the SIGCHLD handler, where 'job_list' is the linked list where the info is stored: 以下是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();
}

The thing is, when the handler is called, some entries that should not be deleted because the process they represent are not exited, are deleted, when they shouldn't. 关键是,在调用处理程序时,不应删除某些不应删除的条目,因为它们所代表的进程没有退出,因此不应删除它们。 Anyone would know why that happens? 任何人都知道为什么会这样吗?

Thank you and sorry for your lost time :( 谢谢你,我很抱歉失去你的时间:(

I see many problems in this code, but the immediate issue is probably here: 我在这段代码中看到了很多问题,但是直接的问题可能在这里:

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

When waitpid(pid, &status, WNOHANG) returns because the process has not exited, it does not write anything to status , so the subsequent if is branching on garbage. waitpid(pid, &status, WNOHANG)由于该进程尚未退出而返回时,它不会向status写入任何内容,因此随后的if在垃圾上分支。 You need to check the actual return value of waitpid before assuming status is meaningful. 在假设status有意义之前,您需要检查waitpid的实际返回值。

The most important other problems are: 最重要的其他问题是:

  • The kernel is allowed to send only one SIGCHLD to tell you that several processes have exited. 允许内核仅发送一个SIGCHLD来告诉您已经退出了多个进程。 When you get a SIGCHLD , you need to call waitpid(0, &status, WNOHANG) in a loop until it tells you there are no more processes to wait for, and you need to process (no pun intended) all of the exited process IDs that it tells you about. 当您收到SIGCHLD ,您需要循环调用waitpid(0, &status, WNOHANG) ,直到它告诉您不再有等待的进程,并且您需要处理(没有双关语) 所有退出的进程ID它告诉你有关。

  • It is not safe to call printf or free from an asynchronous signal handler. 调用printf或从异步信号处理程序中free是不安全的。 Add terminated processes to a list of deferred tasks, instead. 而是将终止的进程添加到延迟任务列表中。 Make sure to block SIGCHLD in the main-loop code that consumes that list. 确保在使用该列表的主循环代码中阻止SIGCHLD。

  • Don't block and unblock SIGCHLD yourself in the handler; 不要自己在处理程序中阻塞和解除阻塞SIGCHLD that has an unavoidable race condition. 具有不可避免的比赛条件。 Instead, let the kernel do it for you, atomically, by setting up your signal handler correctly: use sigaction and don't put SA_NODEFER in sa_flags . 相反,让内核通过正确设置信号处理程序为您完成原子操作:使用sigaction ,不要将SA_NODEFER放在sa_flags ( Do put SA_RESTART in sa_flags , unless you have a very good reason not to.) (除非您有充分的理由不这样做,请务必SA_RESTART放入sa_flags中。)

  • The literal number 17 should be the signal constant SIGCHLD instead. 文字数字17应该是信号常数SIGCHLD Some signal numbers have been stable across all Unixes throughout history, but SIGCHLD is not one of them. 在整个历史上, 某些信号在整个Unix上一直稳定,但是SIGCHLD并不是其中之一。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM