如何为 SIGCHLD 创建一个信号处理程序,它将在 shell 中获取后台进程?

How to make a signal handler for SIGCHLD that will reap background processes in shell?

目前我正在制作一个 shell 并且它在它执行的前台进程中运行良好。现在我必须实施后台进程和作业控制,老实说,我对应该如何处理它感到困惑。我知道如果我想 运行 在后台处理进程,我应该设置它们的 pgid 而不是等待它们,但是我在尝试获取它们时遇到了困难...

我有两个结构:job 和 process

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算法是一种标准。

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) 循环,因为在信号处理程序中,前台进程当前正在执行 finsihes 的机会很小。

这是否意味着一旦获得 SIGCHLD,我就必须在每个作业的每个进程上执行 waitpid() 以便我只等待非 fg 进程?

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

我知道这已经很晚了,现在可能与您无关,但我也在研究 shell 和 运行 您的问题,同时试图找出同样的问题。

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

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

pause() 调用只是让进程进入睡眠状态,直到它被信号唤醒,在这种情况下 SIGCHLD 将始终唤醒进程,以便可以再次检查是否有任何前台进程仍然 运行 需要等待。希望这对某人有所帮助!

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