《ORANGE’S:一个操作系统的实现》读书笔记(三十六)内存管理(四)

发布时间:2024年01月22日

现在我们已经有了fork()和exec(),我们的shell终于可以开始写了。这篇文章就记录如何使用fork()和exec()来实现一个简单的shell。

简单的 shell

shell可以很复杂,但目前我们只实现一种功能,那就是读取命令并执行之(如果命令存在的话)。

这个简单的shell可以这么实现:

代码 kernel/main.c,shabby_shell。

/**
 * A very very simple shell.
 * 
 * @param tty_name  TTY file name.
 */
void shabby_shell(const char * tty_name)
{
    int fd_stdin = open(tty_name, O_RDWR);
    assert(fd_stdin == 0);
    int fd_stdout = open(tty_name, O_RDWR);
    assert(fd_stdout == 1);

    char rdbuf[128];

    while (1) {
        write(fd_stdout, "$ ", 2);
        int r = read(fd_stdin, rdbuf, 70);
        rdbuf[r] = 0;

        int argc = 0;
        char * argv[PROC_ORIGIN_STACK];
        char * p = rdbuf;
        char * s;
        int word = 0;
        char ch;
        do {
            ch = *p;
            if (*p != ' ' && *p != 0 && !word) {
                s = p;
                word = 1;
            }
            if ((*p == ' ' || *p == 0) && word) {
                word = 0;
                argv[argc++] = s;
                *p = 0;
            }
            p++;
        } while(ch);
        argv[argc] = 0;

        int fd = open(argv[0], O_RDWR);
        if (fd == -1) {
            if (rdbuf[0]) {
                write(fd_stdout, "{", 1);
                write(fd_stdout, rdbuf, r);
                write(fd_stdout, "}\n", 2);
            }
        } else {
            close(fd);
            int pid = fork();
            if (pid != 0) { /* parent */
                int s;
                wait(&s);
            } else { /* child */
                execv(argv[0], argv);
            }
        }
    }

    close(fd_stdout);
    close(fd_stdin);
}

void Init()
{
    int fd_stdin = open("/dev_tty0", O_RDWR);
    assert(fd_stdin == 0);
    int fd_stdout = open("/dev_tty0", O_RDWR);
    assert(fd_stdout == 1);

    printf("Init() is running ...\n");

    /* extract 'cmd.tar' */
    untar("/cmd.tar");

    char * tty_list[] = {"/dev_tty1", "/dev_tty2"};

    int i;
    for (i = 0; i < sizeof(tty_list) / sizeof(tty_list[0]); i++) {
        int pid = fork();
        if (pid != 0) { /* parent process */
            printf("{parent is running, child pid:%d}\n", pid);
        } else { /* child process */
            printf("{child is running, pid:%d}\n", getpid());
            close(fd_stdin);
            close(fd_stdout);

            shabby_shell(tty_list[i]);
            assert(0);
        }
    }

    while (1) {
        int s;
        int child = wait(&s);
        printf("child (%d) exited with status: %d.\n", child, s);
    }

    assert(0);
}

我们用Init进程启动两个shell,分别运行在TTY1和TTY2上。两个shell都是Init进程的子进程,同时它们也将生成自己的子进程。由于它实在很简陋,所以给它起名为shabby_shell。

shabby_shell用read()读取用户输入,然后fork出一个子进程,在子进程中将输入交给execv()来执行。如果用户的输入并不是一个合法的命令,那么shabby_shell只是将命令行回显出来,不做其它任何处理。

让我们make运行一下,用一下我们的这个shabby_shell,效果如下图所示。

在左图中,我们使用了一个echo命令和一个不合法的命令。在右图中,我们还使用了pwd命令。在我们的系统中,pwd可以永远打印“/”,因为我们的文件系统是扁平的。因此pwd是个比echo还要简单的程序。从现在开始我们可以为自己的操作系统编写应用程序了,比如ls、rm等命令,都比较容易实现,有兴趣的话可以试试。

想想真是不可思议,我们居然有了自己的shell,可以执行自己的应用程序。我们的试验品已经越来越像是个能用的操作系统了。

总结

虽然书上这一章的标题叫做“内存管理”,但其实并没有涉及太多“管理”的事情,只是围绕如何通过实现fork()、exit()、wait()以及exec()等系统调用来实现一个简单的shell,并用它来执行自己的应用程序。不过尽管如此,内存管理的框架我们却已经建立起来了。不需要太多努力,我们就可以进一步实现诸如brk()这样的系统调用,从而进一步让用户进程可以使用malloc(),逐步地,内存管理就可以完善起来了。

欢迎关注我的公众号


?

公众号中对应文章附有当前文章代码下载说明。

文章来源:https://blog.csdn.net/zorro_z/article/details/135740976
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。