目前我正在制作一个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进程?
我并不真正需要代码,只是解释一个好方法。
答案 0 :(得分:1)
我知道现在这已经很晚了,可能对你来说无关紧要,但是我也在研究shell,并在试图找出同样的问题时遇到了你的问题。
无论如何,似乎这里的问题是让主进程等待所有前台子进程,但这对waitpid
处理程序中SIGCHLD
的调用很困难。我是如何做到这一点的,而不是在主进程中使用wait()
调用,我只是在处理程序中获取所有进程并从适当的列表中删除它们(我目前有一个所有前台进程的列表和所有背景的列表过程),然后在我做的主要过程中:
while(foreground process list is not empty)
pause();
pause()
调用只是将进程置于睡眠状态,直到被信号唤醒为止,在这种情况下,SIGCHLD
将始终唤醒进程,以便再次检查是否有任何前景仍在运行的进程需要等待。希望这有助于某人!
编辑:刚刚意识到如果孩子在您检查列表不为空之后,以及在您致电pause()
之前终止,这可能会导致竞争条件。解决这个问题的简单方法是使用nanosleep
而不是暂停,它仍然会被信号中断,但是您可以选择定期检查列表大小,这样可以避免在这种情况下的竞争条件