Operating system second experiment

Operating system second experiment

Topic: Operating system concept test project 2: C program to set up shell interface.
Requirements:
  1. Master the principles and implementation methods of operating system user command interfaces. Ability to write simple command interpreters.
  2. Master command input, command execution and result output through keyboard/monitor input/output devices.
  3. 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
  1. Modify the main() function, use the fork() system call to create a child process, and execute user-specified commands.
  2. Parses user-entered commands, splits the commands into individual tokens, and stores these tokens in a character string array.
  3. Use the execvp() function to execute user commands, passing the command and parameters to the execvp() function.
  4. 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
  1. Modify the shell interface program and add a history function to allow users to access recently entered commands.
  2. Use a command history buffer to store commands entered by the user.
  3. Implements two techniques for retrieving commands from the command history:
  4. When the user enters !!, execute the most recent command.
  5. When the user enters a single ! followed by an integer N, execute the Nth command in the history.
  6. 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:
  1. In the first part, we need to create a child process using the fork() system call and execute user commands using the execvp() function.
  2. 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)

Insert image description here

Use gcc simple.c to compile the file. If there are no warnings or errors, our key code will have no syntax errors.
Insert image description here

After we compile it with the gcc command, a shell executable file will be generated:
Insert image description here
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

Insert image description here
During the experiment running process, we can test the experimental results through the following steps:

  1. Enter the command in the shell interface and observe whether the command is executed correctly.

Insert image description here

  1. Use the "history" command to view command history and verify the execution results of recent commands and specified commands.
    Insert image description here

  2. Enter the "!!" command to verify that the most recent command was executed.
    Insert image description here

  3. Enter the "!N" command, where N is an integer, to verify whether the Nth command in the history is executed.

Insert image description here


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.


Guess you like

Origin blog.csdn.net/m0_53157282/article/details/134044264