操作系统第二次实验

操作系统第二次实验

Topic:操作系统概念验项目2:设置shell接口的C程序。
Requirements:
  1. 掌握操作系统用户命令界面的原理和实现方法。具备编写简单命令解释器的能力。
  2. 掌握通过键盘/显示器输入/输出设备进行命令输入、命令执行和结果输出。
  3. 了解输入/输出设备的计算机管理能力。
Procedure:

本项目的任务是设计一个C程序,作为一个shell接口,接受用户命令,然后在单独的进程中执行每个命令。Shell接口给用户一个提示符,然后输入下一个命令。

本次实验可以分为两个部分:
(1)创建子进程并在子进程中执行命令
(2)修改shell以添加历史功能。

实验步骤:

1. 第一部分:创建子进程并执行命令
  1. 修改main()函数,使用fork()系统调用创建一个子进程,并执行用户指定的命令。
  2. 解析用户输入的命令,将命令分割成单独的标记,并将这些标记存储在字符字符串数组中。
  3. 使用execvp()函数执行用户命令,将命令和参数传递给execvp()函数。
  4. 根据用户是否在命令末尾添加了&符号来确定父进程是否等待子进程退出。
2. 第二部分:添加历史功能
  1. 修改shell界面程序,添加一个历史功能,允许用户访问最近输入的命令。
  2. 使用一个命令历史缓冲区来存储用户输入的命令。
  3. 实现两种从命令历史中检索命令的技术:
  4. 当用户输入!!时,执行最近的命令。
  5. 当用户输入单个!后跟一个整数N时,执行历史记录中第N个命令。
  6. 在命令执行后,将该命令添加到历史缓冲区中,并显示在用户的屏幕上。
3. 实验设计与实现
设计思路:
  1. 在第一部分中,我们需要使用fork()系统调用创建一个子进程,并使用execvp()函数执行用户命令。
  2. 在第二部分中,我们需要使用一个命令历史缓冲区来存储用户输入的命令,并实现两种从历史记录中检索命令的技术。
4. 代码实现
#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. 实验结果

本次实验环境是Ubuntu Linux和termius(SSH远程控制客户端)。
首先我们把关键代码写入我们的simple.c的文件当中,可以用一下命令:

touch simple.c(创建simple.c的文件)
vim simple.c(编辑simple.c的内容,我们可以把代码复制到文件当中)
最后退出保存(:wq)

在这里插入图片描述

用 gcc simple.c来编译文件,如果没有警告或报错我们的关键代码没有语法错误。
在这里插入图片描述

我们用gcc命令编译后会生成一个shell可执行文件:
在这里插入图片描述
完成任务要求后要进行测试,我们以shell编程模式运行这个可执行文件,在终端中输入一下命令:

./a.out

在这里插入图片描述
在实验运行过程中,我们可以通过以下步骤测试实验结果:

  1. 在shell界面中输入命令,并观察命令是否被正确执行。

在这里插入图片描述

  1. 使用"history"命令查看命令历史记录,并验证最近命令和指定命令的执行结果。
    在这里插入图片描述

  2. 输入"!!"命令,验证是否执行最近的命令。
    在这里插入图片描述

  3. 输入"!N"命令,其中N是一个整数,验证是否执行历史记录中第N个命令。

在这里插入图片描述


实验结论:

通过本实验,掌握了操作系统用户命令界面的原理和实现方法,并成功设计了一个简单的shell界面,实现了命令的执行和历史记录功能。


猜你喜欢

转载自blog.csdn.net/m0_53157282/article/details/134044264