如何为SIGCHLD制作信号处理程序,以便在shell中获得后台进程?

时间:2016-11-06 03:04:11

标签: c linux shell process

目前我正在制作一个shell,它可以正常运行它所执行的前台进程。现在我必须实现后台进程和作业控制,我真的很困惑我应该如何处理它。我明白,如果我想在后台运行proccesses,我应该设置他们的pgid而不是等待它们,但是当我试图收获它们时我碰到了墙...

我有两个结构:工作和流程

typedef struct job {
    int is_background_job;
    pid_t pgid;
    int job_status;
    process *p;         // List of processes to execute for this job
    struct job *next;   // If a background job, it will be in a global linked list of job structs 
} job;

typedef struct process {
    char **argv;
    process *next;      // The next process to pipe to
} process;

重要的一点是,作业由一个过程结构的链表组成,并且有一个全局的作业结构列表,代表我正在进行的所有后台作业。

shell算法是一种标准。

壳:

get cmd from terminal
parse cmd and create a job struct filled with processes
if job is a background job:
  add it to global background job list
fork(), setpgid, and do the piping in a while loop of the job's processes   
if job is a foreground process:
  wait on the forked processes
else:
  don't wait // since it's a background process
continue to loop and read next cmd from terminal

现在我的问题在这里。如果我有一堆正在后台执行的进程,这意味着它们中的任何一个(来自任何一个后台作业)都可以简单地结束然后发送SIGCHLD。令人讨厌的部分是,如果某个作业的所有进程都结束,我必须从全局作业列表中删除该作业。我不认为我可以在我的SIGCHLD处理程序中调用waitpid循环(-1,& status,WNOHANG),因为在信号处理程序中,forground进程当前正在执行finsihes的可能性很小。

这是否意味着我必须在获得SIGCHLD后立即对每个作业的每个进程进行waitpid(),以便我只等待非fg进程?

我并不真正需要代码,只是解释一个好方法。

1 个答案:

答案 0 :(得分:1)

我知道现在这已经很晚了,可能对你来说无关紧要,但是我也在研究shell,并在试图找出同样的问题时遇到了你的问题。

无论如何,似乎这里的问题是让主进程等待所有前台子进程,但这对waitpid处理程序中SIGCHLD的调用很困难。我是如何做到这一点的,而不是在主进程中使用wait()调用,我只是在处理程序中获取所有进程并从适当的列表中删除它们(我目前有一个所有前台进程的列表和所有背景的列表过程),然后在我做的主要过程中:

while(foreground process list is not empty)
    pause();

pause()调用只是将进程置于睡眠状态,直到被信号唤醒为止,在这种情况下,SIGCHLD将始终唤醒进程,以便再次检查是否有任何前景仍在运行的进程需要等待。希望这有助于某人!

编辑:刚刚意识到如果孩子在您检查列表不为空之后,以及在您致电pause()之前终止,这可能会导致竞争条件。解决这个问题的简单方法是使用nanosleep而不是暂停,它仍然会被信号中断,但是您可以选择定期检查列表大小,这样可以避免在这种情况下的竞争条件