系统调用基础&UnixShell实现
本文最后更新于一年前或更久前,其中的信息可能已经有所发展或是发生改变。

系统调用基础

实验题目

In Section 2.3, we described a program that copies the contents of one file to a destination file. This program works by first prompting the user for the name of the source and destination files. Write this program using either the Windows or POSIX API. Be sure to include all necessary error checking, including ensuring that the source file exists.

Once you have correctly designed and tested the program, if you used a system that supports it, run the program using a utility that traces system calls. Linux systems provide the strace utility, and Solaris and Mac OS X systems use the dtrace command. As Windows systems do not provide such features, you will have to trace through the Windows version of this program using a debugger.

解决方案

源代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>

int main(int argc, char ** argv)
{
    struct stat src_st;
    FILE * dst_file;
    int src_fd;
    char * buf;

    if (argc != 3)//判断cp指令的参数输入是否正确
    {
        puts("Usage: ./mycp original_file_path goal_file_path");
        exit(0);
    }

    src_fd = open(argv[1], O_RDONLY);//以Readonly模式打开一个文件进行读取
    if (src_fd == -1)
    {
        puts("Failed to open the source file!");
        exit(-1);
    }
    printf("fd of src: %d\n", src_fd);

    dst_file = fopen(argv[2], "wb+");//以wb+模式读取打开文件,以进行写入
    if (dst_file == NULL)
    {
        puts("Failed to open the destination file!");
        exit(-1);
    }

    fstat(src_fd, &src_st); //通过fstat函数读取文件内容和各个参数
    buf = (char*) malloc(sizeof(char) * src_st.st_size);//创建一个大小等于文件大小的buf读入文件
    read(src_fd, buf, src_st.st_size);

    fwrite(buf, sizeof(char), src_st.st_size, dst_file);//写入文件

    puts("Done.");
    fclose(dst_file);
    close(src_fd);
    return 0;
}

Unix Shell编写和History功能实现

实验题目

Project 1—UNIX Shell and History Feature

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. This project can be completed on any Linux, UNIX, or Mac OS X system. A shell interface gives the user a prompt, after which the next command is entered. The example below illustrates the prompt osh> and the user’s 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. This is similar in functionality to the new process creation illustrated in Figure 3.10. However, UNIX shells typically also allow the child process to run in the background, or concurrently. To accomplish this, we add an ampersand (&) at the end of the command. Thus, if we rewrite the above command as

osh> cat prog.c & the parent and child processes will run concurrently.

The separate child process is created using the fork() system call, and the user’s command is executed using one of the system calls in the exec() family (as described in Section 3.3.1).

A C program that provides the general operations of a command-line shell is supplied in Figure 3.36. 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. This project is organized into two parts: (1) creating the child process and executing the command in the child, and (2) modifying the shell to allow a history feature.

解决方案

原理

通常而言,一个shell可以简化为如下形式:

while(1)
{
    typePrompt();
    readCommand();

    int pid = fork();

    if(pid < 0)
    {
        puts("Unable to fork the child, inner error.");
    }
    else if(pid == 0) // the child thread
    {
        execve(command); //execve the command
    }
    else // the parent thread
    {
        wait(NULL); //waiting for the child to exit
    }
}

Shell输入:

当我们在shell中进行输入时,fork()出子进程来执行我们的输入,父进程则等待我们的子进程执行完成。

这说明这需要fork()和wait()对子程序进行创建和等待结束。

历史记录功能的实现:

对于输入历史是否记录,我们还需要进行判断,若是用户仅仅是在不断敲击ENTER,那么就没必要记录了

命令解析:

很显然,最为简单的解析方式便是使用strtok()函数进行分割,这里我们选择以空格” “作为分隔符

同样地,一行命令中的参数数量不应当过多,我们应当限制仅读取一定数量的参数

结果参考

源代码

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LEN     0xff
#define MAX_ARGS    0x0f
#define MAX_HISTORY 0x7f

struct history{//a struct mems history 
    int index;
    char command[MAX_HISTORY];
};


int command_cnt = 0;
int should_run = 1; // global flag for shell to run
struct history history_list[MAX_HISTORY];
int history_head = 0;
int history_len = 0;


