” 写作CMPT 300、 辅导c++设计程序2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 1/9CMPT 300 Assignment 1: Shell Commands and ProcessesTotal marks: 100 + 15 bonusOverall percentage: 12% + 1.8% bonusDue: Feb 15 (10am Pacic)This assignment may be completed idividually or in a group (up to three people).1. OverviewIn this assignment, you will develop a simple Linux shell. The shell accepts user commands and thenexecutes each command in a separate process. The shell provides the user a prompt at which the nextcommand is entered. One technique for implementing a shell interface is to have the parent processrstread what the user enters on the Command line and then create a separate child process thatperforms the command. Unless otherwise specied,the parent process waits for the child to exit beforecontinuing. However, UNIX shells typically also allow the child process to run in the background – orconcurrently – as well by specifying the ampersand () at the end of the command. The separate childprocess is created using the fork() system call and the users command is executed by using one ofthe system calls in the exec() family.Note: As usual, all code must be written in C and run on a Linux machine. We will grade your code on aLinux machine. You should create a directory for this assignment, such as ~/cmpt300/a1/ and put alllesrelated to this assignment in it.2. A Simple ShellA C program that provides the Basic operation of a command line shell is given below. The main()function rstcalls read_command(), which reads a full command from the user and tokenizes it intoseparate words (arguments). These tokens can be passed directly to execvp() in the child process. Ifthe user enters an as the nalargument, read_command() will set the in_background parameter totrue (and remove the from the array of tokens). For example, if the user enters ls -l at the $ prompt,tokens[0] will contain ls, tokens[1] will contain (or point to) the string -l, and tokens[2] will be aNULL pointer indicating the end of the arguments. (Each of these strings is a NULL terminated C-stylestring). Note that the character array buff will contain the text that the user entered; however, it will notbe one single NULL terminated string but rather a bunch of NULL terminated strings, each of which is atoken pointed to by the tokens array.#include stdio.h#include stdbool.h#include stdlib.h#include string.h#include unistd.h2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 2/9#define COMMAND_LENGTH 1024#define NUM_TOKENS (COMMAND_LENGTH / 2 + 1)/*** Read a command From the keyboard into the buffer buff and tokenize it* such that tokens[i] points into buff to the ith token in the command.* buff: Buffer allocated by the calling code. Must be at least* COMMAND_LENGTH bytes long.* tokens[]: Array of character pointers which point into buff. Must be at* least NUM_TOKENS long. Will strip out up to one final token.* tokens will be NULL terminated.* in_background: pointer to a boolean variable. Set to true if user entered* an as their last token; otherwise set to false.*/void read_command(char *buff, char *tokens[], _Bool *in_background){// … Full code available in shell.c…}/*** Main and Execute Commands*/int main(int argc, char* argv[]){char input_buffer[COMMAND_LENGTH];char *tokens[NUM_TOKENS];while (true) {// Get command// Use write because we need to use read()/write() to work with// signals, and they are incompatible with printf().write(STDOUT_FILENO, $ , strlen($ ));_Bool in_background = false;read_command(input_Buffer, tokens, in_background);/*** Steps For Basic Shell:* 1. Fork a child process* 2. Child process invokes execvp() using results in token array.* 3. If in_background is false, parent waits for* child to finish. Otherwise, parent loops back to* read_command() again immediately.*/}2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 3/9return 0;}3. ProblemsThere are three mandatory problems (100 points) that you will need to solve:Create the child process and executing the command in the child.Implement some internal commands.Implement a history feature.There is also one optional problem for a 20-point bonus, but you must attempt the mandatory problemsrst,before attempting the bonus problem. Attempting the bonus problem without work done in themandatory problems will not get you any points for the bonus problem.Problem 1. Creating Child Process [30 points]First, modify main() so that upon returning form read_command(), a child is forked and executes thecommand speciedby the user. As noted above, read_command() loads the contents of the tokensarray with the command speciedby the user. This tokens array will be passed to the execvp()function, which has the following interface:execvp(char *command, char * params[]);where command represents the command to be performed and params stores the parameters to thiscommand. For this project, the execvp() function should be invoked asexecvp(tokens[0], tokens);Be sure to check the value of in_background to determine if the parent process is to wait for the childexit or not. Hint: use waitpid() instead of wait() because you want to wait on the child you justlaunched. If you only use wait() and have previously launched any child processes in the backgroundthat have terminated, wait() will immediately return having consumed the previous zombie process,and your current process incorrectly acts as though it was run in the background. Note that we wont betesting with interactive command-line processes run in the background (think vim), or test using signalswhile running a command in the background. If execvp() returns an error (see man execvp) thendisplay an error message. Note that using printf() may not work well for this assignment and that youshould use write() instead (look up more with man write). The issue is that we need to use theread() function for Getting the users command and use write() when working with signals (later).And, it turns out that printf() and read()/write() dont always work well together. Therefore, whenprinting to the screen, use the write() command. For common things, such as displaying a simplestring, or writing a number to the screen, you may want to make your own functions which make iteasier. You can convert an integer to a string using sprintf().Waiting Aside: When a process in Linux nishes,it still exists in the kernel with some status informationuntil the parent process waits on that child. These un-waited-on terminated child processes are known2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 4/9zombies. For this assignment, you wont lose any marks if you dont correctly wait() on zombieprocesses from background tasks; however, its a good habit to correctly cleanup the zombies on yoursystem! Above it is suggested that you use waitpid() to wait on the correct child. However, this willleave any background process as zombie processes (having exited, the parent process will neverwait() on the child). You can correct this by occasionally trying a non-blocking wait to handle anyzombie child processes. We can pass the WNOHANG option to waitpid() to be non-blocking, and settingthe PID to -1 will wait on any child process. For example, have the following code run after every usercommand is processed:// Cleanup any previously exited background child processes// (The zombies)while (waitpid(-1, NULL, WNOHANG) 0); // do nothing.Problem 2. Shell Prompt and Internal Commands [30 points]First, make sure your shell prompt always shows the current working directory. For example, if in the/home/cmpt300 folder, the prompt should be:/home/cmpt300$Next, lets implement some internal commands. Internal commands are built-in features of the shellitself, as opposed to a separate program that is executed. Implement the commands listed below. Notethat for these you need not fork a new process as they can be handled directly in the parent process. Allthe commands here are case-sensitive.exit: Exit the shell program. If the user provided any argument, abort the operation (i.e., commandnot executed) and display an error message.pwd: Display the current working directory. Use the getcwd() function. Run man getcwd for more.Again, abort the operation and display an error message if the user provided any argument.cd: Change the current working directory. Use the chdir() function. Pass chdir() the rstargumentthe user enters (it will accept absolute and relative paths). If the user passed in more than oneargument, abort the operation and display an error message. If chdir() returns an error, display anerror message.help: Display help information on internal commands.If the rstargument is one of Our internal commands, print argument is a builtin command plusa brief description on what the command does. For example, if argument is cd, the output shouldbe:cd is a builtin command for changing the current working directory.If the rstargument is not an internal command, this command prints argument is an externalcommand or application. For example, if argument is ls, the output must be:ls is an external command or application2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 5/9If there is more than one argument, display an error messageIf there is no argument provided, list all the supported internal commands. For each command,include a short summary on what it does.Problem 3. History Feature [40 points]The next task is to modify your shell to provide a history feature that allows the user access up to the 10most recently entered commands. Start numbering the users commands at 0 and increment for eachcommand entered. These numbers will grow past 9. For example, if the user has entered 35 commands,then the most recent 10 will be numbered 15 through 34.History Commands: First implement an internal command history which displays the 10 most recentcommands executed in the shell. If there are not yet 10 commands entered, display all the commandsentered so far.Display the commands in descending order (sorted by its command number).The output should include both external application commands and internal commands.Display the command number on the left, and the command (with all its arguments) on the right.Hint: Print a tab between the two outputs to have them line up easily.If the command is run in The background using , it must be added to the history with the .A sample output of the history command is shown below:/home/cmpt300$ history30 history29 cd /home/cmpt30028 cd /proc27 cat uptime26 cd /usr25 ls24 man pthread_create23 ls22 echo Hello World from my shell!21 ls -la/home/cmpt300$Next, implement the ! commands which allows users to run commands directly from the history list:Command !n runs command number n, such as !22 will re-run the 23rd command entered thissession. In the above example, this will re-run the echo command.If n is not a number, or an invalid value (not one of the previous 10 command numbers) then displayan error.You may treat any command starting with ! as a history command. For example, if the user types!hi, just display an error. Note that atoi(hi) returns 0, which should not be treated as a validcommand.Command !! runs the previous command.If there is no previous command, display an error message.2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 6/9When running a command using !n or !!, display the command from history to the screen so theuser can see what command they are actually running.Neither the !! nor the !n commands are to be added to the history list themselves, but rather thecommand being executed from the history must be added. Here is an example./home/cmpt300$ echo testtest/home/cmpt300$ !!echo testtest/home/cmpt300$ history15 history14 echo test13 echo test12 ls11 man pthread_create10 cd /home/cmpt3009 ls8 ls -la7 echo Hello World from my shell!6 history/home/cmpt300$SuggestionsImplement history as a global two dimensional array:#define HISTORY_DEPTH 10char history[HISTORY_DEPTH][COMMAND_LENGTH];Rather than having all your code directly access the history array, write some functions whichencapsulate accesses to this array. Suggested functions would include: add command to history,retrieve command (copy into buffer, likely), printing the last 10 commands to the screen.Signals: Change your shell program to display the help information when the user presses ctrl-c (whichis the SIGINT signal). A guide on using signals is available here.In main(), register a custom signal handler for the SIGINT signal.Have the signal handler display the help information (same as the help command).Then re-display the command-prompt before returning.Note that when another child process is running, ctrl-c will likely cancel/kill that process. Thereforedisplaying the help information with ctrl-c will only be tested when there are no child processesrunning.Suggestions2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 7/9To implement this, you will also need to change read_command() a little bit.The signal handler will do nothing but displaying the help information and then display the promptagain. The signal will interrupt the read() system call and discard all data already read for thiscommand.When read() fails, it returns -1. You can check why read fails: if it returns -1 and the environmentvariable errno equals EINTR it means that it was interrupted by a signal. If the return value is -1 anderrno is any other value, it means read just failed and the program should exit.To correctly check read()s return value you can change the code that you now have:if (length 0) {perror(Unable to read command. Terminating.\n);exit(-1); /* terminate with error */}to the following:if ((length 0) (errno !=EINTR)) {perror(Unable to read command. Terminating.\n);exit(-1); /* terminate with error */}Problem 4 (Optional). Better cd Command [15 bonus points]Note: You must nishProblems 1, 2 and 3 before attempting Problem 4. Submitting Problem 4 alonewithout solutions to Problems 1, 2 or 3 will leave you with 0 mark for Problem 4.Implement the following features that are often supported by the cd command in modern shells (e.g.,bash).Change to the home directory if no argument is provided. [4 points]Support ~ for the home folder. [6 points]For example, cd ~/cmpt300 should change to the cmpt300 directory under the current users homedirectory. Issuing cd ~ will switch to the home directory.Support – for changing back to the previous directory. [5 points]For example, suppose that the current working directory is /home and you issued cd / to change tothe root directory. Then, cd – will switch back to the /home directory.Hints: You may ndthe getuid and getpwuid functions useful. They allow you to gather usefulinformation about the current user.3. NotesYou do not need to support either or | from the terminal. These are features of the normal Linuxterminal that we are not implementing. Your code must not have any memory leaks or memory accesserrors (using Valgrind). Your shell must free all memory before it exits. However, your child processes2021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 8/9may exit without freeing all their memory (if exec() fails then the child process will have a copy of all thememory that the parent holds; freeing this memory would be unnecessarily time consuming for the OS).Therefore, you may ignore any Valgrind messages when a child process terminates to the effect ofmemory being held when program terminated.4. ResourcesSample code: shell.c, MakeleGuide to Signals5. SubmissionYou need to submit a compressed archive named a1.tar.gz to CourSys of your code and a make le(sample code: shell.c, Makele).If you did this assignment in a group, include also a simple readme textlein your package that details each group members contributions; each of you should contribute to areasonable share of the assignment, and every group member will receive the same marks. We will buildyour code using your make le,and run it using the command: ./shell. You may use more than one.c/.h lein your solution if you like. If so, your Makefile must correctly build your project. Pleaseremember that all submissions will automatically be compared for unexplainable similarities.Following the steps below to prepare your submission:Make sure that your lesare stored in a directory as called a1Change to each of your folders and issue the command make clean. This will remove all object lesas well as all output and temporary lesChange to your a1 folder:$ cd ~/cmpt300/a1Then, issue:$ tar cvf a1.tar *which creates a tar ball (i.e., a single le)that contains the contents of the folder.Compress your leusing gzip:$ gzip a1.tarSubmit via CourSys by the deadline posted there.Grading PoliciesMake sure you are familiar with the course policies. Especially, we do not accept late submissions, soplease submit on time by the deadline. Your code must compile and run on Linux; you will receive a 02021/2/8 SFU CMPT 300 – Assignment 1 httpss://systems.cs.sfu.ca/cmpt300/spring21/assignments/a1/a1.html 9/9mark if your code does not compile. Sample solutions will not be provided for assignments. Your codeshould also not leak any memory; we will check using Valgrind. Any memory leak will lead to 10 marksoverall deduction. We will test your code using more complex and hidden test cases, so you areencouraged to vary as many parameters as possible to test your code.* Created by Mohamed Hefeeda, modiedby 436 Brian Fraser, Keval Vora and Tianzheng Wang.如有需要,请加QQ:99515681 或WX:codehelp
“
添加老师微信回复‘’官网 辅导‘’获取专业老师帮助,或点击联系老师1对1在线指导。