如何为 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
而不是暂停,它仍然会被信号打断,但让您可以选择定期检查列表大小,这避免了这种情况下的竞争条件
目前我正在制作一个 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
而不是暂停,它仍然会被信号打断,但让您可以选择定期检查列表大小,这避免了这种情况下的竞争条件