Project 1 : sshell report
Key points:
- Parents and child process
- Parsing the input
- Build in functions(cd, pwd)
- Redirection & pipe
- Backgound Proccessing
Start
To implement the shell, firstly, we built a prototype sshell which keep running in a while loop printing sshell$ and asking for user input till exit has been entered.
sshell$
sshell$ exit
Bye...
parsing and fork()
Based on the prototype we implement the basic system call which using fork() to duplicate the current status and run the cmd inside the child proccess, the parents will catch the status of child. At this step, we wrote the parsing function, which split the cmd according to the “ “ and “\n”, and store them in an string(char arry) arry for execvp. The arrays are dynamic allocated. After this, we add more features based on this prototype.
- for this part we used execvp() because for execve() the envirmental var is alway 0, so execvp() might be better.
- we thought using a data structure to hold the parameter for execvp which is clearer, but using array is much simpler.
how to use execvp
Build in function
We faced some problem when dealing with builtin func. Initally we used the child proccess to run cd, however, it didn’t work out. Then we found out that the build in functions are functions inside parent proccess. After taking close look at GNU lib, we found it pretty straight forward by using:
chdir(path)// used for cd to change dir
getcwd()// used to get current working dir
exit() // for exit
Ps. according to the return value of chdir()
, print out the error message.
Redirection & pipe
1. redirection
We add an different parsing function for this. After get the input, we search for any redirection sign(<,>), and treat them differently. we created a data structure to store the input, output and cmd differently, also we have direction to indicate the sign.
typedef struct myData { // data to store redirection
char inputFile[1000]; // content for input
char outputFile[1000]; // content for output
char command[1000];
char direction[1]; // < or >
}Data;
for redirection we used dup2(). Depending on value of data.direction
, if
we have < then int fd_out = open(data.outputFile, O_RDWR)
, close STDIN
and redirect stdin with input by using dup2(); such as dup2(fd_out, 0);
then the rest is same as above, just execvp() the cmd with the inputfile. The
output redirection is pretty similar with redirin()
in my code.
few things to note:
- for > redirection, we initiall using
int fd_out = open(data.outputFile, O_RDWR);
which cannot write to nonexisting file later we change it toint fd_out = open(data.outputFile, O_CREAT|O_RDWR, 0666);
, it pass the tester. - one thing might need to work on is we cannot deal with the input, output redirection happens at the same time because the our parsing function didn’t support that.
2. pipe
pipe is the hardest part of this assignment for us. We finished part 6 within 2
day after the assignment prompt was published, but spent few days on pipe, I
found out its extremly hard to visulized and imagine multi-pipe. it is easy to
deal with two pipe A | B
.
Here is the way we deal with pipe finally:
- new function parsing_pipe for any give argument which contains pipe
will provide us pipe number and each argument
return -1
when there errors in pipe cmd. - based on previous build, add one more flag to indicate there is a pipe in input and handling the error pipe.
- When there is a correct pipe command, then we go over a
for loop
withthe num of pipe
times. We used pipe to connect thestdout
of the last cmd with thestdin
of the next cmd till the end offor loop
. - by doing this we basically connect the input and output throughout the pipe. therefor the multi-pipe was implemented.
few notes for pipe:
- few ideas from how pipe works
- teach u about shell
- Book–Unix OS Design and Practics (Chapter 7,8)
- < will only appear at the first pipe; and > will only at end of the pipe, if not will cause ambiguity
- pipe is kind of similar to redirection part, they both using dup to redirect the input and output, but this part use pipe as a channel to communicate between each other.
- the assignment didn’t ask us to do about « and », which might be similar as < and >
Last bg_proccessing
For bg proccess, which noted as &
at the end of cmd. To implement this,
when get bg_proccess input, the parent didn’t wait for the child to finish
directly asking for next operation. We have a new data structure bg_table to
hold bg_cmd.
typedef struct bgproccess { // bg processTable
pid_t pid;
char cmd[3000];
int status;
int completed;
int printsig;
}bg_proccess;
add two functions:
function 1 : int check_background(char *cmd[])
- return -1 when & mislocated.
- return 1 correct bg and remove
&
. - return 0 when given cmd has no bg.
function 2 : void background_processing(bg_proccess **processTable, int *cannot_exit)
.
- check processtable catch zombie proccess
with
waitpid((*processTable)[iter].pid, &((*processTable)[iter].status), WNOHANG);
. - if proccess completed then check if it is printed.
- if there still bg running set cannot_exit for later
exit()
use.
at the very beginning of the while loop call background_processing
see if
there any finished bg and set the cannot_exit value. When get an input,
callcheck_background
see if is a bg proccess. if it is a bg, then fork()
save the info into the table. child proccess
keep running
while the parentscontinue;
. If the input is exit
then check for the value of
cannot_exit, if is 1
then exit(0);
else print out Error, still bg running
.
notes for bg:
- if child is exits before parents exits, and if there no specific waitpid() to collect them, it will become zombie proccess.
- if parents exits before child exits, the child will become the child of init proccess.
Overview:
- The parsing part is complex but require less skill comparing to other part. It is very important, though.
- didn’t using gdb, but using print to find out
errors(write function
log()
to short code thefprintf()
) - Error message is useful when checking for bugs.
- pointer requires lot of work and easy cause seg fault.
- using the free() and memset to clean the content of the container is very important which will avoid overlapping cmd.
- git is useful to keep tracking what we are doing.
- challenging but interesting project.
Design Choice
We wrote the sshell step by step and build each phase on each other. We tried to separating the code at each phase, so that if one case crashes has no effect on overall sshell. Also it make it easier to debug since it is separate out. Though it will make the code much longer, and a lot of code is kind of repeatitive.
Testting
We run the testing under bash, redirect the stdout and stderr in to a file and using diff
to check the results against the sshell_ref. When we found an error, we step printed out the
var and sign to find out the problem. Since our program is separate for each phase, it is easy
to locate the problem.
While we debuging the sshell, free the pointer and memset
the content of char arry is very
important to get rid of info overlapping, which causes a lot of err.
some link we used:
http://www.cnblogs.com/wuyuegb2312/p/3369082.html
http://stackoverflow.com/questions/14301407/how-does-execvp-run-a-command
http://stackoverflow.com/questions/19099663/how-to-correctly-use-fork-exec-wait