Operating system second experiment
Topic: Operating system concept test project 2: C program to set up shell interface.
Requirements:
- Master the principles and implementation methods of operating system user command interfaces. Ability to write simple command interpreters.
- Master command input, command execution and result output through keyboard/monitor input/output devices.
- Understand computer management capabilities for input/output devices.
Procedure:
The task of this project is to design a C program that acts as a shell interface, accepts user commands, and then executes each command in a separate process. The Shell interface gives the user a prompt and then enters the next command.
This experiment can be divided into two parts:
(1) Create a subprocess and execute commands in the subprocess
(2) Modification shell to add history functionality.
Experimental steps:
1. Part 1: Create a child process and execute the command
- Modify the main() function, use the fork() system call to create a child process, and execute user-specified commands.
- Parses user-entered commands, splits the commands into individual tokens, and stores these tokens in a character string array.
- Use the execvp() function to execute user commands, passing the command and parameters to the execvp() function.
- Determine whether the parent process waits for the child process to exit based on whether the user adds the & symbol at the end of the command.
2. Part 2: Adding history function
- Modify the shell interface program and add a history function to allow users to access recently entered commands.
- Use a command history buffer to store commands entered by the user.
- Implements two techniques for retrieving commands from the command history:
- When the user enters !!, execute the most recent command.
- When the user enters a single ! followed by an integer N, execute the Nth command in the history.
- After the command is executed, the command is added to the history buffer and displayed on the user's screen.
3. Experimental design and implementation
Design ideas:
- In the first part, we need to create a child process using the fork() system call and execute user commands using the execvp() function.
- In the second part, we need to use a command history buffer to store commands entered by the user and implement two techniques for retrieving commands from the history.
4. Code implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LINE 80 /* The maximum length command */
#define MAX_HISTORY 10 /* The maximum number of commands in history */
char* history[MAX_HISTORY]; /* Array to store command history */
int history_count = 0; /* Counter for command history */
void add_to_history(char* command) {
if (history_count < MAX_HISTORY) {
history[history_count] = strdup(command); // 将命令复制到历史记录数组中
history_count++;
} else {
free(history[0]);
for (int i = 0; i < MAX_HISTORY - 1; i++) {
history[i] = history[i + 1];
}
history[MAX_HISTORY - 1] = strdup(command);
}
}
void print_history() {
for (int i = history_count - 1; i >= 0; i--) {
printf("%d %s\n", history_count - i, history[i]); // 打印历史记录
}
}
int main(void) {
char *args[MAX_LINE/2 + 1]; /* command line arguments */
int should_run = 1; /* flag to determine when to exit program */
while (should_run) {
printf("osh> ");
fflush(stdout);
char command[MAX_LINE];
fgets(command, sizeof(command), stdin);
command[strcspn(command, "\n")] = '\0'; /* Remove trailing newline character */
if (strcmp(command, "exit") == 0) {
should_run = 0;
continue;
}
if (strcmp(command, "history") == 0) {
print_history();
continue;
}
if (strcmp(command, "!!") == 0) {
if (history_count == 0) {
printf("No commands in history.\n");
continue;
}
strcpy(command, history[history_count - 1]); // 执行最近的命令
}
if (command[0] == '!') {
int index = atoi(&command[1]);
if (index > 0 && index <= history_count) {
strcpy(command, history[history_count - index]); // 执行历史记录中的第N个命令
} else {
printf("No such command in history.\n");
continue;
}
}
add_to_history(command); // 将命令添加到历史记录中
int background = 0;
if (command[strlen(command) - 1] == '&') {
background = 1;
command[strlen(command) - 1] = '\0';
}
char *token = strtok(command, " ");
int i = 0;
while (token != NULL) {
args[i] = token;
token = strtok(NULL, " ");
i++;
}
args[i] = NULL;
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed.\n");
return 1;
} else if (pid == 0) {
execvp(args[0], args); // 在子进程中执行命令
fprintf(stderr, "Command execution failed.\n");
return 1;
} else {
if (!background) {
wait(NULL); // 等待子进程结束
}
}
}
return 0;
}
5. Experimental results
This experimental environment is Ubuntu Linux and termius (SSH remote control client).
First, we write the key code into our simple.c file. You can use the following command:
touch simple.c(创建simple.c的文件)
vim simple.c(编辑simple.c的内容,我们可以把代码复制到文件当中)
最后退出保存(:wq)
Use gcc simple.c to compile the file. If there are no warnings or errors, our key code will have no syntax errors.
After we compile it with the gcc command, a shell executable file will be generated:
After completing the task requirements, we need to test it. We run this executable file in shell programming mode in the terminal. Enter the following command:
./a.out
During the experiment running process, we can test the experimental results through the following steps:
- Enter the command in the shell interface and observe whether the command is executed correctly.
-
Use the "history" command to view command history and verify the execution results of recent commands and specified commands.
-
Enter the "!!" command to verify that the most recent command was executed.
-
Enter the "!N" command, where N is an integer, to verify whether the Nth command in the history is executed.
Experimental results:
Through this experiment, I mastered the principles and implementation methods of the operating system user command interface, and successfully designed a simple shell interface to implement command execution and history recording functions.