This experiment makes us more familiar with process control and signal control by implementing a Unix Shell that supports job control. The course Lab has helped us build the overall framework of Shell and realize the code that is not very relevant to this experiment. We need to complete the core part by ourselves.
The overall framework
The Shell reads the commands entered by the user from the standard input (stdin) and then parses the commands. The Shell supports two types of commands: if the user enters a built-in command (quit, jobs, etc.), the command is executed directly. If the user entered a path to an executable file, fork a child process in which the command is loaded and executed. The Shell abstracts each command entered by a user into a single job, which can contain multiple processes (such as pipes). Each job can be run in two ways. If the command entered by the user ends with ‘&’, the job will be run in background; otherwise, the job will be run in foreground. At any time, only 0 or 1 foreground jobs can exist, but 0 or more background jobs can run. Finally, to enable the user to send signals to the Shell, we need to implement three signal handlers, SIGCHLD, SIGINT, and SIGTSTP.
Something to watch out for
- By default, a child process and its parent process belong to the same process group, while
Unix
Many of the mechanisms provided by the system to send signals to processes are based on the concept of process groups. When we type inCtrl + C
, the kernel will send oneSIGINT
Signals to each process in the foreground process group, similarly, inputCtrl + Z
Causes the kernel to send oneSIGTSTP
Signals to each process in the foreground process group. The foreground process group here refers toShell
The process group to which the process belongs. In the experiment, we don’t expect the signal to act directly on phiShell
The process itself (otherwiseShell
receivedSIGINT
The signal stops), but needs to letShell
To forward the signal toShell
The child processes in the foreground job and all the processes in the process group to which they belong. So, we can’t have the child process sumShell
All processes belong to the same process group. The way to do this is through usesetpgid
Function to change the subprocess’s process group when calledsetpgid(0, 0)
“, the kernel creates a new process group, its process groupID
Belongs to the caller processPID
And adds the caller process to the process group. - when
Shell
When a signal is received, the specific work needs to be done by the signal processing function. Such as receivedSIGINT
Signal, then the signal processing function will send that signal to the front stationjob
The process and all processes in the process group to which it belongs. In the experiment, we were throughkill(pid_t pid, int sig)
To send a signal, notice that we’re not just sending a signalPID = pid
The process sends a signal,kill
The function helps us achieve this: ifpid
Less than0
.kill
Send a signalsig
To process group|pid|
(pid
Absolute value of). We can realize that the previous caveat sets the stage for this. - The parent process (
Shell
)fork
When a child process is created, the parent process needs to treat that process as a child processjob
Added to thejob
In the queue (addjob
), the kernel sends one when the child process terminatesSIGCHLD
Signal to the parent process, and then, in the corresponding signal handler, to the terminated child processjob
fromjob
Delete from the queue (deletejob
). Consider one case: being a parent processfork
After a child process is created, the child process gets the schedule before the parent and executes in the parent processaddjob
The child process has already terminated and sentSIGCHLD
Signal to the parent process. At this point, in the signal handlerdeletejob
Nothing will be done because the parent process has not yet calledjob
To join thejob
In the queue. The root cause of this problem is inaddjob
I calleddeletejob
. The solution to this problem is in the parent processfork
Before the child process willSIGCHLD
Signal block when finishedaddjob
After that, it was unpairedSIGCHLD
Signal to block, so that the child process is guaranteed to be added tojob
The child process is then reclaimed from the queue. Note that child processes inherit the set of blocked signals from their parent, so we have to callexecve
Before unblocking the child processSIGCHLD
The signal.
code
The code for Shell Lab is here.