Question
CPS 470 Introduction to Operating Systems Lab 2 Process Management Objectives To get familiar with the process of kernel module compilation. To present how a
CPS 470 Introduction to Operating Systems
Lab 2 Process Management
Objectives
- To get familiar with the process of kernel module compilation.
- To present how a module can be used with a kernel.
- To get familiar with simple kernel debugging methods.
- To implement two kernel modules.
Due Date: Sunday, Feb. 14, 2021 (at 11:59 p.m.).
Machine Details
Complete this assignment by yourself on the Ubuntu Virtual Machine. Make sure that the kernel version of your operating system is 5.8.0-38-generic (or higher). To check the kernel version, type uname -r in the Terminal. To update your kernel version, you can run the Software Updater program in Ubuntu.
Lab Description
This project consists of designing a C program to serve as a shell interface that accepts user commands and then executes each command in a separate process. Your implementation will support input and output redirection, as well as pipes as a form of IPC between a pair of commands. Completing this project will involve using the UNIX fork(), exec(), wait(), dup2(), and pipe() system calls.
- Overview
A shell interface gives the user a prompt, after which the next command is entered. The example below illustrates the prompt osh> and the users next command: cat prog.c (This command displays the file prog.c on the terminal using the UNIX cat command.)
osh>cat prog.c
One technique for implementing a shell interface is to have the parent process first read what the user enters on the command line (in this case, cat prog.c) and then create a separate child process that performs the command. Unless otherwise specified, the parent process waits for the child to exit before continuing.
The separate child process is created using the fork() system call, and the users command is executed using one of the system calls in the exec() family.
A C program that provides the general operations of a command-line shell is supplied below and is uploaded to Blackboard. The main() function presents the prompt osh> and outlines the steps to be taken after input from the user has been read. The main() function continually loops as long as should_run equals 1; when the user enters exit at the prompt, your program will set should_run to 0 and terminate.
_____________________________________________________________________________________________
#include
#include
#define MAX_LINE 80 /* 80 chars per line, per command */
int main(void)
{
char *args[MAX_LINE/2 + 1];/* command line (of 80) has max of 40 arguments */
int should_run = 1;
while (should_run){
printf("osh>");
fflush(stdout);
/**
* After reading user input, the steps are:
* (1) fork a child process
* (2) the child process will invoke execvp()
* (3) parent process waits for the child to exit before continuing
*/
}
return 0;
}
_____________________________________________________________________________________________
This project is organized into several parts:
- Creating the child process and executing the command in the child
- Providing a history feature
- Adding support of input and output redirection
- Allowing the parent and child processes to communicate via a pipe
- Executing Command in a Child Process
The first task is to modify the main() function in the code above so that a child process is forked and executes the command specified by the user. To get the command from user, fget() function can be used. Following is the declaration for fgets() function:
char *fgets(char *str, int n, FILE *stream)
str is the pointer to an array of chars where the string read is stored. n is the maximum number of characters to be read. stream is the pointer to a FILE object that identifies the stream where characters are read from. In this case, fget() is needed to read user inputs from Terminal, which is the standard input stream, so the following porotype can be used (need to define command as character a character string with length MAX_LINE):
fgets(command, MAX_LINE, stdin);
Next what the user has entered needs to be parsed into separate tokens and store the tokens in an array of character strings (args in the above code). For example, if the user enters the command ps -ael at the osh> prompt, the values stored in the args array are:
args[0] = "ps"
args[1] = "-ael"
args[2] = NULL
To get the args array, function strtok()[1] can be used to break a string into a series of tokens using the delimiter " ". Function strtok()is defined in
char *strtok(char *str, const char *delim)
str contents of this string are modified and broken into smaller strings (tokens). delim is the C string containing the delimiters. This function returns a pointer to the first token found in the string. A null pointer is returned if there are no tokens left to retrieve. Assume user inputs is "ps -ael" and it is stored in command, the following code breaks the first token:
char *token = strtok(command, " ")
After strtok() returns, token points to the space character between "ps" and "-ael". The user input also contains a new line character " " in the end by default, including " " in delim recognizes " " as a delimiter and ignores it.
After tokens are stored in args array, it will be passed to the execvp() function, which has the following prototype:
execvp(char *command, char *params[])
Here, command represents the command to be performed and params stores the parameters to this command. For this project, the execvp() function should be invoked as execvp(args[0], args). This function replaces the current running process with a new process. It can be used to run a C program by using another C program. The exec()functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error. By checking the return value, your implementation should promote and error message "Command not found" if the command cant be executed.
- Creating a History Feature
The next task is to modify the shell interface program so that it provides a history feature to allow a user to execute the most recent command by entering !!. For example, if a user enters the command ls -l, she can then execute that command again by entering !! at the prompt. Any command executed in this fashion should be echoed on the users screen, and the command should also be placed in the history buffer as the next command.
Your program should also manage basic error handling. If there is no recent command in the history, entering !! should result in a message "No commands in history."
To check whether the command is !!, strncmp() function is used. Below is the declaration for strncmp() function:
int strncmp(const char *str1, const char *str2, size_t n)
This function compares at most the first n bytes of str1 and str2. This function return values that are as follows:
- If Return value < 0 then it indicates str1 is less than str2.
- If Return value > 0 then it indicates str2 is less than str1.
- If Return value = 0 then it indicates str1 is equal to str2.
- Redirecting Input and Output
Whenever running any command in the terminal, stdin, stderr, and stdout are three data streams that bash creates. Essentially, they allow piping/redirecting data from one command to another.
- 0 - stdin: Stands for standard input. It takes text as input.
- 1 - stdout: Stands for standard output. The text output of a command is stored in the stdout stream.
- 2 - stderr: Stands for standard error.
Whenever a command faces an error, the error message is stored in this stream. In Linux, almost all the streams are treated as if they were files. An easy way to access any file is by using the unique file descriptor number associated with it. In the case of these streams, there are unique values assigned to each one of them.
For example, the read command requires input from the keyboard, which is the standard input device (stdin). The ls command lists the file(s) in the current directory. The list is sent to stdout and the terminal prints it out.
The POSIX systems, symbolic constants of stdin, stderr, and stdout are defined as STDIN_FILENO, STDERR_FILENO, and STDOUT_FILENO in
Redirection is a feature in Linux such that when executing a command, the standard input/output devices is changed. When redirecting the output of a command to a file, the '>' and '<' redirection operators are used. The '>' redirects the output of a command to a file instead of stdout and the '<' redirects the input to a command from a file instead of stdin. For example, if a user enters
ls > out.txt
the output from the ls command will be redirected to the file out.txt. Similarly, input can be redirected as well. For example, if the user enters
read < in.txt
the file in.txt will serve as input to the read command.
The task is to modify your shell to support the '>' and '<' redirection. Managing the redirection of both input and output will involve using the dup2() function, which duplicates an existing file descriptor to another file descriptor. For example, if fd is a file descriptor to the file out.txt, the call
dup2(fd, STDOUT_FILENO);
duplicates fd to standard output (the terminal). This means that any writes to standard output will in fact be sent to the out.txt file. You can assume that commands will contain either one input or one output redirection and will not contain both. In other words, you do not have to be concerned with command sequences such as sort < in.txt > out.txt.
- Communication via a Pipe
The final modification to your shell is to allow the output of one command to serve as input to another using a pipe. For example, the following command sequence
osh>ls -el | less
has the output of the command ls -el serve as the input to the less command. Both the ls and less commands will run as separate processes and will communicate using the UNIX pipe() function. Perhaps the easiest way to create these separate processes is to have the parent process create the child process (which will execute ls -el). This child will also create another child process (which will execute less) and will establish a pipe between itself and the child process it creates. Implementing pipe functionality will also require using the dup2() function, which duplicates an existing file descriptor to another file descriptor. Finally, although several commands can be chained together using multiple pipes, you can assume that commands will contain only one pipe character.
Assignment Description
Implement the shell interface with the following functionalities:
- Accepts user commands repeatedly until user enters "exit"
- Executes each command in a separate process
- Supports input and output redirection
- Support history feature
- Support pipes as a form of IPC between a pair of commands.
Formatting, Grading, and Submission Notes
- Your programs will be graded on both correctness and style, so include good comments, well-chosen variable names, etc. For full credit, your code must not be significantly more complicated than necessary.
- Upload and submit your shell.c file in Blackboard.
- You may submit your assignment in Blackboard as many times as you like; we will grade your latest submission.
- For every day that your assignment is late (up to 3 days), your grade reduces 10%.
Further Reading
- https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
- https://linuxhint.com/bash_stdin_stderr_stdout/
- https://en.wikipedia.org/wiki/Standard_streams
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started