[自制操作系統] JOS文件系統詳解&支持工作路徑&MSH

這裏寫圖片描述
本文分爲兩部分:
第一部分將詳細分析JOS的文件系統及文件描述符的實現方法。
第二部分將實現工作路徑,提供新的系統調用,完善用戶空間工具。
本文中支持的新特性:

  • 支持進程工作目錄 提供getcwdchdir

  • 新的syscall

    • SYS_env_set_workpath 修改工作路徑
  • 新的用戶程序
    • ls 功能完善
    • pwd 輸出當前工作目錄
    • cat 接入工作目錄
    • touch 由於文件屬性沒啥可改的,用於創建文件
    • mkdir 創建目錄文件
    • msh 更高級的shell 還未完全完工 支持cd 支持默認二進制路徑爲 bin
  • 調整目標磁盤生成工具

Github:https://github.com/He11oLiu/MOS

JOS文件系統詳解

文件系統總結

      Regular env           FS env
   +---------------+   +---------------+
   |      read     |   |   file_read   |
   |   (lib/fd.c)  |   |   (fs/fs.c)   |
...|.......|.......|...|.......^.......|...............
   |       v       |   |       |       | RPC mechanism
   |  devfile_read |   |  serve_read   |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |     fsipc     |   |     serve     |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |   ipc_send    |   |   ipc_recv    |
   |       |       |   |       ^       |
   +-------|-------+   +-------|-------+
           |                   |
           +-------------------+
  • 底層與磁盤有關的丟給ide_xx來實現,因爲要用到IO中斷,要給對應的權限
  • 通過bc_pgfault來實現缺頁自己映射,利用flush_block來回寫磁盤
  • 然後通過分block利用block cache實現對於磁盤的數據讀入內存或者寫回磁盤
  • 再上面一層的file_readfile_write,均是對於blk的操作。
  • 再上面就是文件系統服務器,通過調用file_read實現功能了。
  • 客戶端通過對需求打包,發送IPC給文件系統服務器,即可實現讀/寫文件的功能。

文件系統&文件描述符 Overview

JOS文件系統是直接映射到內存空間DISKMAPDISKMAP + DISKSIZE這塊空間。故其支持的文件系統最大爲3GB.

IDE ide.c

文件系統底層PIO驅動放在ide.c中。注意在IDE中,是以硬件的角度來看待硬盤,其基本單位是sector,不是block

  • bool ide_probe_disk1(void) 用於檢測disk1是否存在。
  • voidide_set_disk(int diskno) 用於設置目標磁盤。
  • ide_read ide_write 用於磁盤讀寫。

block cache bc.c

文件系統在內存中的映射是基於block cache的。以一個block爲單位在內存中爲其分配單元。注意在bc中,是以操作系統的角度來看待硬盤,其基本單位是block,不是sector

  • void *diskaddr(uint32_t blockno) 用於查找blockno在地址空間中的地址。

  • blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE 用於查找addr對應文件系統中的blockno

  • static void bc_pgfault(struct UTrapframe *utf) 用於處理讀取不在內存中而出現page fault的情況。這時需要從file system通過PIO讀取到block cache(也就是內存中新分配的一頁)中,並做好映射。

  • void flush_block(void *addr) 用於寫回硬盤,寫回時清理PTE_D標記。

file system fs.c

文件系統是基於剛纔的block cache和底層ide驅動的。

bitmap 相關

bitmap每一位代表着一個block的狀態,用位操作檢查/設置block狀態即可。

  • bool block_is_free(uint32_t blockno) 用於check給定的blockno是否是空閒的。

  • void free_block(uint32_t blockno) 設置對應位爲0

  • int alloc_block(void) 設置對應位爲1

文件系統操作

  • void fs_init(void) 初始化文件系統。檢測disk1是否存在,檢測super blockbitmap block

  • static int file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc) 用於找到文件ffilenoblockblocknoalloc用於控制當f_indirect不存在的時候,是否需要新申請一個block

  • int file_get_block(struct File *f, uint32_t filebno, char **blk) 用於找到文件ffilenoblock的地址。

  • static int dir_lookup(struct File *dir, const char *name, struct File **file) 用於在dir下查找name這個文件。其遍歷讀取dir這個文件,並逐個判斷其目錄下每一個文件的名字是否相等。

  • static int dir_alloc_file(struct File *dir, struct File **file)dir下新申請一個file。同樣也是遍歷所有的dir下的文件。找到第一個名字爲空的文件,並把新的文件存在這裏。

  • static int walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem) 用於從根目錄獲取path的文件,文件放在pf中,路徑放在pdir中。如果找到了路徑沒有找到文件。最後的路徑名放在lastelem中,最後的路徑放在pdir中。

