当前位置:网站首页>csapp shell lab

csapp shell lab

2022-07-06 15:25:00 Hu Da jinshengyu

The complete code will be placed at the end of the article .

The experiment purpose :

modify tsh.c The file completes the following seven functions :
 eval: Parse the command line statement and run the process
 builtin_cmd: Determine whether it is a built-in instruction
 do_bgfg: perform bg < job > and fg < job > Built in commands
 waitfg: wait for pid The process is no longer a foreground process
 sigchld_handler: Recycle all available dead child processes
 sigtstp_handler: Respond to SIGTSTP(ctrl-z) The signal
 sigint_handler: Respond to SIGINT(ctrl-c) The signal

Experimental content :

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 .
We know that seven functions are in tsh.c To realize , We see tsh.c The concrete realization inside , We found that some auxiliary functions are defined as follows :

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 :
1. Learn to compile tsh.c, call tsh file traceXX.txt The function verification method uses make Command compilation tsh.c file ( If the file changes, you need to use it first make clean Command clear );
 Insert picture description here
2. 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”;
3. 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 .

Realization

eval() function :

The functionality : If the user has requested a built-in command ( sign out 、 Homework 、bg or fg) Then execute it immediately . otherwise , Derive a child process and run the job in the context of the child object . If the job is running in the foreground , Wait for it to end , Then return . notes : Each child process must have a unique process group ID, So that the backend child does not receive from the kernel SIGINT(SIGTSTP) When we type ctrl-c(ctrl-z) when .

The prototype of function is in the book void eval(char *cmdline);

This function keeps looping in the loop body Parse command line We passed what we have given parseline Function to parse into a character pointer If found to be built-in Then directly execute the built-in command

Realize the idea :

  1. First initialize variables , Next call parseline Function to parse into a character pointer , because builtin_cmd Function implements the execution of built-in instructions , So we just need to judge whether the input instruction is a built-in instruction , return 0 Description is not a built-in command .
  2. If not built-in instructions , Then use fork Function create subprocess , At the same time, in order to avoid the synchronization problem caused by the competition between parent and child processes , We need to mask out before creating sub processes SIGCHLD The signal .
  3. In the subprocess , First, unblock , And then use setpgid(0,0) Set up process groups , Make the process group number of the child process different from that of the parent process , And then call execve Function to execute job.
  4. 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 .

Be careful :

  1. The key point of this function is to be careful about the competition in synchronous concurrent streams , Besides ,setpgid It's also necessary , It combines sub process groups with tsh Separate process groups , avoid tsh Stop after receiving an inexplicable signal .

  2. The child process inherits the blocked set of the parent process , Calling execve Before , First, be careful to remove the blocking in the subprocess SIGCHID The signal .

  3. stay fork() The new process should be blocked before and after SIGCHLD The signal , Prevent competitive synchronization errors . otherwise , There may be sub processes running first and ending from jobs Delete in , and deletejob Don't do anything? , Because the parent process has not added the child process to the list ; Then execute the parent process , call addjob Add non-existent subprocess to the job list by mistake , It will never be deleted .

  4. Each child process must have its own process group id, In order to send signals to the sub process group . otherwise , All subprocesses are related to tsh shell The process is the same process group , When sending a signal , The subprocesses of the front and back stations will receive .—— Realization : stay fork() In the next sub process setpgid(0,0)

    setpgid(pid_t pid, pid_t pgid): Process pid The process group of is changed to pgid. If pid yes 0, Then it is the current process pid, If pgid yes 0, Then use pid.

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))//builtin The command is used to execute the specified shell Internal orders , And return the return value of the internal command .
    {
    
        if(sigemptyset(&set) < 0)// This function initializes the signal set to null .
            unix_error("sigemptyset error");
        if(sigaddset(&set, SIGINT) < 0 || sigaddset(&set, SIGTSTP) < 0 || sigaddset(&set, SIGCHLD) < 0)// The purpose of this function is to put the signal signo Add to signal set set in , Return on success 0, Return... On failure -1.
            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, Put it in the same process group 
                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;
}

builtin_cmd() function

** The functionality :**builtin The command is used to execute the specified shell Internal orders , And return the return value of the internal command . This is to recognize and execute the built-in commands : quit, fg, bg, and jobs.

** Parameter setting :** Parameter is argv parameter list

