The background work has been done in the last section of the simple processing, basically to achieve the function has been finished, the following back to the code to make an adjustment, write a bad place to comb a bit,
Add appropriate comments to the Code, this habit is actually better, because in the development time is relatively tight, are just a way to achieve as soon as possible, and there will be some code is not well written, so it is best to have time to comb the whole code from beginning to finish, perhaps in the process of carding will find many deficiencies in the place, well, The following starts: And this signal installation function is implemented in INIT.C: The following is the Shell loop: it is implemented in PARSE.C: As noted in the note, you can move to init.c: Next, get the command: Then parse the command: The next sentence, for testing, can be commented out at the time of publication: The final execution of the command: Method inside the code is a bit messy, the following to extract its implementation into another file, so that the function to look refreshing: the implementation of EXECUTE.C:
#include"execute.h"#include"def.h"#include"Externs.h"#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<linux/limits.h>#include<fcntl.h>voidForkexec (inti) {pid_t pid; PID=Fork (); if(PID = =-1) { /*the creation process failed*/Err_exit ("Fork"); } if(PID >0) { /*Parent Process*/ if(Backgnd = =1) printf ("%d\n", PID); Lastpid=pid; } Else if(PID = =0) { /*Child Process*/ /*indicates that the INFD of the first simple command is redirected to/dev/null, where cmd[i].infd = = 0 may be the first simple command*/ /*when the first command attempts to fetch data from the standard input, it returns both EOF*/ if(CMD[I].INFD = =0&& Backgnd = =1){ //Block background jobs because job control is not implementedCMD[I].INFD = open ("/dev/null", o_rdonly); } /*the first simple command process as the group leader of the process*/ if(i = =0) {Setpgid (0,0); } if(CMD[I].INFD! =0){ //indicates that the input to the command is the read-side of the pipelineClose0); DUP (CMD[I].INFD); } if(Cmd[i].outfd! =1){ //indicates that the output of this command is pointing to the write end of the pipelineClose1); DUP (CMD[I].OUTFD); } /*Close all file descriptors above 3*/ /*int i; for (i=3; i<open_max; ++i) {close (i); }*/ /*the foreground job can receive the sigint,sigquit signal, the two signals will be restored to the default operation*/ if(Backgnd = =0){//non-background jobssignal (SIGINT, SIG_DFL); Signal (Sigquit, SIG_DFL); } /*start the Replace process*/EXECVP (cmd[i].args[0], Cmd[i].args); /*If this sentence is executed, then the replacement is proved to be unsuccessful.*/exit (exit_failure); }}intExecute_disk_command (void){ /*ls | grep init | wc-w*/ if(Cmd_count = =0) { return 0; } if(infile[0] !=' /') {cmd[0].INFD =Open (infile, o_rdonly); } if(outfile[0] !=' /'){ if(append)//The description is in an additional waycmd[cmd_count-1].OUTFD = open (outfile, o_wronly | O_creat | O_append,0666); ElseCmd[cmd_count-1].OUTFD = open (outfile, o_wronly | O_creat | O_trunc,0666); } /*because the background job does not call wait for the child process to exit, the SIGCHLD signal can be ignored in order to avoid the zombie process*/ if(Backgnd = =1) {signal (SIGCHLD, sig_ign); }Else{signal (SIGCHLD, SIG_DFL); } inti; /*Pipe Descriptor*/ intfds[2]; intFD; for(i=0; i<cmd_count; ++i) { /*If this is not the last command, you need to create a pipeline*/ if(I < cmd_count-1) {pipe (FDS); /*the output of the first command is no longer the standard output, but the write end of the pipeline*/CMD[I].OUTFD= fds[1]; /*the input of the second command is no longer the standard input, but the read end of the pipe*/Cmd[i+1].INFD = fds[0]; } /*Create a process and replace it with a system command*/forkexec (i); if(FD = CMD[I].INFD)! =0) Close (FD); if(FD = cmd[i].outfd)! =1) Close (FD); } if(Backgnd = =0){//if a non-background job while(Wait (NULL)! =lastpid); }}
The Forkexec function is also extracted to the execute.c file, which is compiled below:
In addition, we need to revise the makefile:
Modify so much after the following compile: Well, for the above implementation are explained external command, that for the internal command of the system also need to be compatible, the following is mainly to achieve internal command parsing: first to determine whether it is internal command, if yes, Execute internal command: Also need to include builtin.h in the parse.c file: The following to compile: The description forgot to add the builtin.o file to the makefile, modify and then compile: a lot of mistakes, do not worry, a solution, the first BUILTIN.C files need to use check function, which was previously defined in parse.c, should be defined in parse.h, let builtin.c to include it, and also include some system header files: In builtin.c to include parse.h files: Then make and execute: Think of a problem: The system has a lot of internal commands, that is not every parsing an internal command, We all want to add a judgment statement in the BUILTIN.C, this will cause the BUILTIN function will be more and more large, so this implementation is still not very flexible, the following use array to avoid this situation: The last BUILTIN.C code is as follows:
#include"Builtin.h"#include"parse.h"#include"Externs.h"#include<stdlib.h>#include<stdio.h>typedefvoid(*cmd_handler) (void); typedefstructbuiltin_cmd{Char*name; Cmd_handler HANDLER;} Builtin_cmd;voidDo_exit (void);voidDO_CD (void);voidDo_type (void); Builtin_cmd builtins[]= { {"Exit", Do_exit}, {"CD", Do_cd}, {"type", Do_type}, {null, NULL}};/** Internal Command resolution * Returns 1 for internal command, 0 for not internal command*/intBuiltinvoid){ /*if (check ("Exit")) Do_exit (); else if (check ("CD")) Do_cd (); else return 0; return 1; */ inti =0; intFound =0; while(Builtins[i].name! =NULL) { if(check (builtins[i].name)) {Builtins[i].handler (); Found=1; Break; } I++; } returnfound;}voidDo_exit (void) {printf ("exit\n"); Exit (exit_success);}voidDO_CD (void) {printf ("do_cd ... \ n");}voidDo_type (void) {printf ("do_type ... \ n");}
Compile run:
OK, about the specific implementation of the internal command here is not much to say, mainly to achieve its principle, so that its own a small shell program has been completed, and the system shell program is much different, but through this program is enough to connect the knowledge previously learned, To achieve a good practice for the purpose of the small shell program in the end to achieve this, things are still more, need to digest."description": Because there are too many Linux system commands, here are just a few simple implementations, to do an example, the focus is to know its implementation principle. The functions of each file used in the program are listed below: MAIN.C----Keynote program Def:h define constants, struct EXTERNS.H----define extern variables init.h/init.c----Do some initialization operations parse.h/ PARSE.C----Do the parsing of the command execute.h/execute.c----do the execution of the external command BUILTIN.H/BUILTIN.C----Do the internal command "only implement its principle"