文件操作

  • int file_create(const char *path, struct File **pf) 用於創建文件。

  • int file_open(const char *path, struct File **pf) 打開文件。

  • ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset)foffset讀取countbytes的數據放入buf中。

  • int file_write(struct File *f, const void *buf, size_t count, off_t offset) 與上面的類似。

  • static int file_free_block(struct File *f, uint32_t filebno) 刪除文件中的filebno

  • static void file_truncate_blocks(struct File *f, off_t newsize) 縮短文件大小。

  • int file_set_size(struct File *f, off_t newsize) 修改文件大小。

  • void file_flush(struct File *f) 將文件寫回硬盤

  • void fs_sync(void) 將所有的文件寫回硬盤

文件系統服務器 serv.c

  • 服務器主要邏輯umain: 初始化文件系統,初始化服務器,開始接收請求。

  • 服務器具體函數見上面實現。

  • int openfile_alloc(struct OpenFile **o)用於服務器分配一個openfile結構體

文件描述符 fd.c

  • struct fd 結構體

    struct Fd {
    int fd_dev_id;
    off_t fd_offset;
    int fd_omode;
    union {
        // File server files
        struct FdFile fd_file;
    };
    };

    其中fd_file用於發送的時候傳入服務器對應的fileid

    包括了fd_id 文件讀取的offset,讀取模式以及FdFile

  • int fd2num(struct Fd *fd)fd獲取其編號

  • char* fd2data(struct Fd *fd)fd獲取文件內容

  • int fd_alloc(struct Fd **fd_store) 查找到第一個空閒的fd,並分配出去。

  • int fd_lookup(int fdnum, struct Fd **fd_store) 爲查找fdnum的fd,並放在fd_store中。

  • int fd_close(struct Fd *fd, bool must_exist) 用於關閉並free一個fd

  • int dev_lookup(int dev_id, struct Dev **dev) 獲取不同的Device

  • int close(int fdnum) 關閉fd

  • void close_all(void) 關閉全部

  • int dup(int oldfdnum, int newfdnum) dup不是簡單的複製,而是要將兩個fd的內容完全同步,其是通過虛擬內存映射做到的。

  • read(int fdnum, void *buf, size_t n) 後面的與這個類似

    • 獲取fdfd_dev_id並根據其獲取dev
    • 調用dev對應的function
  • int seek(int fdnum, off_t offset) 用於設置fdoffset

  • int fstat(int fdnum, struct Stat *stat) 獲取文件狀態。

    struct Stat
    {
    char st_name[MAXNAMELEN];
    off_t st_size;
    int st_isdir;
    struct Dev *st_dev;
    };
  • int stat(const char *path, struct Stat *stat) 獲取路徑狀態。

具體關於文件描述符的設計見下圖。

這裏寫圖片描述

下面就來詳細看現有的三個device

文件系統讀寫 file.c

  • 之前已經分析過devfile_xx的函數

  • static int fsipc(unsigned type, void *dstva)用於給文件系統服務器發送IPC

  • 這裏是實例化了一個用於文件讀取的dev

    struct Dev devfile =
    {
    .dev_id = 'f',
    .dev_name = "file",
    .dev_read = devfile_read,
    .dev_close = devfile_flush,
    .dev_stat = devfile_stat,
    .dev_write = devfile_write,
    .dev_trunc = devfile_trunc
    };

管道 pipe.c

關於pipe

管道是一種把兩個進程之間的標準輸入和標準輸出連接起來的機制,從而提供一種讓多個進程間通信的方法,當進程創建管道時,每次都需要提供兩個文件描述符來操作管道。其中一個對管道進行寫操作,另一個對管道進行讀操作。對管道的讀寫與一般的IO系統函數一致,使用write()函數寫入數據,使用read()讀出數據。

同剛纔的file的操作類似,這裏是對於pipe的操作。

struct Dev devpipe =
    {
        .dev_id = 'p',
        .dev_name = "pipe",
        .dev_read = devpipe_read,
        .dev_write = devpipe_write,
        .dev_close = devpipe_close,
        .dev_stat = devpipe_stat,
};

pipe 的結構體如下