void create_history(char* command){
    command_cnt++;
    if (history_len < MAX_HISTORY)
        history_len++;
    else
        history_head = (history_head + 1) % MAX_HISTORY;
    history_list[(history_head + history_len) % MAX_HISTORY].index = command_cnt;
    strcpy(history_list[(history_head + history_len) % MAX_HISTORY].command, command);
}

void parse_args(char* raw_command, char* command, char* args[], int *arg_cnt){
    int cnt = 0;
    if (raw_command[strlen(raw_command) - 1] == '\n')
        raw_command[strlen(raw_command) - 1] = 0;
    static char temp_command[MAX_LEN];  // !! the value may change if not set to static
    memset(temp_command, 0, sizeof(temp_command));
    strcpy(temp_command, raw_command);
    args[cnt] = strtok(temp_command, " ");
    while (args[cnt]){
         args[++cnt] = strtok(NULL, " ");
    }
    *arg_cnt = cnt;
    if (args[0])
        strcpy(command, args[0]);
    else
        command = "";
}

int run_inner_command(char* command, char* args[]){
    if (strcmp(command, "exit") == 0) {
        should_run = 0;
        printf("Bye ~ \n");
        return 0;
    }
    if (strcmp(command, "history") == 0) {
        printf("history:\nindex\tcommand\n");
        for (int i = 0; i < (command_cnt < 10 ? command_cnt : 10); i++) {
            printf(
                    "%d\t%s\n",
                    history_list[(history_head + history_len - i) % MAX_HISTORY].index,
                    history_list[(history_head + history_len - i) % MAX_HISTORY].command
            );
        }
        return 0;
    }
    //printf("args:%s\n", args[0]);
    return 1;
}

void run_command(char* command, char* args[], int args_cnt){
    int background = 0;
    if (strcmp(args[args_cnt - 1], "&") == 0){
        args[args_cnt - 1] = NULL;
        background = 1;
    }
    if (run_inner_command(command, args)){
        pid_t pid = fork();
        if (pid < 0){
            printf("Unable to fork child process!\n");
        }
        else
            if (pid == 0){
                fflush(stdout);
                if (execvp(command, args) == -1)
                    printf("Error when executing command %s\n", command);
                exit(0);
            }
            else {
                if (!background)
                    waitpid(pid, NULL, 0);
                else {
                    printf("PID %d: Child process run background.\n", pid);
                    fflush(stdout);
                }
            }
    }
}


void history_feature(char* command){
    if (history_len == 0){
        printf("No commands in history.\n");
        return;
    }
    int cnt = 0;
    if (command[1] == '!'){
        cnt = history_len;
    }
    else{
        for (int i = 1; i < strlen(command); i++){
            if (command[i] >= '0' && command[i] <= '9'){
                cnt *= 10;
                cnt += command[i] - '0';
            }
            else{
                puts("Unknown argument!");
                return;
            }
        }
    }
    if (cnt > history_len){
        printf("No such command in history.");
        return;
    }
    char* current_command = history_list[(history_head + cnt) % MAX_HISTORY].command;
    printf("history_command > %s\n", current_command);
    char* args[MAX_ARGS] = { 0 };
    char command_temp[MAX_LEN] = { 0 };
    int arg_cnt = 0;
    parse_args(current_command, command_temp, args, &arg_cnt);
    run_command(command_temp, args, arg_cnt);
}

int main(){
    while (should_run){
        printf("mysh > ");
        fflush(stdout);
        char raw_command[MAX_LEN] = { 0 };
        fgets(raw_command, MAX_LEN, stdin);
        char* args[MAX_ARGS] = { 0 };
        char command[MAX_LEN] = { 0 };
        int args_cnt = 0;
        parse_args(raw_command, command, args, &args_cnt);
        if (strlen(command) && command[0] != '!'){
            create_history(raw_command);
        }
        if (command[0] == '!' && strlen(command) > 1) {
            history_feature(command);
            continue;
        }
        if (args_cnt)
            run_command(command, args, args_cnt);
    }
    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