Realize the idea :

  1. jobs List tasks in all States ( Except that it's over The suspended ones should also be listed )
  2. bg <jid/pid> Make a background task ( Paused or running ) Restart in the background
  3. fg <jid/pid> Make a task Rerun And run in the foreground
  4. quit Exit the program directly
  5. Return if it is not a built-in command 0
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 :** Realization bg and fg Two built-in commands

bg : Change the stopped background job to the running background job . By sending SIGCONT Signal restart , Then run it in the background . Parameters can be PID, It can also be JID.ST -> BG

fg : 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 , Then run it in the foreground . Parameters can be PID, It can also be JID.ST -> FG,BG -> FG

Realize the idea :

  1. Through the first argv[0] Judge the front and back

  2. id=argv[1] probability :

    1.null
    2.%jid( Judge whether it exists )
    3.pid( Judge whether it exists )
    4. neither %jid Neither pid

  3. If the process exists : A signal sent to the process to continue execution

  4. And then use strcmp Function judgment is bg Order or fg Instructions :
    If it's a foreground process , Set state to FG, Wait for it to finish executing
    If it's a background process , Set state to BG, Output prompt message .

Be careful :

  1. distinguish bg and fg command , And the introduction pid perhaps jid The state of the process corresponding to the parameter .

  2. Pay attention to user input error handling , For example, the number of parameters is not enough or the parameters are imported incorrectly

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

** Parameter setting :** There is only one parameter and the parameter is set to 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 equal, sleep until the foreground process ends .

Be careful :

Because the signal may be executing pause Come before ( just ), This will form a competitive relationship , If the program is executed at this time pause Words , Will wait for a signal that will never come ( May never wake up ). So use sigsuspend function . meanwhile , You can also use sleep, But it's also better to use sigsuspend function .

void waitfg(pid_t pid)
{
    
    sigset_t mask, prev;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, &prev);
    while (fgpid(jobs) != 0){
    
        sigsuspend(&mask);
        //printf ("wait here\n");
    }
    sigprocmask(SIG_SETMASK, &prev, NULL);
    return;
}

sigchld_handler() function

** The functionality :** Handle SIGCHILD The signal

** Parameter setting :** The parameter is the signal type

Realize the idea :

  1. use while Cycle call waitpid Until all its child processes terminate .

  2. 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

  3. 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.

Be careful :

This function deals with three cases , One is to deal with normal suspension , The second is when the signal is received and stopped , Third, it is temporarily stopped by the signal , In the first two cases, delete the process from the process table , The third case is not used , But you have to change the state . In order to prevent del The program was interrupted , We still need to plug .

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 when the child process terminates will it return . 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 :** When the user presses ctrl-c when , Send to the front program SIGINT The signal

** Parameter setting :** The parameter is the signal type

Realize the idea :

  1. Use fgpid To get the foreground process pid
  2. When the current process exists , Use kill send out SIGINT To the foreground process group
  3. If kill Function call failed , The output error

Be careful :

The processes in the foreground process group are in addition to the current foreground process , It also includes sub processes of the foreground process .

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 :** Capture SIGTSTP The signal

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 .

** Parameter setting :** The parameter is the signal type

Realize the idea :

  1. use fgpid(jobs) Get foreground process pid, Determine whether there is currently a foreground process , If there is no direct return .
  2. use kill(-pid,sig) Function send SIGTSPT Signal to the foreground process group .
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;
}

thus , All seven functions are completed , Next, start to test whether the function function is correct .

test :

First , We need to know what the output is :

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 .

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 :

The documents of the first level are as follows :

#
# trace01.txt - Properly terminate on EOF.
# CLOSE
WAIT

We can see that the first level calls linux command close Close the file and wait wait for , See if it stops correctly in EOF.

The verification results :

 Insert picture description here

You can see that the test file is consistent with the answer output .

The second level :

The documents of the second level are as follows :

#
# trace02.txt - Process builtin quit command.
# quit
WAIT

Here is the internal command when inputting quit When , We need to quit shell process , Here we need to judge whether the order is quit, If so, we need to exit this shell process . That is, built-in commands , It's a test builtin_cmd() Partial implementation of function .

The verification results :

 Insert picture description here

The result is the same , So explain builtin_cmd() The partial implementation of the function is correct .

The third level :

The documents of the third level are as follows :

#
# trace03.txt - Run a foreground job.
#
/bin/echo tsh> quit
quit

So let's talk about that /bin/echo
eval The function passes first builtin_cmd Inquire about cmdline Is it built-in commands such as quit, If so, implement
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

Turn on echo or turn off request echo , Or display a message . If there are no parameters ,echo The command will display the current echo settings .

So what this means is eval Find out /bin/echo It's not a built-in command , Query path found echo Program and execute in the foreground , Output the following sentence .

What the third level does is :

  1. open bin In the catalog echo The executable of , Start a sub process in the foreground to run echo This file , then eval() Function USES builtin_cmd() Determine whether this is a built-in command , If it is a built-in instruction, execute it directly .
  2. perform quit Instructions , End of subprocess , Next, the second one quit The execution is directly returned to the interrupt .
The verification results :

 Insert picture description here

It is found that the output results are the same .

The fourth level :

The documents of the fourth level are as follows :

#
# trace04.txt - Run a background job.
#
/bin/echo -e tsh> ./myspin 1 \046
./myspin 1 &