struct Pipe
{
    off_t p_rpos;              // read position
    off_t p_wpos;              // write position
    uint8_t p_buf[PIPEBUFSIZ]; // data buffer
};
  • int pipe(int pfd[2]) 申請兩個新的fd,映射到同一個虛擬地址上,一邊Read_only 一邊Write_only即可。

  • static ssize_t devpipe_read(struct Fd *fd, void *vbuf, size_t n) 其從fd對應的data獲取pipep = (struct Pipe *)fd2data(fd);然後從pipe->buf中讀取內容。維護p_rpos

  • static ssize_t devpipe_write(struct Fd *fd, const void *vbuf, size_t n) 其從fd對應的data獲取pipep = (struct Pipe *)fd2data(fd);然後向pipe->buf中寫入內容。維護p_wpos

屏幕輸入輸出 console.c

struct Dev devcons =
    {
        .dev_id = 'c',
        .dev_name = "cons",
        .dev_read = devcons_read,
        .dev_write = devcons_write,
        .dev_close = devcons_close,
        .dev_stat = devcons_stat};

實現直接調用syscall即可,和之前實現的putchar類似。

支持工作路徑以及更完整的工具

本本分將主要關注用戶空間程序,並補全內核功能(支持工作路徑)。
本部分主要包括以下用戶應用程序:

ls      list directory contents
pwd     return working directory name
mkdir   make directories
touch   change file access and modification times(we only support create file)
cat     concatenate and print files
shell

list directory contents

讀文件

由於寫到這裏第一次在用戶空間讀取文件,簡要記錄一下讀取文件的過程。

首先是文件結構,在lab5中設計文件系統的時候設計的,保存在struct File中,用戶可以根據此結構體偏移來找具體的信息。

再是fsformat中提供的與文件系統相關的接口。這裏用到了readn。其只是對於read的一層包裝。

功能實現

回到ls本身的邏輯上。ls 主要是讀取path文件,並將其下所有的文件名全部打印出來。

return working directory name

由於之前寫的JOS中每個進程沒有寫工作目錄。這裏再加上工作目錄。

struct env中加入工作目錄,添加後env如下:

struct Env {
    struct Trapframe env_tf;    // Saved registers
    struct Env *env_link;       // Next free Env
    envid_t env_id;         // Unique environment identifier
    envid_t env_parent_id;      // env_id of this env's parent
    enum EnvType env_type;      // Indicates special system environments
    unsigned env_status;        // Status of the environment
    uint32_t env_runs;      // Number of times environment has run
    int env_cpunum;         // The CPU that the env is running on

    // Address space
    pde_t *env_pgdir;       // Kernel virtual address of page dir

    // Exception handling
    void *env_pgfault_upcall;   // Page fault upcall entry point

    // IPC
    bool env_ipc_recving;       // Env is blocked receiving
    void *env_ipc_dstva;        // VA at which to map received page
    uint32_t env_ipc_value;     // Data value sent to us
    envid_t env_ipc_from;       // envid of the sender
    int env_ipc_perm;       // Perm of page mapping received

    // work path
    char workpath[MAXPATH];
};

由於env對於用戶是不可以寫的,所以要添加新的syscall,進入內核態改。

enum {
    SYS_cputs = 0,
    SYS_cgetc,
    SYS_getenvid,
    SYS_env_destroy,
    SYS_page_alloc,
    SYS_page_map,
    SYS_page_unmap,
    SYS_exofork,
    SYS_env_set_status,
    SYS_env_set_trapframe,
    SYS_env_set_pgfault_upcall,
    SYS_yield,
    SYS_ipc_try_send,
    SYS_ipc_recv,
    SYS_getcwd,
    SYS_chdir,
    NSYSCALLS
};

由於JOS中用戶其實可以讀env中的內容,所以getcwd就不陷入內核態了,直接讀取就好。

新建dir.c用於存放與目錄有關的函數,實現getcwd

char *getcwd(char *buffer, int maxlen)
{
    if(!buffer || maxlen < 0)
        return NULL;
    return strncpy((char *)buffer,(const char*)thisenv->workpath,maxlen);
}

而對於修改目錄,必須要陷入內核態了,新加syscall

int sys_chdir(const char *path)
{
    return syscall(SYS_chdir, 0, (uint32_t)path, 0, 0, 0, 0);
}

剛纔的dir.c中加入用戶接口

