EE217: Software development environment and tools practice
Lecture 11:
How a UNIX shell works
What does the computer do for us?
• What does the computer do for us?
• Run programs
• Store data
• Communicate with each other
• Interact with us
• The interaction with us means...
Run
program!
Store
data!
Communicate with
other computer!
2
How can we use computer?
Run
program!
Store
data!
Communicate with
other computer!
How can we interact with the
computer?
• What is the way to use the computer?
• Telepathy? - No
• Speech? – No (well… kind of, these days)
• Rewiring? – Not nowdays (only before 1950s)
• Type the command you want
3
The shell: the program that initiate commands
Shell: the program initiates commands that users type
I want to run the program
“[Link]”!
Type Find the file
./[Link] “[Link]”!
Load the file
“[Link]”!
Return results Execute the “[Link]”
Shell from its main
function!
• Receives a string that contains the command from the user
• Interprets the string
• Lets the computer work on initiating the command that the user requested
There is Operating System (Kernel) between the shell and the computer
4
GUI vs. CLI
• GUI (Graphic User Interface) • CLI (Command Line Interface)
• Use mouse, windows, icons • Use only keyboard, shell, commands
• Easy to use • Hard to use
• It has limitations to use some programs • It supports almost all programs
• make, server-managing program • It allows combining processes and
• Cannot combine processes or adjust I/O adjusting I/O with pipe, redirection
• ex: Windows, Gnome, KDE • ex: Powershell (Windows), Bash, Zsh
5
Very simple pseudo code of the shell
int i, pid;
char *token, command[2000], *arguments[10];
① Get the command
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
token = strtok(command, “ “);
if (token == NULL) {exit(-1);}
arguments[0] = token; ② Delimit command by space bar
for (i = 1; i<10; i++) {
token = strtok(NULL, “ “); ③ Compose argument array
if (token == NULL)
break;
arguments[i] = token;
}
arguments[i] = NULL;
pid = fork(); ④ fork child process
if (pid != 0){
wait(NULL); ⑤ the parent process waits for exit of the child
} else {
execvp(arguments[0], arguments); ⑥ the child changes its execution image to
}
arguments[0]
6
Very simple pseudo code of the shell
Let us assume that a user typed
“echo hello quasi shell”
• Note that this simple shell has some limitations
• Executes only one command
• Users can type only 2,000 characters in maximum
• Users can use only 10 options in maximum
• It cannot change directory (cd)
• It doesn’t have environment variables such as $PATH
• So users have to specify the full path of the program such as
“/bin/echo” and not just “echo”
• In this lecture, for ease of understanding, we omit the full path
• If you use execvp(), execvpe(), execlp()(note “p” in the names),
it automatically finds the binary in $PATH
• It doesn’t support the pipe and the redirection
7
Functions you must know
• fgets
• strtok
• fork
• wait
• execvp
8
fgets
char *fgets(char *s, int size, FILE *stream);
• It reads at most (size – 1) characters from stream and stores them into
the buffer pointed to by s
• The scanf cannot read string containing “ “ (space) while fgets can!
• The NULL is stored after the last character in the buffer
• Returns s on success, NULL on fail
• ex) Getting inputs from a user by the keyboard (stdin)
char buf[200];
fgets(buf, sizeof(buf), stdin);
9
strtok
char *strtok(char *str, const char *delim);
• It breaks a string pointed by str into a sequence of zero or more nonempty tokens
based on the string pointed by delim
• The first call and subsequent calls are different
• First use: The string to be parsed should be specified in str
• Subsequent calls: The str must be NULL
• Returns a pointer to the next token, or NULL if there are no more tokens
• ex) Parsing a simple string “Hello, World and Universe”
char str[] = “Hello, World and Universe” ,
char* token;
token = strtok(str, “ “); // token = “Hello,”
token = strtok(NULL, “ “); // token = “World”
token = strtok(NULL, “ “); // token = “and”
token = strtok(NULL, “ “); // token = “Universe”
10
fork
pid_t fork(void)
• It creates a new process by duplicating the calling process
• The new process is referred to as the child process
• The calling process is referred to as the parent process
• Returns 0 to the child, and pid of the child to the parent
pid_t pid;
pid = fork();
if (pid == 0)
printf(“I am a child” );
else
printf(“I am a parent and I have a child who has pid %d” , pid);
11
wait
pid_t wait(int *wstatus)
• It sleeps until a state of a child is changed and stores the changed state in wstatus
• Usually, it is used to wait until a state of a child is changed to EXITED
• If the wstatus is NULL, it just returns without storing changed state
• Returns process id of the child process whose state is changed
pid = fork();
if (pid != 0) {
int status;
printf(“I am a parent and I am going to wait for my child”);
wait(&status);
printf(“The state of my child is changed. The status is %d”, status);
12
execvpe
int execvp(const char *filename, char *const argv[]);
int execvpe(const char *filename, char *const argv[], char
*const envp[]);
• It executes the program pointed to by filename with arguments in the array
argv, and environment variables in the array evnp
• Returns error code on fail, no return on success
• ex) Execute echo program
char *filename = “echo”;
char *argv[] = { “echo”, “Hello,”, “and”, “Universe” , NULL};
execvp(filename, argv);
13
Very simple pseudo code of the shell (1)
int i, pid;
char *token, command[2000], *arguments[10];
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
token = strtok(command, “ “);
arguments[0] = token;
...
• Reads the string from stdin and saves it at command
• Assume that we typed “echo hello quasi shell”,
the buffer command is like below:
command
e c h o h e l l o q u a s i s h e l l \n 0 ... ?
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1999
14
Very simple pseudo code of the shell (2)
int i, pid;
char *token, command[2000], *arguments[10];
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
token = strtok(command, “ “);
arguments[0] = token;
...
• The character ‘\n’ should be removed for executing the command
• Replace the character ‘\n’ with ‘\0’ so that the newline character is
removed from the string
command command[strlen(command)
-1]
e c h o h e l l o q u a s i s h e l l \0 0 ... 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1999
15
Very simple pseudo code of the shell (3)
int i, pid;
char *token, command[2000], *arguments[10];
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
token = strtok(command, “ “);
arguments[0] = token;
...
• Delimits command by “ “, and returns the start address of the delimited
string
• Finds “ “ and replaces “ “ with ‘\0’
• Saves position where it scanned in its static variable
command token Saves this position at static variable
e c h o \0 h e l l o q u a s i s h e l l \0 0 ... 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1999
16
Very simple pseudo code of the shell (4)
int i, pid;
char *token, command[2000], *arguments[10];
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
token = strtok(command, “ “);
arguments[0] = token;
...
• Allocates new buffer and copies contents of the delimited argument in the
buffer
command token
e c h o \0 h e l l o q u a s i s h e l l \0 0 ... 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1999
arguments[0]
17
Very simple pseudo code of the shell
...
for (i = 1; i<10; i++) {
token = strtok(NULL, “ “);
if (token == NULL)
break;
arguments[i] = token;
}
arguments[i] = NULL;
...
• Continues to delimit the command
• If strtok gets NULL as its first parameter, it starts to delimit from the
position saved in its static variable
st rd
Changed at 1 Changed at 3 loop
command token loop Changed at 2 nd
loop
e c h o \0 h e l l o \0 q u a s i \0 s h e l l \0 0 …
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
18
Very simple pseudo code of the shell
...
for (i = 1; i<10; i++) {
token = strtok(NULL, “ “);
if (token == NULL)
break;
arguments[i] = token;
}
arguments[i] = NULL;
...
• Continues to delimit the command
• if strtok gets NULL as its first parameter, it starts to delimit from the
position saved in its static variable
command token
e c h o \0 h e l l o \0 q u a s i \0 s h e l l \0 0 …
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
arguments[0 arguments[3]
1 st arguments[1] 2 nd arguments[2] 3 rd
]
loop loop loop
19
Very simple pseudo code of the shell
...
for (i = 1; i<10; i++) {
token = strtok(NULL, “ “);
if (token == NULL)
break;
arguments[i] = token;
}
arguments[i] = NULL;
...
• Saves NULL at the last element of the array arguments
• For specifying the array is ended there (like string)
0 1 2 3 4 5 6 7 8 9
argument
s
NULL
e c h o \0 h e l l o \0 q u a s i \0 s h e l l \0 0 …
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
arguments[0] arguments[1] arguments[2] arguments[3]
20
Very simple pseudo code of the shell
pid = fork();
if (pid != 0)
wait(NULL);
else
execvp(arugments[0], arguments);
• Creates the child process
• The child process starts executing code from inside of fork()
• The return value of the fork() is different between the parent process and
the child process
• Parent process gets process id of the child process made by fork()
• Child process gets 0
21
Very simple pseudo code of the shell
pid = fork();
if (pid != 0)
wait(NULL);
else
execvp(arugments[0], arguments);
• Parent process waits until the child process exits
• It gets no exit status because it passes NULL for 1st parameter
pid = fork();
if (pid != 0)
wait(NULL);
else
execvp(arguments[0], arguments);
• Child process executes the program the command requests
• Inside the execvp(), the child process starts to execute code from main()
function of the program that corresponds to arguments[0]
• execvp() does not return unless execvp() fails
22
Assignment
• Add below functionalities int i, pid;
char *token, command[2000], *arguments[10];
• Make the shell execute command
fgets(command, sizeof(command), stdin);
command[strlen(command)-1] = 0;
multiple times (use loop)
token = strtok(command, “ “);
• Make the shell get exited when if (token == NULL) {exit(-1);}
the user types “exit” arguments[0] = token;
for (i = 1; i<10; i++) {
• Add appropriate error handling token = strtok(NULL, “ “);
if (token == NULL)
• Check the fgets() return value break;
arguments[i] = token;
• What if execvp() returns }
arguments[i] = NULL;
• Name your file “studentID_name.c”
pid = fork();
and submit it to KLMS (no if (pid != 0){
wait(NULL);
} else {
screenshot or JPG file allowed) execvp(arguments[0], arguments);
}
23
24