A shell is a program which allows a user to send commands to the operating system (OS), and allows the OS to respond to the user by printing output to the screen. The shell allows a simple character-oriented interface in which the user types a string of characters (terminated by pressing Enter(\n)) and the OS responds by printing lines of characters back to the screen. Typical Shell Interaction The shell executes the following basic steps in a loop.
The shell prints a “prompt>” to indicate that it is waiting for instructions. prompt>
The user types a command, terminated with an character (‘\n’). All commands are of the form COMMAND [arg1] [arg2] … [argn]. prompt> ls
The shell executes the chosen command and passes any arguments to the command. The command prints results to the screen. Typical printed output for an ls command is shown below. prompt> ls hello.c hello testprog.c testprog
Some commands may accept arguments. These arguments must be provided after the command, on the same line as the command. All arguments will be separated by 1 or more blankspace characters, either space or tab characters. An example of a command which accepts arguments is shown below where a program called “foo” is invoked and the program takes three command-line arguments. prompt> foo 1 2 3 Types of commands that your shell must support There are two types of commands, built-in commands which are performed directly by the shell, and general commands which indicate compiled programs which the shell should cause to be executed. Your shell will support five built-in commands: jobs, bg, fg, kill, and quit. You shell must also support general commands. General commands can indicate any compiled executable. We will assume that any compiled executable used as a general command must exist in the current directory. The general command typed at the shell prompt is the name of the compiled executable, just like it would be for a normal shell. For example, to execute an executable called hello the user would type the following at the prompt: prompt> hello Built-in commands are to be executed directly by the shell process and general commands should be executed in a child process which is spawned by the shell process using a fork command. Be sure to reap all terminated child processes. Job Control A job is a program started interactively from the shell. Each job is assigned a sequential job ID (JID). Because a job is executed within a process, each job has an associated process ID (PID). Each job can be in one of three states:
Foreground/Running: A foreground job is one which blocks the shell process, causing it to wait until the foreground job is complete. While a foreground job is executing, the shell cannot accept commands. Some jobs execute very quickly (like printing hello on the screen) but some jobs may take a long time to complete execution and would block the shell as long as they are running. Only one job can run in the foreground at a time. A foreground job is started when the user simply enters the name of the executable program at the prompt. For example, the following command executes a program called “hello” in the foreground. • prompt> hello You can assume that any built-in command will be executed in the foreground.
Background/Running: When you start a program and enter an ampersand (&) symbol at the end of the command line, the program becomes a background job. In this case, the shell process is not blocked while the job is executing, so the shell can be used while the job is executing. Immediately after a background program is started, the shell will print a new prompt and the user can enter new commands. This is an example of a background job. • prompt> hello & In this example, the ampersand (&) symbol is a unique token, separated from the text before it by one or more space characters. For this assignment you can assume that that space separation will always exist.
Stopped: A job is stopped if it is not currently executing but it is not terminated, so it can be restarted later. A job which is stopped must be automatically placed in the background so that it does not cause the shell to be blocked. You can assume that a built-in command will never be stopped. Built-In Commands • jobs: List the running and stopped background jobs. Status can be “Running” (if it is in the “Background/Running” state) and “Stopped”. The format is shown here. [<job_id>] () <command_line> prompt> jobs [1] (30522) Running hello & [2] (30527) Stopped sleep • fg <job_id|pid>: Change the state of a job currently in the Stopped state or the Background/Running state to the Foreground/Running state. A user may refer to the job using either its job_id or pid. In case the job_id is used, the JID must be preceded by the “%” character. prompt> fg %1 prompt> fg 30522 • bg <job_id|pid>: Change the state of a job currently in the Stopped state to the Background/Running state. • kill <job_id|pid>: Terminate a job by sending it a SIGINT signal using the kill() system call. Be sure to reap a terminated process. • quit: Ends the shell process. Processes and Jobs Built-in commands are executed in the shell process, but general commands are executed in a child process which is forked by the shell. All processes forked by the shell process must be properly reaped after the child process is terminated. How reaping should be performed depends on whether the job is a foreground or background job. If the job is a foreground job then the shell should explicitly call wait() or waitpid() after it forks the new child process. If the job is a background job then the shell should not call wait() or waitpid() because this would cause it to block until the child process is complete. Instead, you should create a handler for the SIGCHLD signal which calls wait() or waitpid(). The SIGCHLD signal is received by the shell process whenever one of its child processes terminates. By using the SIGCHLD handler to reap the child process, the shell process does not need to block while the job executes. If a foreground job is running and the user types ctrl-C into the shell, then the foreground job should be terminated. You should accomplish this by creating a handler for the SIGINT signal in the shell. The SIGINT signal is received by the shell when a user types ctrl-C and the default behavior would be to terminate the shell. Instead of terminating the shell, the child process running the foreground job should be terminated. Your handler for the SIGINT signal should send a SIGINT signal to the foreground child process using the kill() system call. If a foreground job is running and the user types ctrl-Z into the shell, then the foreground job should be stopped (moved to the Stopped state). You should accomplish this by creating a handler for the SIGTSTP signal in the shell. The SIGTSTP signal is received by the shell when a user types ctrl-Z and the default behavior would be to stop the shell process. Instead of stopping the shell, the child process running the foreground job should be stopped. Your handler for the SIGTSTP signal should send a SIGTSTP signal to the foreground child process using the kill() system call. Summary of Job State Changes Each job can be moved between one of the three job states, Foreground/Running, Background/Running, and Stopped. Each job can also be forcibly terminated by the user. • If the user enters ctrl-C into the shell while a foreground job is executing, the job is terminated. • If the user enters the kill built-in command then the indicated job is terminated, whether it is in the state Background/Running or Stopped. • If the user enters ctrl-Z into the shell while a foreground job is executing, the foreground job moves to the Stopped state. You can assume that built-in commands are never stopped. • If a user enters fg <job_id|pid> into the shell and the job indicated by <job_id|pid> is currently in the Stopped state or the Background/Running state, then it is moved to the Foreground/Running state. In order to move a process from the Stopped state to the Foreground/Running state, the process must be restarted by sending it a SIGCONT signal using the kill() system call. • If a user enters bg <job_id|pid> into the shell and the job indicated by <job_id|pid> is currently in the Stopped state, then it is moved to the Background/Running state. In order to move a process from the Stopped state to the Background/Running state, the process must be restarted by sending it a SIGCONT signal using the kill() system call. I/O redirection Your shell must support I/O redirection. Most command line programs that display their results do so by sending their results to standard output (display). However, before a command is executed, its input and output may be redirected using a special notation interpreted by the shell. To redirect standard output to a file, the “>” character is used like this: prompt> ls > file_list.txt In this example, the ls command is executed and the results are written in a file named file_list.txt. Since the output of ls was redirected to the file, no results appear on the display. Each time the command above is repeated, file_list.txt is overwritten from the beginning with the output of the command ls. To redirect standard input from a file instead of the keyboard, the “<” character is used like this: prompt> sort < file_list.txt In the example above, we used the sort command to process the contents of file_list.txt. The results are output on the display since the standard output was not redirected. We could redirect standard output to another file like this: prompt> sort < file_list.txt > sorted_file_list.txt I/O redirection Authorization We should add permission bit when we do I/O Redirection. Permission bits control who can read or write the file. https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html • Input redirection to “input.txt” (Read) mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; inFileID = open (“input.txt”, O_RDONLY, mode); dup2(inFileID, STDIN_FILENO); • Output redirection to “out.txt” (Create or Write) outFileID = open (“out.txt”, O_CREAT|O_WRONLY|O_TRUNC, mode); dup2(outFileID, STDOUT_FILENO); Submission Instructions Your source code should be a single c file named ‘hw2.c’. Submissions will be done through Gradescope. You have already been added on the Gradescope course for ICS53. Please login to with your school (UCI) email to access it. Please remember that each C program should compile and execute properly on openlab.ics.uci.edu when it is compiled using the gcc compiler version 4.8.5. The only compiler switches which may be used are -o (to change the name of the executable), -std=c99 (to use C99 features), and - std=c11 (to use C11 features). Specific directions • Headers: stdio.h, string.h, unistd.h, stdlib.h, sys/stat.h, sys/types.h, sys/wait.h, ctype.h, signal.h, fcntl.h
As long as you can compile it on openlab (gcc 4.8.5) without any additional compiler flags, you can use any headers other than those specified. • MaxLine: 80, MaxArgc: 80, MaxJob: 5 • Use both execvp() and execv() to allow either case. execvp() : Linux commands {ls, cat, sort, ./hellp, ./slp}. execv() : Linux commands {/bin/ls, /bin/cat, /bin/sort, hello, slp}. Example 1
Create 2 programs, add.c and counter.c, and compile them. < add.c > int main(int argc, char * argv[]){ int n = atoi(argv[1]); printf("%d \n", n+2); // print n+2 return 0; } <counter.c> int main() { unsigned int i = 0; while(1) { printf(“Counter: %d\n”, i); i++; sleep(1); } } $gcc add.c -o add $gcc counter.c -o counter Now we have compiled executables, “add” and “counter”. ^Z in below == ctrl-Z Example 2 Prepare two terminals on the same server. [Terminal1] ssh <your_id>@openlab.ics.uci.edu You will see your server name e.g. <your_id>@circinus-14 circinus-14 is your current server name in this example. [Terminal2] ssh <your_id>@<server_name>.ics.uci.edu e.g. ssh <your_id>@circinus-14.ics.uci.edu In this way you can access to the same server using two terminals.
[Terminal2] ps -e | grep “counter” No output < Terminal 1 > <Terminal 2 > If you can still see “<a_pid> pts/0 00:00:01 counter” after kill, it means that you did not properly terminate a child process. References “Understanding the job control commands in Linux – bg, fg and CTRL+Z“, Understanding the job control commands in Linux – bg, fg and CTRL+Z”, accessed Jan 10, https://www.thegeekdiary.com/understanding-the-job-control-commands-in-linux-bg-fg-and-ctrlz/
这篇关于ICS 53, Spring 2021 Assignment 2: A Simple Shell的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!