// change work path
// Return 0 on success, 
// Return < 0 on error. Errors are:
//  -E_INVAL *path not exist or not a path
int chdir(const char *path)
{
    int r;
    struct Stat st;
    if ((r = stat(path, &st)) < 0)
        return r;
    if(!st.st_isdir)
        return -E_INVAL;
    return sys_chdir(path);
}

然後去內核添加功能

// change work path
// return 0 on success.
static int
sys_chdir(const char * path)
{
    strcpy((char *)curenv->workpath,path);
    return 0;
}

最後實現pwd

#include <inc/lib.h>

void umain(int argc, char **argv)
{
    char path[200];
    if(argc > 1)
        printf("%s : too many arguments\n",argv[0]);
    else
        printf("%s\n",getcwd(path,200));
}

make directories

發現JOS給我們預留了標識位O_MKDIR,由於與普通的file_create不一樣,當有同名的文件存在的時候,但其不是目錄的情況下,我們仍然可以創建,所以新寫了函數

int dir_create(const char *path, struct File **pf)
{
    char name[MAXNAMELEN];
    int r;
    struct File *dir, *f;

    if (((r = walk_path(path, &dir, &f, name)) == 0) &&
        f->f_type == FTYPE_DIR)
        return -E_FILE_EXISTS;
    if (r != -E_NOT_FOUND || dir == 0)
        return r;
    if ((r = dir_alloc_file(dir, &f)) < 0)
        return r;

    // fill struct file
    strcpy(f->f_name, name);
    f->f_type = FTYPE_DIR;

    *pf = f;
    file_flush(dir);
    return 0;
}

然後在serve_open下建立新的分支

    // create dir
    else if (req->req_omode & O_MKDIR)
    {
        if ((r = dir_create(path, &f)) < 0)
        {
            if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
                goto try_open;
            if (debug)
                cprintf("file_create failed: %e", r);
            return r;
        }
    }

dir.c下提供mkdir函數

// make directory
// Return 0 on success,
// Return < 0 on error. Errors are:
//  -E_FILE_EXISTS directory already exist
int mkdir(const char *dirname)
{
    char cur_path[MAXPATH];
    int r;
    getcwd(cur_path, MAXPATH);
    strcat(cur_path, dirname);
    if ((r = open(cur_path, O_MKDIR)) < 0)
        return r;
    close(r);
    return 0;
}

最後提供用戶程序

#include <inc/lib.h>
#define MAXPATH 200

void umain(int argc, char **argv)
{
    int r;

    if (argc != 2)
    {
        printf("usage: mkdir directory\n");
        return;
    }
    if((r = mkdir(argv[1])) < 0)
        printf("%s error : %e\n",argv[0],r);
}

Create file

創建文件直接利用open中的O_CREAT選項即可。

#include <inc/lib.h>
#define MAXPATH 200

void umain(int argc, char **argv)
{
    int r;
    char *filename;
    char pathbuf[MAXPATH];
    if (argc != 2)
    {
        printf("usage: touch filename\n");
        return;
    }
    filename = argv[1];
    if (*filename != '/')
        getcwd(pathbuf, MAXPATH);
    strcat(pathbuf, filename);
    if ((r = open(pathbuf, O_CREAT)) < 0)
        printf("%s error : %e\n", argv[0], r);
    close(r);
}

cat

這個只需要修改好支持工作路徑即可

#include <inc/lib.h>

char buf[8192];

void cat(int f, char *s)
{
    long n;
    int r;

    while ((n = read(f, buf, (long)sizeof(buf))) > 0)
        if ((r = write(1, buf, n)) != n)
            panic("write error copying %s: %e", s, r);
    if (n < 0)
        panic("error reading %s: %e", s, n);
}

void umain(int argc, char **argv)
{
    int f, i;
    char *filename;
    char pathbuf[MAXPATH];

    binaryname = "cat";
    if (argc == 1)
        cat(0, "<stdin>");
    else
        for (i = 1; i < argc; i++)
        {
            filename = argv[1];
            if (*filename != '/')
                getcwd(pathbuf, MAXPATH);
            strcat(pathbuf, filename);
            f = open(pathbuf, O_RDONLY);
            if (f < 0)
                printf("can't open %s: %e\n", argv[i], f);
            else
            {
                cat(f, argv[i]);
                close(f);
            }
        }
}

SHELL

Shell的時候發現問題:之前沒有解決fork以及spawn時候的子進程的工作路徑的問題。所有再一次修改了系統調用,將系統調用sys_chdir修改爲能夠設定指定進程的工作目錄的系統調用。

int sys_env_set_workpath(envid_t envid, const char *path);

