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

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

  • 支持進程工做目錄 提供getcwdchdirgithub

  • 新的syscallshell

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

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

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,不是blockmarkdown

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

block cache bc.c

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

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

  • 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]
$
相關文章
相關標籤/搜索