当前位置:网站首页>CSAPP shell lab experiment report
CSAPP shell lab experiment report
2022-07-06 15:13:00 【Wuhu hanjinlun】
Preface : It is strongly recommended to finish reading csapp In Chapter 8, do this experiment again , complete tsh.c The code is posted at the end of the article
1. Prepare knowledge
- Process concept 、 State and several functions that control the process (fork,waitpid,execve).
- The concept of signal , Be able to write correct and safe signal processing programs .
- shell The concept of , understand shell How does a program use process management and signals to execute a command line statement .
2. The experiment purpose
shell lab The main purpose is to be familiar with process control and signals . Specifically, we need to compare 16 individual test and rtest The output of the file , Implement seven functions :
void eval(char *cmdline): Analysis of the command , And send child processes to execute The main function is to analyze cmdline And run
int builtin_cmd(char **argv): Parsing and execution bulidin command , Include quit, fg, bg, and jobs
void do_bgfg(char **argv) perform bg and fg command
void waitfg(pid_t pid): Realize blocking and wait for the end of the foreground program
void sigchld_handler(int sig):SIGCHID Signal processing functions
void sigint_handler(int sig): Signal processing functions , Respond to SIGINT (ctrl-c) The signal
void sigtstp_handler(int sig): Signal processing functions , Respond to SIGTSTP (ctrl-z) The signal
3. Experimental environment
ubuntu12.04 (32 position ) Environmental Science
4. Experiment content and operation steps
By reading the experimental instruction, we know that this experiment requires us to complete tsh.c In order to achieve a simple shell, Be able to handle the front and background running programs 、 Able to handle ctrl+z、ctrl+c Equal signal .
Let's look at it first tsh.c The specific content .
First, I define some macros
/* Defined some macros */
#define MAXLINE 1024 /* max line size */
#define MAXARGS 128 /* max args on a command line */
#define MAXJOBS 16 /* max jobs at any point in time */
#define MAXJID 1<<16 /* max job ID */
Four process states are defined
/* Working state */
#define UNDEF 0 /* undefined */
#define FG 1 /* Front desk status */
#define BG 2 /* Backstage status */
#define ST 3 /* Pending state */
And then defined job_t Task class , And created jobs[] Array
struct job_t {
/* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};
struct job_t jobs[MAXJOBS]; /* The job list */
Then there are seven function definitions that we need to complete
void eval(char *cmdline): Analysis of the command , And send child processes to execute The main function is to analyze cmdline And run
int builtin_cmd(char **argv): Parsing and execution bulidin command , Include quit, fg, bg, and jobs
void do_bgfg(char **argv) perform bg and fg command
void waitfg(pid_t pid): Realize blocking and wait for the end of the foreground program
void sigchld_handler(int sig):SIGCHID Signal processing functions
void sigint_handler(int sig): Signal processing functions , Respond to SIGINT (ctrl-c) The signal
void sigtstp_handler(int sig): Signal processing functions , Respond to SIGTSTP (ctrl-z) The signal
Here are some auxiliary functions
int parseline(const char *cmdline, char **argv); // Get parameter list , Returns whether to run the command in the background
void sigquit_handler(int sig); // Handle SIGQUIT The signal
void clearjob(struct job_t *job); // eliminate job Structure
void initjobs(struct job_t *jobs); // Initialization task jobs[]
int maxjid(struct job_t *jobs); // return jobs The largest in the linked list jid Number .
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); // towards jobs[] Add a task
int deletejob(struct job_t *jobs, pid_t pid); // stay jobs[] Delete in pid Of job
pid_t fgpid(struct job_t *jobs); // Return to the current foreground operation job Of pid Number
struct job_t *getjobpid(struct job_t *jobs, pid_t pid); // according to pid Find the corresponding job
struct job_t *getjobjid(struct job_t *jobs, int jid); // according to jid Find the corresponding job
int pid2jid(pid_t pid); // according to pid find jid
void listjobs(struct job_t *jobs); // Print jobs
Then there is mian function , The function is to get commands line by line in the file , And judge whether the file ends (EOF), Will command cmdline Send in eval Function to parse . What we need to do is gradually improve this process
Then start the experiment :
- Use make Command compilation tsh.c file ( If the file changes, you need to use it first make clean Command clear )
[email protected]:~/shlab-handout$ make
gcc -Wall -O2 tsh.c -o tsh
gcc -Wall -O2 myspin.c -o myspin
gcc -Wall -O2 mysplit.c -o mysplit
gcc -Wall -O2 mystop.c -o mystop
gcc -Wall -O2 myint.c -o myint
Use make testXX Command comparison traceXX.txt The document is under preparation shell and reference shell Results of operation ; Or you can use it ”./sdriver.pl -t traceXX.txt -s ./tsh -a “-p”
If you add r, Is the implementation of Standards tshref, Or will tsh Turn into tshref. By comparing Standards tshref And self-control tsh The execution result of , You can observe tsh Whether the function of is correct . If tsh The results of the implementation and tshref Consistent result , It shows that the result is correct
Next, we start to add functions
eval() function
The functionality :eval Function is used to parse and interpret the command line .eval First parse the command line , If the user requests a built-in command quit、jobs、bg or fg( Built in commands ) Then execute immediately . otherwise ,fork Subprocesses and running jobs in the context of subprocesses . If the job is running in the foreground , Wait for it to end , Then return .
The function prototype :void eval(char *cmdline)
, The parameter passed in is cmdline, Command line string
Realize the idea : Copy the book eval Function writing and required functions to complete the function
- First call parseline Function parsing command line , Return directly if it is empty , Then use builtin_cmd Function to determine whether it is a built-in command , return 0 Description is not a built-in command , If it is a built-in command, execute it directly .
- If it's not a built-in command , Then block the signal first ( Specifically, in the fourth point ), Call again fork Create child process . In the subprocess , First, unblock , Set up your own id Number , And then call execve Function to execute job.
- The parent process determines whether the job is running in the background , Yes, call addjob Function will subprocess job Join in job In the list , unblocked , And then call waifg Function waits for the foreground to complete . If you are not working in the background, print the process group jid And subprocesses pid And command line string .
- Because child processes inherit the blocking vector of their parent process , So before executing the new program , The subroutine must ensure the release of SIGCHLD Signal blocking . The parent process must use sigprocmask Before it spawns a child process, it calls fork Block before function SIGCHLD The signal , Then unblock ; By calling addjob Add child processes to the job list , Again using sigprocmask, unblocked .
Complete code :
void eval(char *cmdline)
{
char* argv[MAXARGS]; //execve() The parameters of the function
int state = UNDEF; // Working state ,FG or BG
sigset_t set;
pid_t pid; // process id
// Process the input data
if(parseline(cmdline, argv) == 1) // Parse command line , Return to argv Array
state = BG;
else
state = FG;
if(argv[0] == NULL) // The command line returns null directly
return;
// If it's not a built-in command
if(!builtin_cmd(argv))
{
if(sigemptyset(&set) < 0)
unix_error("sigemptyset error");
if(sigaddset(&set, SIGINT) < 0 || sigaddset(&set, SIGTSTP) < 0 || sigaddset(&set, SIGCHLD) < 0)
unix_error("sigaddset error");
// Block before it spawns a child process SIGCHLD The signal , Prevent competition
if(sigprocmask(SIG_BLOCK, &set, NULL) < 0)
unix_error("sigprocmask error");
if((pid = fork()) < 0) //fork Failed to create child process
unix_error("fork error");
else if(pid == 0) //fork Create child process
{
// The control flow of the child process begins
if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0) // unblocked
unix_error("sigprocmask error");
if(setpgid(0, 0) < 0) // Set up child processes id
unix_error("setpgid error");
if(execve(argv[0], argv, environ) < 0){
printf("%s: command not found\n", argv[0]);
exit(0);
}
}
// Add the current process to job in , Whether it's a foreground process or a background process
addjob(jobs, pid, state, cmdline);
// Recover the blocked signal SIGINT SIGTSTP SIGCHLD
if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)
unix_error("sigprocmask error");
// Determine the sub process type and handle it
if(state == FG)
waitfg(pid); // Front desk job waiting
else
printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); // Process id Mapping to job id
}
return;
}
Be careful :
- Each child process must have its own unique process group id, By means of fork() In the next sub process Setpgid(0,0) Realization , In this way, when sending to the foreground program ctrl+c or ctrl+z On command , Will not affect the background program . If there is no such step , Then all child processes are related to the current tsh shell The process is the same process group , When sending a signal , The subprocesses of the front and back stations will receive .
- stay fork() Block before the new process SIGCHLD The signal , Prevent competition , This is a classic synchronization error , If there is no blocking, the child process will end first from jobs Delete in , Then execute to the main process addjob The competition problem .
builtin_cmd function
The functionality : Recognize and execute built-in commands : quit, fg, bg, and jobs.
The function prototype :int builtin_cmd(char **argv)
, Parameter is argv parameter list
Realize the idea :
When the command line parameter is quit when , Directly to terminate shell
When the command line parameter is jobs when , call listjobs function , Show job list
When the command line parameter is bg or fg when , call do_bgfg function , Execute built-in bg and fg command
Return if it is not a built-in command 0
Complete code :
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0], "quit")) // If the order is quit, sign out
exit(0);
else if(!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) // If it is bg perhaps fg command , perform do_fgbg function
do_bgfg(argv);
else if(!strcmp(argv[0], "jobs")) // If the order is jobs, List running and stopped background jobs
listjobs(jobs);
else
return 0; /* not a builtin command */
return 1;
}
do_bgfg function
The functionality : Implement built-in commands bg and fg
The first thing to be clear is bg and bg The role of
bg <job>
: Change the stopped background job to the running background job . By sending SIGCONT Signal restart<job>
, Then run it in the background .<job>
Parameters can be PID, It can also be JID.ST -> BG
fg <job>
: Change the background job that has been stopped or is running to the job that is running in the foreground . By sending SIGCONT The signal starts again<job>
, Then run it in the foreground .<job>
Parameters can be PID, It can also be JID.ST -> FG,BG -> FG
The function prototype :void do_bgfg(char **argv)
, Parameter is argv parameter list
Realize the idea :
- Judge argv[] Do you have %, If it is an integer, it will be passed in pid, If % Then in jid. Then call getjobjid Function to get the corresponding job Structure , If the return is empty , Note that... Does not exist in the list jid Of job, To output a prompt .
- Use strcmp Function judgment is bg Order or fg command
- if bg, Restart the target process , Set state to BG( backstage ), Print process information
- if fg, Restart the target process , Set state to FG( The front desk ), Wait for the process to finish
Complete code :
void do_bgfg(char **argv)
{
int num;
struct job_t *job;
// Without parameters fg/bg Should be discarded
if(!argv[1]){
// The command line is empty
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return ;
}
// testing fg/bg Parameters , among % The first number is JobID, Pure numbers are PID
if(argv[1][0] == '%'){
// analysis jid
if((num = strtol(&argv[1][1], NULL, 10)) <= 0){
printf("%s: argument must be a PID or %%jobid\n",argv[0]);// Failure , Print error messages
return;
}
if((job = getjobjid(jobs, num)) == NULL){
printf("%%%d: No such job\n", num); // No corresponding job
return;
}
} else {
if((num = strtol(argv[1], NULL, 10)) <= 0){
printf("%s: argument must be a PID or %%jobid\n",argv[0]);// Failure , Print error messages
return;
}
if((job = getjobpid(jobs, num)) == NULL){
printf("(%d): No such process\n", num); // No corresponding process was found
return;
}
}
if(!strcmp(argv[0], "bg")){
// bg Will start the child process , And put it in the background for execution
job->state = BG; // Set the state of
if(kill(-job->pid, SIGCONT) < 0) // Send a negative signal to the process group
unix_error("kill error");
printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
} else if(!strcmp(argv[0], "fg")) {
job->state = FG; // Set the state of
if(kill(-job->pid, SIGCONT) < 0) // Send a negative signal to the process group
unix_error("kill error");
// When a process is set to execute in the foreground , At present tsh You should wait for the subprocess to end
waitfg(job->pid);
} else {
puts("do_bgfg: Internal error");
exit(0);
}
return;
}
waitfg() function
The functionality : Wait for a foreground job to finish , Or block a foreground process until it becomes a background process
The function prototype :void waitfg(pid_t pid)
, The parameter is process ID
Realize the idea : Determine the current foreground process group pid Whether it is related to the current process pid Whether it is equal or not , If they are equal sleep Until the end of the foreground process .
Complete code :
void waitfg(pid_t pid)
{
struct job_t *job = getjobpid(jobs, pid);
if(!job) return;
// If the status of the current child process has not changed , be tsh Keep sleeping
while(job->state == FG)
// Use sleep This code of will be slow , Best use sigsuspend
sleep(1);
return;
}
sigchld_handler function
The functionality : Handle SIGCHILD The signal
The function prototype :void sigchld_handler(int sig)
, The parameter is the signal type
First, let's learn about the process of the parent process reclaiming the child process : When a child process terminates or stops , The kernel will send a SIGCHLD Signal the parent process . Therefore, the parent process must recycle the child process , To avoid leaving dead processes in the system . The parent process captures this SIGCHLD The signal , Recycle a subprocess . A process can be called by waitpid Function to wait for its subprocesses to terminate or stop . If recycling succeeds , Is returned as PID, If WNOHANG, Then return to 0, If there are other mistakes , Then for -1.
Realize the idea :
- use while Cycle call waitpid Until all its child processes terminate .
- Check the exit status of the recycled child process
- WIFSTOPPED:: The child process that caused the return is currently stopped
- WIFSIGNALED: The subprocess terminates because of an uncapped signal
- WIFEXITED: Subprocesses call exit perhaps return Normal termination
- And then separately WSTOPSIG,WTERMSIG,WEXITSTATUS Extract the above three exit States . Note that if the child process causing the return is currently the stopped process , Then set its status to ST
Complete code :
void sigchld_handler(int sig)
{
int status, jid;
pid_t pid;
struct job_t *job;
if(verbose)
puts("sigchld_handler: entering");
/* Wait for all child processes in a non blocking manner waitpid Parameters 3: 1. 0 : perform waitpid when , Only in child processes ** End ** Only when . 2. WNOHANG : If child process is still running , Then return to 0 . Note that only this flag is set ,waitpid It's possible to return 0 3. WUNTRACED : If the child process stops because of a signal , Then we will return immediately . Only when this flag is set ,waitpid return , Its WIFSTOPPED(status) It's possible to return true */
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0){
// If the current subprocess job Has deleted , It means there is an error
if((job = getjobpid(jobs, pid)) == NULL){
printf("Lost track of (%d)\n", pid);
return;
}
jid = job->jid;
// Next, judge three states
// If the subprocess receives a pause signal ( I haven't quit yet )
if(WIFSTOPPED(status)){
printf("Job [%d] (%d) stopped by signal %d\n", jid, job->pid, WSTOPSIG(status));
job->state = ST; // The status is set to pending
}
// If the child process calls exit Or a return (return) Normal termination
else if(WIFEXITED(status)){
if(deletejob(jobs, pid))
if(verbose){
printf("sigchld_handler: Job [%d] (%d) deleted\n", jid, pid);
printf("sigchld_handler: Job [%d] (%d) terminates OK (status %d)\n", jid, pid, WEXITSTATUS(status));
}
}
// If the child process is terminated because of an uncapped signal , for example SIGKILL
else {
if(deletejob(jobs, pid)){
// Clear process
if(verbose)
printf("sigchld_handler: Job [%d] (%d) deleted\n", jid, pid);
}
printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status)); // Returns the number of signals that cause the child process to terminate
}
}
if(verbose)
puts("sigchld_handler: exiting");
return;
}
sigint_handler function
The functionality : Capture SIGINT The signal
The function prototype :void sigchld_handler(int sig)
, The parameter is the signal type
Realize the idea :
- Call function fgpid Return to the foreground process pid
- If the current process pid Not for 0, So called kill Function send SIGINT Signal to the foreground process group
- stay 2 Call in kill Function if the return value is -1 Indicates that the process does not exist . Output error
Complete code :
void sigint_handler(int sig)
{
if(verbose)
puts("sigint_handler: entering");
pid_t pid = fgpid(jobs);
if(pid){
// send out SIGINT To all processes in the foreground process group
// It should be noted that , The processes in the foreground process group are in addition to the current foreground process , It also includes sub processes of the foreground process .
// At most one foreground process can exist , But there can be multiple processes in the foreground process group
if(kill(-pid, SIGINT) < 0)
unix_error("kill (sigint) error");
if(verbose){
printf("sigint_handler: Job (%d) killed\n", pid);
}
}
if(verbose)
puts("sigint_handler: exiting");
return;
}
sigtstp_handler function
The functionality : Same as sigint_handler almost , Capture SIGTSTP The signal
The function prototype :void sigtstp_handler(int sig)
, The parameter is the signal type
First of all, let's get to know SIGTSTP The role of :SIGTSPT The default behavior of the signal is to stop until the next SIGCONT, Is the stop signal from the terminal , Type... On the keyboard CTR+Z Will lead to a SIGTSPT The signal is sent to the housing . The enclosure captures this signal , And then send SIGTSPT Signal to each process in the foreground process group . By default , The result is to stop or suspend the foreground job .
Realize the idea :
- use fgpid(jobs) Get foreground process pid, Determine whether there is currently a foreground process , If there is no direct return .
- use kill(-pid,sig) Function send SIGTSPT Signal to the foreground process group .
Complete code :
void sigtstp_handler(int sig)
{
if(verbose)
puts("sigstp_handler: entering");
pid_t pid = fgpid(jobs);
struct job_t *job = getjobpid(jobs, pid);
if(pid){
if(kill(-pid, SIGTSTP) < 0)
unix_error("kill (tstp) error");
if(verbose){
printf("sigstp_handler: Job [%d] (%d) stopped\n", job->jid, pid);
}
}
if(verbose)
puts("sigstp_handler: exiting");
return;
}
Be careful : Use kill function , If pid Less than zero will send the signal sig For each process in the process group , So I'm going to use -pid.
thus tsh.c File complete .
Next, we analyze the contents of the following test files and see whether our function can complete the test
First of all, let's understand the symbols and command definitions of some test files :
Symbol :
- Space : Separate instruction function
- &: If the command is in & ending , Indicates that the job is running in the background
- #: Print directly # The text content of the next line
- %: Followed by an integer , Express job Of ID Number .
command :
- jobs: List running and stopped background jobs
bg <job>
: Change the stopped background job to the running background jobfg <job>
: Change the background job that has been stopped or is running to the job that is running in the foreground- kill : Terminate a job
User programs :
- myint Program : Functional sleep , Make the program sleep n second , It will not exit automatically after running , And will detect system errors ;
- myspin Program : Functional sleep , Make the program sleep n second , Automatically quit after sleep , Do not detect system errors ;
- mysplit Program : Functional sleep , Make the program sleep n second , Create a sub process to sleep , Then the parent process waits for the child process to sleep normally n Seconds later , Continue operation ;
- mystop Program : Let the process be tentative n second , And send signals .
The first level - Terminate correctly EOF
Start by opening trace01.txt View file contents
#
# trace01.txt - Properly terminate on EOF.
# CLOSE
WAIT
# It can be understood as //, We don't need to parse . The first level calls linux command close Close the file and wait wait for , stay EOF On normal termination , So we don't need to do anything
Input make test01
and make rtest01
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The second level - Built in process quit command
open trace02.txt View file contents
#
# trace02.txt - Process builtin quit command.
# quit
WAIT
The second level requires us to input commands quit sign out shell process , We need to parse cmdline( The command entered ), Judgment is not “quit” character string , Yes, just quit .
Input make test02
and make rtest02
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The third level - Run a foreground job
open trace03.txt View file contents
#
# trace03.txt - Run a foreground job.
#
/bin/echo tsh> quit
quit
Here's an explanation /bin/echo:
eval The function passes first builtin_cmd Inquire about cmdline Is it built-in commands such as quit, If yes, the current process executes the command
If not, create a child process , Called in a child process execve() Function by argv[0] To find the path , And run the executable file in the path in the subprocess , If not found, the command is invalid , Invalid output command , And use exit(0) End the subprocess
/bin/echo Open up bin In the catalog echo file ,echo It can be understood that the content behind it is output as a string
So the task of the third level is :
- First of all /bin/echo tsh> quit It means to open bin In the catalog echo Executable file , stay foregound Start a child process and run it ( Because there is no end & Symbol , If there is , Is in the backgound function )
- function echo In the process of this process , adopt tsh>quit command , call tsh And execute the built-in commands quit, sign out echo This subprocess
- Last in tsh Execute built-in commands quit, sign out tsh process , Back to our terminal .
Input make test03
and make rtest03
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The fourth level - Running the background job
open trace04.txt View file contents
#
# trace04.txt - Run a background job.
#
/bin/echo -e tsh> ./myspin 1 \046
./myspin 1 &
Execute at the front desk first echo command , Wait for the program to finish executing and recycle the sub process .& Represents a background program ,myspin sleep 1 second , Then stop . Because backstage , So show the following sentence , If you are at the front desk, there is no .
Input make test04
and make rtest04
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The fifth level - Handle jobs Built in commands
open trace05.txt View file contents
#
# trace05.txt - Process jobs builtin command.
#
/bin/echo -e tsh> ./myspin 2 \046
./myspin 2 &
/bin/echo -e tsh> ./myspin 3 \046
./myspin 3 &
/bin/echo tsh> jobs
jobs
Respectively run the foreground echo、 backstage myspin、 The front desk echo、 backstage myspin, Then you need to implement a built-in command job, The function is to display all properties of all tasks in the current task list
Input make test05
and make rtest05
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The sixth level - take SIGINT Forward to the foreground job
open trace06.txt View file contents
#
# trace06.txt - Forward SIGINT to foreground job.
#
/bin/echo -e tsh> ./myspin 4
./myspin 4
SLEEP 2
INT
Received interrupt signal SIGINT( namely CTRL_C) Then end the foreground process
Input make test06
and make rtest06
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The seventh level - Only will SIGINT Forward to the foreground job
open trace07.txt View file contents
#
# trace07.txt - Forward SIGINT only to foreground job.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
INT
/bin/echo tsh> jobs
jobs
According to the notes , We can know that the seventh level tests only SIGINT Forward to the foreground job . The command line here is actually well understood according to the previous , Is to give two assignments , One works at the front desk , The other one works in the background , Next pass SIGINT Instructions , Then call the built-in instructions jobs To view the work information at this time , To compare whether it is only SIGINT Forward to the foreground job .
Input make test07
and make rtest07
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The eighth level - Only will SIGTSTP Forward to the foreground job
open trace08.txt View file contents
#
# trace08.txt - Forward SIGTSTP only to foreground job.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
According to the notes, we need to SIGTSTP Forward to the foreground job . According to the function of this signal , That is, the process will stop until the next SIGCONT That is, to suspend , Let other programs continue . Here is the background program , And then use jobs To print out the process information .
Input make test08
and make rtest08
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The Ninth level - process bg Built in commands
open trace09.txt View file contents
#
# trace09.txt - Process bg builtin command
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> bg %2
bg %2
/bin/echo tsh> jobs
jobs
Here is a more complete test on the test file of the eighth level , Here is after the stop , After outputting process information , Use bg Command to wake up the process 2, That is, the program just suspended , Next, continue to use Jobs Command to output results .
Input make test09
and make rtest09
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The tenth level - process fg Built in commands
open trace10.txt View file contents
#
# trace10.txt - Process fg builtin command.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
SLEEP 1
/bin/echo tsh> fg %1
fg %1
SLEEP 1
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> jobs
jobs
Here is to change the background process to the foreground running program . Test the process in the text 1 according to & You can know , process 1 It's a background process . First use fg Command to convert it into a program in the foreground , Next, stop the process 1, Then print out the process information , This is the process 1 The foreground program should be suspended at the same time , Next use fg Command to continue running , Use jobs To print out the process information
Input make test10
and make rtest10
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The eleventh level - take SIGINT Forward to each process in the foreground process group
open trace11.txt View file contents
#
# trace11.txt - Forward SIGINT to every process in foreground process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
INT
/bin/echo tsh> /bin/ps a
/bin/ps a
According to the notes, we can know that we need to SIGINT Send it to each process in the foreground process group .ps –a Show all processes , There are two processes here ,mysplit Created a subprocess , Next, send instructions SIGINT, So all processes in the process group should be stopped , Next call pl To see whether each process in the process group has stopped .
Input make test11
and make rtest11
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
Pass 12 - take SIGTSTP Forward to each process in the foreground process group
open trace12.txt View file contents
#
# trace12.txt - Forward SIGTSTP to every process in foreground process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> /bin/ps a
/bin/ps a
According to the notes, the test program is to test SIGTSTP Forward to each process in the foreground process group . Same as the previous level , Only need the corresponding process to be suspended .
Input make test12
and make rtest12
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
Level 13 - Restart each stopped process in the process group
open trace13.txt View file contents
#
# trace13.txt - Restart every stopped process in process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> /bin/ps a
/bin/ps a
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> /bin/ps a
/bin/ps a
According to the comments, we can know that the program is to test every stopped process in the restart process group . Here is to use fg To wake up the whole work , In the middle ps -a To see the difference between stopping the whole work and waking up the whole work .
Input make test13
and make rtest13
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
The fourteenth pass - Simple error handling
open trace14.txt View file contents
#
# trace14.txt - Simple error handling
#
/bin/echo tsh> ./bogus
./bogus
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo tsh> fg
fg
/bin/echo tsh> bg
bg
/bin/echo tsh> fg a
fg a
/bin/echo tsh> bg a
bg a
/bin/echo tsh> fg 9999999
fg 9999999
/bin/echo tsh> bg 9999999
bg 9999999
/bin/echo tsh> fg %2
fg %2
/bin/echo tsh> fg %1
fg %1
SLEEP 2
TSTP
/bin/echo tsh> bg %2
bg %2
/bin/echo tsh> bg %1
bg %1
/bin/echo tsh> jobs
jobs
According to the comments, you can know that this file is for testing simple error handling . Here's the test file , That's testing fg and bg Later parameters , We know fg and bg I need a JID Or is it PID, among JID Plus % The integer number of . Other parameters should report errors , Or if there is no parameter, it should also report an error . Next, test the function , All of them have been tested at the above level
Input make test14
and make rtest14
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
15 - Put them together
open trace15.txt View file contents
#
# trace15.txt - Putting it all together
#
/bin/echo tsh> ./bogus
./bogus
/bin/echo tsh> ./myspin 10
./myspin 10
SLEEP 2
INT
/bin/echo -e tsh> ./myspin 3 \046
./myspin 3 &
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> bg %3
bg %3
/bin/echo tsh> bg %1
bg %1
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> quit
quit
According to the notes, the test of this test file is to put them together . I was still wondering what it means to put them together ? Read the test file carefully , You can know that he tested all the above commands , Such as jobs,fg,bg,quit.
Input make test15
and make rtest15
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
16 - test shell Whether it is able to handle from other processes rather than terminals SIGTSTP and SIGINT The signal
open trace16.txt View file contents
#
# trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT
# signals that come from other processes instead of the terminal.
#
/bin/echo tsh> ./mystop 2
./mystop 2
SLEEP 3
/bin/echo tsh> jobs
jobs
/bin/echo tsh> ./myint 2
./myint 2
The specific meaning of this test file is , User program to job 2 Send a stop signal , So the process will be output finally 2 Suspended information . meanwhile ,mystop You need to stop yourself to send signals to other processes , So there will also be processes in the middle 1 Suspended information
Input make test16
and make rtest16
View the run results
tsh Experimental phenomena and tshref Agreement , Results the correct
All the tests of the sixteen levels are correct .
complete tsh.c The code is as follows :
/* * tsh - A tiny shell program with job control * * <Put your name and login ID here> */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
/* fdddd */
#define MAXLINE 1024 /* max line size */
#define MAXARGS 128 /* max args on a command line */
#define MAXJOBS 16 /* max jobs at any point in time */
#define MAXJID 1<<16 /* max job ID */
/* Job states */
#define UNDEF 0 /* undefined */
#define FG 1 /* running in foreground */
#define BG 2 /* running in background */
#define ST 3 /* stopped */
/* * Jobs states: FG (foreground), BG (background), ST (stopped) * Job state transitions and enabling actions: * FG -> ST : ctrl-z * ST -> FG : fg command * ST -> BG : bg command * BG -> FG : fg command * At most 1 job can be in the FG state. */
/* Global variables */
extern char **environ; /* defined in libc */
char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
int verbose = 0; /* if true, print additional output */
int nextjid = 1; /* next job ID to allocate */
char sbuf[MAXLINE]; /* for composing sprintf messages */
struct job_t {
/* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};
struct job_t jobs[MAXJOBS]; /* The job list */
/* End global variables */
/* Function prototypes */
/* Here are the functions that you will implement */
void eval(char *cmdline);
int builtin_cmd(char **argv);
void do_bgfg(char **argv);
void waitfg(pid_t pid);
void sigchld_handler(int sig);
void sigtstp_handler(int sig);
void sigint_handler(int sig);
/* Here are helper routines that we've provided for you */
int parseline(const char *cmdline, char **argv);
void sigquit_handler(int sig);
void clearjob(struct job_t *job);
void initjobs(struct job_t *jobs);
int maxjid(struct job_t *jobs);
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
int deletejob(struct job_t *jobs, pid_t pid);
pid_t fgpid(struct job_t *jobs);
struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
struct job_t *getjobjid(struct job_t *jobs, int jid);
int pid2jid(pid_t pid);
void listjobs(struct job_t *jobs);
void usage(void);
void unix_error(char *msg);
void app_error(char *msg);
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler);
/* * main - The shell's main routine */
int main(int argc, char **argv)
{
char c;
char cmdline[MAXLINE];
int emit_prompt = 1; /* emit prompt (default) */
/* Redirect stderr to stdout (so that driver will get all output * on the pipe connected to stdout) */
dup2(1, 2);
/* Parse the command line */
while ((c = getopt(argc, argv, "hvp")) != EOF) {
switch (c) {
case 'h': /* print help message */
usage();
break;
case 'v': /* emit additional diagnostic info */
verbose = 1;
break;
case 'p': /* don't print a prompt */
emit_prompt = 0; /* handy for automatic testing */
break;
default:
usage();
}
}
/* Install the signal handlers */
/* These are the ones you will need to implement */
Signal(SIGINT, sigint_handler); /* ctrl-c */
Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
/* This one provides a clean way to kill the shell */
Signal(SIGQUIT, sigquit_handler);
/* Initialize the job list */
initjobs(jobs);
/* Execute the shell's read/eval loop */
while (1) {
/* Read command line */
if (emit_prompt) {
printf("%s", prompt);
fflush(stdout);
}
if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
app_error("fgets error");
if (feof(stdin)) {
/* End of file (ctrl-d) */
fflush(stdout);
exit(0);
}
/* Evaluate the command line */
eval(cmdline);
fflush(stdout);
fflush(stdout);
}
exit(0); /* control never reaches here */
}
/* * eval - Evaluate the command line that the user has just typed in * * If the user has requested a built-in command (quit, jobs, bg or fg) * then execute it immediately. Otherwise, fork a child process and * run the job in the context of the child. If the job is running in * the foreground, wait for it to terminate and then return. Note: * each child process must have a unique process group ID so that our * background children don't receive SIGINT (SIGTSTP) from the kernel * when we type ctrl-c (ctrl-z) at the keyboard. */
void eval(char *cmdline)
{
char* argv[MAXARGS]; //execve() The parameters of the function
int state = UNDEF; // Working state ,FG or BG
sigset_t set;
pid_t pid; // process id
// Process the input data
if(parseline(cmdline, argv) == 1) // Parse command line , Return to argv Array
state = BG;
else
state = FG;
if(argv[0] == NULL) // The command line returns null directly
return;
// If it's not a built-in command
if(!builtin_cmd(argv))
{
if(sigemptyset(&set) < 0)
unix_error("sigemptyset error");
if(sigaddset(&set, SIGINT) < 0 || sigaddset(&set, SIGTSTP) < 0 || sigaddset(&set, SIGCHLD) < 0)
unix_error("sigaddset error");
// Block before it spawns a child process SIGCHLD The signal , Prevent competition
if(sigprocmask(SIG_BLOCK, &set, NULL) < 0)
unix_error("sigprocmask error");
if((pid = fork()) < 0) //fork Failed to create child process
unix_error("fork error");
else if(pid == 0) //fork Create child process
{
// The control flow of the child process begins
if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0) // unblocked
unix_error("sigprocmask error");
if(setpgid(0, 0) < 0) // Set up child processes id
unix_error("setpgid error");
if(execve(argv[0], argv, environ) < 0){
printf("%s: command not found\n", argv[0]);
exit(0);
}
}
// Add the current process to job in , Whether it's a foreground process or a background process
addjob(jobs, pid, state, cmdline);
// Recover the blocked signal SIGINT SIGTSTP SIGCHLD
if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)
unix_error("sigprocmask error");
// Determine the sub process type and handle it
if(state == FG)
waitfg(pid); // Front desk job waiting
else
printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); // Process id Mapping to job id
}
return;
}
/* * parseline - Parse the command line and build the argv array. * * Characters enclosed in single quotes are treated as a single * argument. Return true if the user has requested a BG job, false if * the user has requested a FG job. */
int parseline(const char *cmdline, char **argv)
{
static char array[MAXLINE]; /* holds local copy of command line */
char *buf = array; /* ptr that traverses command line */
char *delim; /* points to first space delimiter */
int argc; /* number of args */
int bg; /* background job? */
strcpy(buf, cmdline);
buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* ignore leading spaces */
buf++;
/* Build the argv list */
argc = 0;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}
while (delim) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* ignore spaces */
buf++;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}
}
argv[argc] = NULL;
if (argc == 0) /* ignore blank line */
return 1;
/* should the job run in the background? */
if ((bg = (*argv[argc-1] == '&')) != 0) {
argv[--argc] = NULL;
}
return bg;
}
/* * builtin_cmd - If the user has typed a built-in command then execute * it immediately. */
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0], "quit")) // If the order is quit, sign out
exit(0);
else if(!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) // If it is bg perhaps fg command , perform do_fgbg function
do_bgfg(argv);
else if(!strcmp(argv[0], "jobs")) // If the order is jobs, List running and stopped background jobs
listjobs(jobs);
else
return 0; /* not a builtin command */
return 1;
}
/* * do_bgfg - Execute the builtin bg and fg commands */
void do_bgfg(char **argv)
{
int num;
struct job_t *job;
// Without parameters fg/bg Should be discarded
if(!argv[1]){
// The command line is empty
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return ;
}
// testing fg/bg Parameters , among % The first number is JobID, Pure numbers are PID
if(argv[1][0] == '%'){
// analysis jid
if((num = strtol(&argv[1][1], NULL, 10)) <= 0){
printf("%s: argument must be a PID or %%jobid\n",argv[0]);// Failure , Print error messages
return;
}
if((job = getjobjid(jobs, num)) == NULL){
printf("%%%d: No such job\n", num); // No corresponding job
return;
}
} else {
if((num = strtol(argv[1], NULL, 10)) <= 0){
printf("%s: argument must be a PID or %%jobid\n",argv[0]);// Failure , Print error messages
return;
}
if((job = getjobpid(jobs, num)) == NULL){
printf("(%d): No such process\n", num); // No corresponding process was found
return;
}
}
if(!strcmp(argv[0], "bg")){
// bg Will start the child process , And put it in the background for execution
job->state = BG; // Set the state of
if(kill(-job->pid, SIGCONT) < 0) // Send a negative signal to the process group
unix_error("kill error");
printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
} else if(!strcmp(argv[0], "fg")) {
job->state = FG; // Set the state of
if(kill(-job->pid, SIGCONT) < 0) // Send a negative signal to the process group
unix_error("kill error");
// When a process is set to execute in the foreground , At present tsh You should wait for the subprocess to end
waitfg(job->pid);
} else {
puts("do_bgfg: Internal error");
exit(0);
}
return;
}
/* * waitfg - Block until process pid is no longer the foreground process */
void waitfg(pid_t pid)
{
struct job_t *job = getjobpid(jobs, pid);
if(!job) return;
// If the status of the current child process has not changed , be tsh Keep sleeping
while(job->state == FG)
// Use sleep This code of will be slow , Best use sigsuspend
sleep(1);
return;
}
/***************** * Signal handlers *****************/
/* * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever * a child job terminates (becomes a zombie), or stops because it * received a SIGSTOP or SIGTSTP signal. The handler reaps all * available zombie children, but doesn't wait for any other * currently running children to terminate. */
void sigchld_handler(int sig)
{
int status, jid;
pid_t pid;
struct job_t *job;
if(verbose)
puts("sigchld_handler: entering");
/* Wait for all child processes in a non blocking manner waitpid Parameters 3: 1. 0 : perform waitpid when , Only in child processes ** End ** Only when . 2. WNOHANG : If child process is still running , Then return to 0 . Note that only this flag is set ,waitpid It's possible to return 0 3. WUNTRACED : If the child process stops because of a signal , Then we will return immediately . Only when this flag is set ,waitpid return , Its WIFSTOPPED(status) It's possible to return true */
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0){
// If the current subprocess job Has deleted , It means there is an error
if((job = getjobpid(jobs, pid)) == NULL){
printf("Lost track of (%d)\n", pid);
return;
}
jid = job->jid;
// Next, judge three states
// If the subprocess receives a pause signal ( I haven't quit yet )
if(WIFSTOPPED(status)){
printf("Job [%d] (%d) stopped by signal %d\n", jid, job->pid, WSTOPSIG(status));
job->state = ST; // The status is set to pending
}
// If the child process calls exit Or a return (return) Normal termination
else if(WIFEXITED(status)){
if(deletejob(jobs, pid))
if(verbose){
printf("sigchld_handler: Job [%d] (%d) deleted\n", jid, pid);
printf("sigchld_handler: Job [%d] (%d) terminates OK (status %d)\n", jid, pid, WEXITSTATUS(status));
}
}
// If the child process is terminated because of an uncapped signal , for example SIGKILL
else {
if(deletejob(jobs, pid)){
// Clear process
if(verbose)
printf("sigchld_handler: Job [%d] (%d) deleted\n", jid, pid);
}
printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status)); // Returns the number of signals that cause the child process to terminate
}
}
if(verbose)
puts("sigchld_handler: exiting");
return;
}
/* * sigint_handler - The kernel sends a SIGINT to the shell whenver the * user types ctrl-c at the keyboard. Catch it and send it along * to the foreground job. */
void sigint_handler(int sig)
{
if(verbose)
puts("sigint_handler: entering");
pid_t pid = fgpid(jobs);
if(pid){
// send out SIGINT To all processes in the foreground process group
// It should be noted that , The processes in the foreground process group are in addition to the current foreground process , It also includes sub processes of the foreground process .
// At most one foreground process can exist , But there can be multiple processes in the foreground process group
if(kill(-pid, SIGINT) < 0)
unix_error("kill (sigint) error");
if(verbose){
printf("sigint_handler: Job (%d) killed\n", pid);
}
}
if(verbose)
puts("sigint_handler: exiting");
return;
}
/* * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever * the user types ctrl-z at the keyboard. Catch it and suspend the * foreground job by sending it a SIGTSTP. */
void sigtstp_handler(int sig)
{
if(verbose)
puts("sigstp_handler: entering");
pid_t pid = fgpid(jobs);
struct job_t *job = getjobpid(jobs, pid);
if(pid){
if(kill(-pid, SIGTSTP) < 0)
unix_error("kill (tstp) error");
if(verbose){
printf("sigstp_handler: Job [%d] (%d) stopped\n", job->jid, pid);
}
}
if(verbose)
puts("sigstp_handler: exiting");
return;
}
/********************* * End signal handlers *********************/
/*********************************************** * Helper routines that manipulate the job list **********************************************/
/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
job->pid = 0;
job->jid = 0;
job->state = UNDEF;
job->cmdline[0] = '\0';
}
/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
clearjob(&jobs[i]);
}
/* maxjid - Returns largest allocated job ID */
int maxjid(struct job_t *jobs)
{
int i, max=0;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid > max)
max = jobs[i].jid;
return max;
}
/* addjob - Add a job to the job list */
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
{
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == 0) {
jobs[i].pid = pid;
jobs[i].state = state;
jobs[i].jid = nextjid++;
if (nextjid > MAXJOBS)
nextjid = 1;
strcpy(jobs[i].cmdline, cmdline);
if(verbose){
printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
}
return 1;
}
}
printf("Tried to create too many jobs\n");
return 0;
}
/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob(struct job_t *jobs, pid_t pid)
{
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == pid) {
clearjob(&jobs[i]);
nextjid = maxjid(jobs)+1;
return 1;
}
}
return 0;
}
/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].state == FG)
return jobs[i].pid;
return 0;
}
/* getjobpid - Find a job (by PID) on the job list */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
int i;
if (pid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid)
return &jobs[i];
return NULL;
}
/* getjobjid - Find a job (by JID) on the job list */
struct job_t *getjobjid(struct job_t *jobs, int jid)
{
int i;
if (jid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid == jid)
return &jobs[i];
return NULL;
}
/* pid2jid - Map process ID to job ID */
int pid2jid(pid_t pid)
{
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid) {
return jobs[i].jid;
}
return 0;
}
/* listjobs - Print the job list */
void listjobs(struct job_t *jobs)
{
int i;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid != 0) {
printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
switch (jobs[i].state) {
case BG:
printf("Running ");
break;
case FG:
printf("Foreground ");
break;
case ST:
printf("Stopped ");
break;
default:
printf("listjobs: Internal error: job[%d].state=%d ",
i, jobs[i].state);
}
printf("%s", jobs[i].cmdline);
}
}
}
/****************************** * end job list helper routines ******************************/
/*********************** * Other helper routines ***********************/
/* * usage - print a help message */
void usage(void)
{
printf("Usage: shell [-hvp]\n");
printf(" -h print this message\n");
printf(" -v print additional diagnostic information\n");
printf(" -p do not emit a command prompt\n");
exit(1);
}
/* * unix_error - unix-style error routine */
void unix_error(char *msg)
{
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
exit(1);
}
/* * app_error - application-style error routine */
void app_error(char *msg)
{
fprintf(stdout, "%s\n", msg);
exit(1);
}
/* * Signal - wrapper for the sigaction function */
handler_t *Signal(int signum, handler_t *handler)
{
struct sigaction action, old_action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
action.sa_flags = SA_RESTART; /* restart syscalls if possible */
if (sigaction(signum, &action, &old_action) < 0)
unix_error("Signal error");
return (old_action.sa_handler);
}
/* * sigquit_handler - The driver program can gracefully terminate the * child shell by sending it a SIGQUIT signal. */
void sigquit_handler(int sig)
{
printf("Terminating after receipt of SIGQUIT signal\n");
exit(1);
}
边栏推荐
- MySQL数据库(二)DML数据操作语句和基本的DQL语句
- Database monitoring SQL execution
- How to solve the poor sound quality of Vos?
- Install and run tensorflow object detection API video object recognition system of Google open source
- Report on the double computer experiment of scoring system based on 485 bus
- Should wildcard import be avoided- Should wildcard import be avoided?
- Statistics 8th Edition Jia Junping Chapter 2 after class exercises and answer summary
- Oracle foundation and system table
- Description of Vos storage space, bandwidth occupation and PPS requirements
- [oiclass] share prizes
猜你喜欢
What to do when programmers don't modify bugs? I teach you
Cadence physical library lef file syntax learning [continuous update]
自动化测试中敏捷测试怎么做?
Interview answering skills for software testing
Logstack introduction and deployment -- elasticstack (elk) work notes 019
CSAPP家庭作业答案7 8 9章
Report on the double computer experiment of scoring system based on 485 bus
Eigen User Guide (Introduction)
Nest and merge new videos, and preset new video titles
What level do 18K test engineers want? Take a look at the interview experience of a 26 year old test engineer
随机推荐
Currently, mysql5.6 is used. Which version would you like to upgrade to?
DVWA exercise 05 file upload file upload
Global and Chinese markets of MPV ACC ECU 2022-2028: Research Report on technology, participants, trends, market size and share
ucore lab7 同步互斥 实验报告
Express
ucore lab6 调度器 实验报告
Stc-b learning board buzzer plays music 2.0
Public key box
Global and Chinese market of RF shielding room 2022-2028: Research Report on technology, participants, trends, market size and share
Zhejiang University Edition "C language programming experiment and exercise guide (3rd Edition)" topic set
[HCIA continuous update] working principle of static route and default route
Global and Chinese market of goat milk powder 2022-2028: Research Report on technology, participants, trends, market size and share
[200 opencv routines] 98 Statistical sorting filter
UCORE lab8 file system experiment report
软件测试工作太忙没时间学习怎么办?
Fundamentals of digital circuits (I) number system and code system
Future trend and planning of software testing industry
Statistics 8th Edition Jia Junping Chapter 4 Summary and after class exercise answers
Opencv recognition of face in image
Pedestrian re identification (Reid) - data set description market-1501