修改對應的內核處理:

// change work path
// return 0 on success.
static int
sys_env_set_workpath(envid_t envid, const char *path)
{
    struct Env *e;
    int ret = envid2env(envid, &e, 1);
    if (ret != 0)
        return ret;
    strcpy((char *)e->workpath, path);
    return 0;
}

這樣就會fork出來的子進程繼承父親的工作路徑。

shell中加入built-in功能,爲未來擴展shell功能提供基礎

int builtin_cmd(char *cmdline)
{
    int ret;
    int i;
    char cmd[20];
    for (i = 0; cmdline[i] != ' ' && cmdline[i] != '\0'; i++)
        cmd[i] = cmdline[i];
    cmd[i] = '\0';
    if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit"))
        exit();
    if (!strcmp(cmd, "cd"))
    {
        ret = do_cd(cmdline);
        return 1;
    }
    return 0;
}

int do_cd(char *cmdline)
{
    char pathbuf[BUFSIZ];
    int r;
    pathbuf[0] = '\0';
    cmdline += 2;
    while (*cmdline == ' ')
        cmdline++;
    if (*cmdline == '\0')
        return 0;
    if (*cmdline != '/')
    {
        getcwd(pathbuf, BUFSIZ);
    }
    strcat(pathbuf, cmdline);
    if ((r = chdir(pathbuf)) < 0)
        printf("cd error : %e\n", r);
    return 0;
}

修改<> 支持當前工作路徑

case '<': // Input redirection
            // Grab the filename from the argument list
            if (gettoken(0, &t) != 'w')
            {
                cprintf("syntax error: < not followed by word\n");
                exit();
            }
            // Open 't' for reading as file descriptor 0
            // (which environments use as standard input).
            // We can't open a file onto a particular descriptor,
            // so open the file as 'fd',
            // then check whether 'fd' is 0.
            // If not, dup 'fd' onto file descriptor 0,
            // then close the original 'fd'.

            if (t[0] != '/')
                getcwd(argv0buf, MAXPATH);
            strcat(argv0buf, t);
            if ((fd = open(argv0buf, O_RDONLY)) < 0)
            {
                cprintf("Error open %s fail: %e", argv0buf, fd);
                exit();
            }
            if (fd != 0)
            {
                dup(fd, 0);
                close(fd);
            }
            break;

        case '>': // Output redirection
            // Grab the filename from the argument list
            if (gettoken(0, &t) != 'w')
            {
                cprintf("syntax error: > not followed by word\n");
                exit();
            }
            if (t[0] != '/')
                getcwd(argv0buf, MAXPATH);
            strcat(argv0buf, t);
            if ((fd = open(argv0buf, O_WRONLY | O_CREAT | O_TRUNC)) < 0)
            {
                cprintf("open %s for write: %e", argv0buf, fd);
                exit();
            }
            if (fd != 1)
            {
                dup(fd, 1);
                close(fd);
            }
            break;

創建硬盤鏡像

  • 利用mmap映射到內存,對內存讀寫。

    if ((diskmap = mmap(NULL, nblocks * BLKSIZE, PROT_READ | PROT_WRITE,
                        MAP_SHARED, diskfd, 0)) == MAP_FAILED)
        panic("mmap %s: %s", name, strerror(errno));

    diskmap開始,大小爲nblocks * BLKSIZE

  • alloc用於分配空間,移動diskpos