According to the comments, we can know that this file is used to test whether a background program can be run .

See specific instructions , Through the third level, we can know , Here is also running bin The executable file under the directory echo, At this time, it is executed at the front desk echo command , Then wait for the execution of the program to complete 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 .( This is a program written by myself , The background program will print out a sentence )

The verification results :

 Insert picture description here

The output is the same , It shows that the background running program is correct .

The fifth level :

The documents of the fifth level are as follows :

#
# 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

The running sequence here is as follows : The front desk echo, backstage myspin, The front desk echo, backstage myspin, The front desk echo, Then use the built-in instructions jobs To print all the information in the process list .

The verification results :

 Insert picture description here

The output is the same , explain eval() and builtin_cmd() The partial implementation of the function is correct .

The sixth level :

The code of the sixth level is as follows :

#
# trace06.txt - Forward SIGINT to foreground job.
#
/bin/echo -e tsh> ./myspin 4
./myspin 4 

SLEEP 2
INT

According to the comments, we can know the specific meaning of these commands is to SIGINT Forward to the foreground job .

therefore , We should know if we receive an interrupt signal SIGINT( namely CTRL_C) Then end the foreground process .

The verification results :

 Insert picture description here

According to the output , We can know that the process is signal 2 Aborted , According to the macro definition, this signal is an interrupt signal SIGINT. So the output result is correct .

The seventh level :

The documents of the seventh level are as follows :

#
# 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 .

The verification results :

 Insert picture description here

By output , We know that in the end, only the backstage work still exists , It shows that our implementation is correct . At the same time, the output results are the same , It also shows that .

The eighth level :

The documents of the eighth level are as follows :

#
# 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 .

The verification results :

 Insert picture description here

We can find that the final output is the same as what we analyzed , It is also the same as the answer output .

The Ninth level :

The ninth document is as follows :

#
# 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

According to the notes , This test program is to test processing bg Built in commands .

bg : Change the stopped background job to the running background job . By sending SIGCONT Signal restart , Then run it in the background . Parameters can be PID, It can also be JID.ST -> BG

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 .

The verification results :

 Insert picture description here

Last , It is found that the output results are consistent with the analysis , At the same time, it is also consistent with the output of the answer .

The tenth level :

The tenth document is as follows :

#
# 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

According to the notes, we can know that this level is mainly for testing commands fg Whether it is right .

fg : Change the background job that has been stopped or is running to the job that is running in the foreground . By sending SIGCONT Signal restart , Then run it in the foreground . Parameters can be PID, It can also be JID.ST -> FG,BG -> FG

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 to verify whether our analysis is correct .

The verification results :

 Insert picture description here

Consistent with the analysis , The explanation is correct .

The eleventh level :

The eleventh document is as follows :

#
# 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 .

The verification results :

 Insert picture description here

The output result is the same as the analysis .

Pass 12 :

The documents of the twelfth level are as follows :

#
# 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 .

The verification results :

 Insert picture description here
 Insert picture description here

Consistent with the results of the analysis .

Level 13 :

The documents of the thirteenth level are as follows :

#
# 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 .

The verification results :

 Insert picture description here

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-RTNSgdxf-1653482215796)(C:\Users\zhaolv\AppData\Roaming\Typora\typora-user-images\image-20220525201048749.png)]

Consistent with the analysis .

The fourteenth pass :

The documents of the 14th level are as follows :

#
# 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 .

notes : Here I think there is one missing test , That is, when three parameters are input . You should also report an error with the wrong number of parameters .

The verification results :

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-jbjA5chf-1653482215797)(C:\Users\zhaolv\AppData\Roaming\Typora\typora-user-images\image-20220525201808891.png)]

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-PKCuinDW-1653482215797)(C:\Users\zhaolv\AppData\Roaming\Typora\typora-user-images\image-20220525201828296.png)]

Consistent with the results of the analysis .

15 :

The documents of the fifteenth level are as follows :

#
# 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. I'm not going to repeat it here .

The verification results :

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-cYCRTQcO-1653482215798)(C:\Users\zhaolv\AppData\Roaming\Typora\typora-user-images\image-20220525202849301.png)]

[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-r6GE9ttq-1653482215798)(C:\Users\zhaolv\AppData\Roaming\Typora\typora-user-images\image-20220525202903169.png)]

The result is the same as that of the analysis .

16 :

The documents of the 16th level are as follows :

#
# 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

According to the comments, you can know that this test file is for testing shell Is it possible to handle SIGTSTP and SIGINT Signals come from other processes rather than terminals .

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 .

The verification results :

 Insert picture description here
Consistent with the results of the analysis .

thus , Pass all the sixteen levels .

complete tsh.c Code for :

/* * 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>

/* Misc manifest constants */
#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);
}



原网站

版权声明
本文为[Hu Da jinshengyu]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060919193032.html