I am programming a shell for my CS class and part of the project involves running a process in the background if the '&' character is passed in by the user.
If a process is run in the foreground, I simply execvp
the process and it remains in control of the terminal since it is in the foreground. However, if it is a background process, I must return control to my main shell after starting the execution of the process. I understand that the system call tcsetpgrp(pid_t)
places the process passed in as argument in the foreground, but I don't quite understand how to use it.
Should I call tcsetpgrp
after execvp
if it is a background process? If so, can I obtain the pid of my shell just by calling getpid
?
tcsetpgrp()
works on process groups, not individual processes. What you want to do is this:
When you create a new pipeline, call setpgid()
to put all the members of the pipeline in a new process group (with the PID of the first process in the pipeline as the PGID). (A pipeline is a sequence of processes started by your shell when it sees a request like ls | grep foo | wc -l
- the simplest pipeline has just one process in it). Normally you would call setpgid(0, 0)
from the first process in the pipeline, before calling exec()
.
Use tcsetpgrp()
to manage which process group is in the foreground. If you move a process group from the foreground to the background, you would set the shell's own process group as the foreground process group - you can get this with getpgid(0)
in the shell.
When the shell is in the background, it should use a blocking waitpid()
call to wait for a child process to exit rather than show a prompt. Once every process in the foreground pipeline has exited, it should put itself back into the foreground again (and show a prompt).
When the shell is in the foreground, it should call waitpid()
with the WNOHANG
and WUNTRACED
flags to check on the status of child processes just before you show the prompt - this will inform you when they have stopped or exited, and let you inform the user.
You should take a look at fork to create the child process. Then, use exec
in the child to run the desired command.
If your shell starts a process using &
to background it, the $!
shell variable will contain the PID of the child process. You can then send signals to that process, or use wait $!
to wait for it to complete.