void *
alloc(uint32_t bytes)
{
    void *start = diskpos;
    diskpos += ROUNDUP(bytes, BLKSIZE);
    if (blockof(diskpos) >= nblocks)
        panic("out of disk blocks");
    return start;
}
  • 塊 123 在初始化的時候分配

    alloc(BLKSIZE);
    super = alloc(BLKSIZE);
    super->s_magic = FS_MAGIC;
    super->s_nblocks = nblocks;
    super->s_root.f_type = FTYPE_DIR;
    strcpy(super->s_root.f_name, "/");
    
    nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
    bitmap = alloc(nbitblocks * BLKSIZE);
    memset(bitmap, 0xFF, nbitblocks * BLKSIZE);
  • writefile用於申請空間,寫入磁盤

    void writefile(struct Dir *dir, const char *name)
    {
    int r, fd;
    struct File *f;
    struct stat st;
    const char *last;
    char *start;
    
    if ((fd = open(name, O_RDONLY)) < 0)
        panic("open %s: %s", name, strerror(errno));
    if ((r = fstat(fd, &st)) < 0)
        panic("stat %s: %s", name, strerror(errno));
    if (!S_ISREG(st.st_mode))
        panic("%s is not a regular file", name);
    if (st.st_size >= MAXFILESIZE)
        panic("%s too large", name);
    
    last = strrchr(name, '/');
    if (last)
        last++;
    else
        last = name;
    
    // 獲取目錄中的一個空位
    f = diradd(dir, FTYPE_REG, last);
    // 獲取文件存放地址,分配空間
    start = alloc(st.st_size);
    // 將文件讀如到磁盤中剛剛分配的地址
    readn(fd, start, st.st_size);
    // 完成文件信息
    finishfile(f, blockof(start), st.st_size);
    close(fd);
    }
    
    void finishfile(struct File *f, uint32_t start, uint32_t len)
    {
    int i;
    // 這個是剛纔目錄下傳過來的地址,直接修改目錄下的相應項
    f->f_size = len;
    len = ROUNDUP(len, BLKSIZE);
    for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i)
        f->f_direct[i] = start + i;
    if (i == NDIRECT)
    {
        uint32_t *ind = alloc(BLKSIZE);
        f->f_indirect = blockof(ind);
        for (; i < len / BLKSIZE; ++i)
            ind[i - NDIRECT] = start + i;
    }
    }
  • 目錄結構體與何時將目錄寫入

    void startdir(struct File *f, struct Dir *dout)
    {
    dout->f = f;
    dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents);
    dout->n = 0;
    }
    
    void finishdir(struct Dir *d)
    {
    // 目錄文件的大小
    int size = d->n * sizeof(struct File);
    // 申請目錄文件存放空間
    struct File *start = alloc(size);
        // 將目錄的文件內容放進去
    memmove(start, d->ents, size);
      // 補全目錄在磁盤當中的信息
    finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE));
    free(d->ents);
    d->ents = NULL;
    }
  • 添加bin路徑,並在shell中類似path環境變量默認讀取bin下的可執行文件

    opendisk(argv[1]);
    
    startdir(&super->s_root, &root);
    f = diradd(&root, FTYPE_DIR, "bin");
    startdir(f,&bin);
    for (i = 3; i < argc; i++)
        writefile(&bin, argv[i]);
    finishdir(&bin);
    finishdir(&root);
    
    finishdisk();

獲取時間

又新增一個syscall,這裏不再累述,利用mc146818_read獲取cmos時間即可。

int gettime(struct tm *tm)
{
    unsigned datas, datam, datah;
    int i;
    tm->tm_sec = BCD_TO_BIN(mc146818_read(0));
    tm->tm_min = BCD_TO_BIN(mc146818_read(2));
    tm->tm_hour = BCD_TO_BIN(mc146818_read(4)) + TIMEZONE;
    tm->tm_wday = BCD_TO_BIN(mc146818_read(6));
    tm->tm_mday = BCD_TO_BIN(mc146818_read(7));
    tm->tm_mon = BCD_TO_BIN(mc146818_read(8));
    tm->tm_year = BCD_TO_BIN(mc146818_read(9));
    return 0;
}

實機運行輸出

check_page_free_list() succeeded!
check_page_alloc() succeeded!
check_page() succeeded!
check_kern_pgdir() succeeded!
check_page_free_list() succeeded!
check_page_installed_pgdir() succeeded!
====Graph mode on====
   scrnx = 1024
   scrny = 768
MMIO VRAM = 0xef803000
=====================
SMP: CPU 0 found 1 CPU(s)
enabled interrupts: 1 2 4
FS is running
FS can do I/O
Device 1 presence: 1
block cache is good
superblock is good
bitmap is good

# msh in / [12: 4:28]
$ cd documents

# msh in /documents/ [12: 4:35]
$ echo hello liu > hello

# msh in /documents/ [12: 4:45]
$ cat hello
hello liu

# msh in /documents/ [12: 4:49]
$ cd /bin

# msh in /bin/ [12: 4:54]
$ ls -l -F
-          37 newmotd
-          92 motd
-         447 lorem
-         132 script
-        2916 testshell.key
-         113 testshell.sh
-       20308 cat
-       20076 echo
-       20508 ls
-       20332 lsfd
-       25060 sh
-       20076 hello
-       20276 pwd
-       20276 mkdir
-       20280 touch
-       29208 msh

# msh in /bin/ [12: 4:57]
$ 
發佈了74 篇原創文章 · 獲贊 366 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章