本文分为两部分:
第一部分将详细分析JOS的文件系统及文件描述符的实现方法。
第二部分将实现工作路径,提供新的系统调用,完善用户空间工具。
本文中支持的新特性:

  • 支持进程工作目录 提供getcwdchdir

  • 新的syscall

    • SYS_env_set_workpath 修改工作路径
  • 新的用户程序
    • ls 功能完善
    • pwd 输出当前工作目录
    • cat 接入工作目录
    • touch 由于文件属性没啥可改的,用于创建文件
    • mkdir 创建目录文件
    • msh 更高级的shell 还未完全完工 支持cd 支持默认二进制路径为 bin
  • 调整目标磁盘生成工具

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

JOS文件系统详解

文件系统总结

  1. Regular env FS env
  2. +---------------+ +---------------+
  3. | read | | file_read |
  4. | (lib/fd.c) | | (fs/fs.c) |
  5. ...|.......|.......|...|.......^.......|...............
  6. | v | | | | RPC mechanism
  7. | devfile_read | | serve_read |
  8. | (lib/file.c) | | (fs/serv.c) |
  9. | | | | ^ |
  10. | v | | | |
  11. | fsipc | | serve |
  12. | (lib/file.c) | | (fs/serv.c) |
  13. | | | | ^ |
  14. | v | | | |
  15. | ipc_send | | ipc_recv |
  16. | | | | ^ |
  17. +-------|-------+ +-------|-------+
  18. | |
  19. +-------------------+
  • 底层与磁盘有关的丢给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 结构体

    1. struct Fd {
    2. int fd_dev_id;
    3. off_t fd_offset;
    4. int fd_omode;
    5. union {
    6. // File server files
    7. struct FdFile fd_file;
    8. };
    9. };

    其中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) 获取文件状态。

    1. struct Stat
    2. {
    3. char st_name[MAXNAMELEN];
    4. off_t st_size;
    5. int st_isdir;
    6. struct Dev *st_dev;
    7. };
  • int stat(const char *path, struct Stat *stat) 获取路径状态。

具体关于文件描述符的设计见下图。

下面就来详细看现有的三个device

文件系统读写 file.c

  • 之前已经分析过devfile_xx的函数

  • static int fsipc(unsigned type, void *dstva)用于给文件系统服务器发送IPC

  • 这里是实例化了一个用于文件读取的dev

    1. struct Dev devfile =
    2. {
    3. .dev_id = 'f',
    4. .dev_name = "file",
    5. .dev_read = devfile_read,
    6. .dev_close = devfile_flush,
    7. .dev_stat = devfile_stat,
    8. .dev_write = devfile_write,
    9. .dev_trunc = devfile_trunc
    10. };

管道 pipe.c

关于pipe

管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法,当进程创建管道时,每次都需要提供两个文件描述符来操作管道。其中一个对管道进行写操作,另一个对管道进行读操作。对管道的读写与一般的IO系统函数一致,使用write()函数写入数据,使用read()读出数据。

同刚才的file的操作类似,这里是对于pipe的操作。

  1. struct Dev devpipe =
  2. {
  3. .dev_id = 'p',
  4. .dev_name = "pipe",
  5. .dev_read = devpipe_read,
  6. .dev_write = devpipe_write,
  7. .dev_close = devpipe_close,
  8. .dev_stat = devpipe_stat,
  9. };

pipe 的结构体如下

  1. struct Pipe
  2. {
  3. off_t p_rpos; // read position
  4. off_t p_wpos; // write position
  5. uint8_t p_buf[PIPEBUFSIZ]; // data buffer
  6. };
  • 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

  1. struct Dev devcons =
  2. {
  3. .dev_id = 'c',
  4. .dev_name = "cons",
  5. .dev_read = devcons_read,
  6. .dev_write = devcons_write,
  7. .dev_close = devcons_close,
  8. .dev_stat = devcons_stat};

实现直接调用syscall即可,和之前实现的putchar类似。

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

本本分将主要关注用户空间程序,并补全内核功能(支持工作路径)。
本部分主要包括以下用户应用程序:

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

list directory contents

读文件

由于写到这里第一次在用户空间读取文件,简要记录一下读取文件的过程。

首先是文件结构,在lab5中设计文件系统的时候设计的,保存在struct File中,用户可以根据此结构体偏移来找具体的信息。

再是fsformat中提供的与文件系统相关的接口。这里用到了readn。其只是对于read的一层包装。

功能实现

回到ls本身的逻辑上。ls 主要是读取path文件,并将其下所有的文件名全部打印出来。

return working directory name

由于之前写的JOS中每个进程没有写工作目录。这里再加上工作目录。

struct env中加入工作目录,添加后env如下:

  1. struct Env {
  2. struct Trapframe env_tf; // Saved registers
  3. struct Env *env_link; // Next free Env
  4. envid_t env_id; // Unique environment identifier
  5. envid_t env_parent_id; // env_id of this env's parent
  6. enum EnvType env_type; // Indicates special system environments
  7. unsigned env_status; // Status of the environment
  8. uint32_t env_runs; // Number of times environment has run
  9. int env_cpunum; // The CPU that the env is running on
  10. // Address space
  11. pde_t *env_pgdir; // Kernel virtual address of page dir
  12. // Exception handling
  13. void *env_pgfault_upcall; // Page fault upcall entry point
  14. // IPC
  15. bool env_ipc_recving; // Env is blocked receiving
  16. void *env_ipc_dstva; // VA at which to map received page
  17. uint32_t env_ipc_value; // Data value sent to us
  18. envid_t env_ipc_from; // envid of the sender
  19. int env_ipc_perm; // Perm of page mapping received
  20. // work path
  21. char workpath[MAXPATH];
  22. };

由于env对于用户是不可以写的,所以要添加新的syscall,进入内核态改。

  1. enum {
  2. SYS_cputs = 0,
  3. SYS_cgetc,
  4. SYS_getenvid,
  5. SYS_env_destroy,
  6. SYS_page_alloc,
  7. SYS_page_map,
  8. SYS_page_unmap,
  9. SYS_exofork,
  10. SYS_env_set_status,
  11. SYS_env_set_trapframe,
  12. SYS_env_set_pgfault_upcall,
  13. SYS_yield,
  14. SYS_ipc_try_send,
  15. SYS_ipc_recv,
  16. SYS_getcwd,
  17. SYS_chdir,
  18. NSYSCALLS
  19. };

由于JOS中用户其实可以读env中的内容,所以getcwd就不陷入内核态了,直接读取就好。

新建dir.c用于存放与目录有关的函数,实现getcwd

  1. char *getcwd(char *buffer, int maxlen)
  2. {
  3. if(!buffer || maxlen < 0)
  4. return NULL;
  5. return strncpy((char *)buffer,(const char*)thisenv->workpath,maxlen);
  6. }

而对于修改目录,必须要陷入内核态了,新加syscall

  1. int sys_chdir(const char *path)
  2. {
  3. return syscall(SYS_chdir, 0, (uint32_t)path, 0, 0, 0, 0);
  4. }

刚才的dir.c中加入用户接口

  1. // change work path
  2. // Return 0 on success,
  3. // Return < 0 on error. Errors are:
  4. // -E_INVAL *path not exist or not a path
  5. int chdir(const char *path)
  6. {
  7. int r;
  8. struct Stat st;
  9. if ((r = stat(path, &st)) < 0)
  10. return r;
  11. if(!st.st_isdir)
  12. return -E_INVAL;
  13. return sys_chdir(path);
  14. }

然后去内核添加功能

  1. // change work path
  2. // return 0 on success.
  3. static int
  4. sys_chdir(const char * path)
  5. {
  6. strcpy((char *)curenv->workpath,path);
  7. return 0;
  8. }

最后实现pwd

  1. #include <inc/lib.h>
  2. void umain(int argc, char **argv)
  3. {
  4. char path[200];
  5. if(argc > 1)
  6. printf("%s : too many arguments\n",argv[0]);
  7. else
  8. printf("%s\n",getcwd(path,200));
  9. }

make directories

发现JOS给我们预留了标识位O_MKDIR,由于与普通的file_create不一样,当有同名的文件存在的时候,但其不是目录的情况下,我们仍然可以创建,所以新写了函数

  1. int dir_create(const char *path, struct File **pf)
  2. {
  3. char name[MAXNAMELEN];
  4. int r;
  5. struct File *dir, *f;
  6. if (((r = walk_path(path, &dir, &f, name)) == 0) &&
  7. f->f_type == FTYPE_DIR)
  8. return -E_FILE_EXISTS;
  9. if (r != -E_NOT_FOUND || dir == 0)
  10. return r;
  11. if ((r = dir_alloc_file(dir, &f)) < 0)
  12. return r;
  13. // fill struct file
  14. strcpy(f->f_name, name);
  15. f->f_type = FTYPE_DIR;
  16. *pf = f;
  17. file_flush(dir);
  18. return 0;
  19. }

然后在serve_open下建立新的分支

  1. // create dir
  2. else if (req->req_omode & O_MKDIR)
  3. {
  4. if ((r = dir_create(path, &f)) < 0)
  5. {
  6. if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
  7. goto try_open;
  8. if (debug)
  9. cprintf("file_create failed: %e", r);
  10. return r;
  11. }
  12. }

dir.c下提供mkdir函数

  1. // make directory
  2. // Return 0 on success,
  3. // Return < 0 on error. Errors are:
  4. // -E_FILE_EXISTS directory already exist
  5. int mkdir(const char *dirname)
  6. {
  7. char cur_path[MAXPATH];
  8. int r;
  9. getcwd(cur_path, MAXPATH);
  10. strcat(cur_path, dirname);
  11. if ((r = open(cur_path, O_MKDIR)) < 0)
  12. return r;
  13. close(r);
  14. return 0;
  15. }

最后提供用户程序

  1. #include <inc/lib.h>
  2. #define MAXPATH 200
  3. void umain(int argc, char **argv)
  4. {
  5. int r;
  6. if (argc != 2)
  7. {
  8. printf("usage: mkdir directory\n");
  9. return;
  10. }
  11. if((r = mkdir(argv[1])) < 0)
  12. printf("%s error : %e\n",argv[0],r);
  13. }

Create file

创建文件直接利用open中的O_CREAT选项即可。

  1. #include <inc/lib.h>
  2. #define MAXPATH 200
  3. void umain(int argc, char **argv)
  4. {
  5. int r;
  6. char *filename;
  7. char pathbuf[MAXPATH];
  8. if (argc != 2)
  9. {
  10. printf("usage: touch filename\n");
  11. return;
  12. }
  13. filename = argv[1];
  14. if (*filename != '/')
  15. getcwd(pathbuf, MAXPATH);
  16. strcat(pathbuf, filename);
  17. if ((r = open(pathbuf, O_CREAT)) < 0)
  18. printf("%s error : %e\n", argv[0], r);
  19. close(r);
  20. }

cat

这个只需要修改好支持工作路径即可

  1. #include <inc/lib.h>
  2. char buf[8192];
  3. void cat(int f, char *s)
  4. {
  5. long n;
  6. int r;
  7. while ((n = read(f, buf, (long)sizeof(buf))) > 0)
  8. if ((r = write(1, buf, n)) != n)
  9. panic("write error copying %s: %e", s, r);
  10. if (n < 0)
  11. panic("error reading %s: %e", s, n);
  12. }
  13. void umain(int argc, char **argv)
  14. {
  15. int f, i;
  16. char *filename;
  17. char pathbuf[MAXPATH];
  18. binaryname = "cat";
  19. if (argc == 1)
  20. cat(0, "<stdin>");
  21. else
  22. for (i = 1; i < argc; i++)
  23. {
  24. filename = argv[1];
  25. if (*filename != '/')
  26. getcwd(pathbuf, MAXPATH);
  27. strcat(pathbuf, filename);
  28. f = open(pathbuf, O_RDONLY);
  29. if (f < 0)
  30. printf("can't open %s: %e\n", argv[i], f);
  31. else
  32. {
  33. cat(f, argv[i]);
  34. close(f);
  35. }
  36. }
  37. }

SHELL

Shell的时候发现问题:之前没有解决fork以及spawn时候的子进程的工作路径的问题。所有再一次修改了系统调用,将系统调用sys_chdir修改为能够设定指定进程的工作目录的系统调用。

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

修改对应的内核处理:

  1. // change work path
  2. // return 0 on success.
  3. static int
  4. sys_env_set_workpath(envid_t envid, const char *path)
  5. {
  6. struct Env *e;
  7. int ret = envid2env(envid, &e, 1);
  8. if (ret != 0)
  9. return ret;
  10. strcpy((char *)e->workpath, path);
  11. return 0;
  12. }

这样就会fork出来的子进程继承父亲的工作路径。

shell中加入built-in功能,为未来扩展shell功能提供基础

  1. int builtin_cmd(char *cmdline)
  2. {
  3. int ret;
  4. int i;
  5. char cmd[20];
  6. for (i = 0; cmdline[i] != ' ' && cmdline[i] != '\0'; i++)
  7. cmd[i] = cmdline[i];
  8. cmd[i] = '\0';
  9. if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit"))
  10. exit();
  11. if (!strcmp(cmd, "cd"))
  12. {
  13. ret = do_cd(cmdline);
  14. return 1;
  15. }
  16. return 0;
  17. }
  18. int do_cd(char *cmdline)
  19. {
  20. char pathbuf[BUFSIZ];
  21. int r;
  22. pathbuf[0] = '\0';
  23. cmdline += 2;
  24. while (*cmdline == ' ')
  25. cmdline++;
  26. if (*cmdline == '\0')
  27. return 0;
  28. if (*cmdline != '/')
  29. {
  30. getcwd(pathbuf, BUFSIZ);
  31. }
  32. strcat(pathbuf, cmdline);
  33. if ((r = chdir(pathbuf)) < 0)
  34. printf("cd error : %e\n", r);
  35. return 0;
  36. }

修改<> 支持当前工作路径

  1. case '<': // Input redirection
  2. // Grab the filename from the argument list
  3. if (gettoken(0, &t) != 'w')
  4. {
  5. cprintf("syntax error: < not followed by word\n");
  6. exit();
  7. }
  8. // Open 't' for reading as file descriptor 0
  9. // (which environments use as standard input).
  10. // We can't open a file onto a particular descriptor,
  11. // so open the file as 'fd',
  12. // then check whether 'fd' is 0.
  13. // If not, dup 'fd' onto file descriptor 0,
  14. // then close the original 'fd'.
  15. if (t[0] != '/')
  16. getcwd(argv0buf, MAXPATH);
  17. strcat(argv0buf, t);
  18. if ((fd = open(argv0buf, O_RDONLY)) < 0)
  19. {
  20. cprintf("Error open %s fail: %e", argv0buf, fd);
  21. exit();
  22. }
  23. if (fd != 0)
  24. {
  25. dup(fd, 0);
  26. close(fd);
  27. }
  28. break;
  29. case '>': // Output redirection
  30. // Grab the filename from the argument list
  31. if (gettoken(0, &t) != 'w')
  32. {
  33. cprintf("syntax error: > not followed by word\n");
  34. exit();
  35. }
  36. if (t[0] != '/')
  37. getcwd(argv0buf, MAXPATH);
  38. strcat(argv0buf, t);
  39. if ((fd = open(argv0buf, O_WRONLY | O_CREAT | O_TRUNC)) < 0)
  40. {
  41. cprintf("open %s for write: %e", argv0buf, fd);
  42. exit();
  43. }
  44. if (fd != 1)
  45. {
  46. dup(fd, 1);
  47. close(fd);
  48. }
  49. break;

创建硬盘镜像

  • 利用mmap映射到内存,对内存读写。

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

    diskmap开始,大小为nblocks * BLKSIZE

  • alloc用于分配空间,移动diskpos

  1. void *
  2. alloc(uint32_t bytes)
  3. {
  4. void *start = diskpos;
  5. diskpos += ROUNDUP(bytes, BLKSIZE);
  6. if (blockof(diskpos) >= nblocks)
  7. panic("out of disk blocks");
  8. return start;
  9. }
  • 块 123 在初始化的时候分配

    1. alloc(BLKSIZE);
    2. super = alloc(BLKSIZE);
    3. super->s_magic = FS_MAGIC;
    4. super->s_nblocks = nblocks;
    5. super->s_root.f_type = FTYPE_DIR;
    6. strcpy(super->s_root.f_name, "/");
    7. nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
    8. bitmap = alloc(nbitblocks * BLKSIZE);
    9. memset(bitmap, 0xFF, nbitblocks * BLKSIZE);
  • writefile用于申请空间,写入磁盘

    1. void writefile(struct Dir *dir, const char *name)
    2. {
    3. int r, fd;
    4. struct File *f;
    5. struct stat st;
    6. const char *last;
    7. char *start;
    8. if ((fd = open(name, O_RDONLY)) < 0)
    9. panic("open %s: %s", name, strerror(errno));
    10. if ((r = fstat(fd, &st)) < 0)
    11. panic("stat %s: %s", name, strerror(errno));
    12. if (!S_ISREG(st.st_mode))
    13. panic("%s is not a regular file", name);
    14. if (st.st_size >= MAXFILESIZE)
    15. panic("%s too large", name);
    16. last = strrchr(name, '/');
    17. if (last)
    18. last++;
    19. else
    20. last = name;
    21. // 获取目录中的一个空位
    22. f = diradd(dir, FTYPE_REG, last);
    23. // 获取文件存放地址,分配空间
    24. start = alloc(st.st_size);
    25. // 将文件读如到磁盘中刚刚分配的地址
    26. readn(fd, start, st.st_size);
    27. // 完成文件信息
    28. finishfile(f, blockof(start), st.st_size);
    29. close(fd);
    30. }
    31. void finishfile(struct File *f, uint32_t start, uint32_t len)
    32. {
    33. int i;
    34. // 这个是刚才目录下传过来的地址,直接修改目录下的相应项
    35. f->f_size = len;
    36. len = ROUNDUP(len, BLKSIZE);
    37. for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i)
    38. f->f_direct[i] = start + i;
    39. if (i == NDIRECT)
    40. {
    41. uint32_t *ind = alloc(BLKSIZE);
    42. f->f_indirect = blockof(ind);
    43. for (; i < len / BLKSIZE; ++i)
    44. ind[i - NDIRECT] = start + i;
    45. }
    46. }
  • 目录结构体与何时将目录写入

    1. void startdir(struct File *f, struct Dir *dout)
    2. {
    3. dout->f = f;
    4. dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents);
    5. dout->n = 0;
    6. }
    7. void finishdir(struct Dir *d)
    8. {
    9. // 目录文件的大小
    10. int size = d->n * sizeof(struct File);
    11. // 申请目录文件存放空间
    12. struct File *start = alloc(size);
    13. // 将目录的文件内容放进去
    14. memmove(start, d->ents, size);
    15. // 补全目录在磁盘当中的信息
    16. finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE));
    17. free(d->ents);
    18. d->ents = NULL;
    19. }
  • 添加bin路径,并在shell中类似path环境变量默认读取bin下的可执行文件

    1. opendisk(argv[1]);
    2. startdir(&super->s_root, &root);
    3. f = diradd(&root, FTYPE_DIR, "bin");
    4. startdir(f,&bin);
    5. for (i = 3; i < argc; i++)
    6. writefile(&bin, argv[i]);
    7. finishdir(&bin);
    8. finishdir(&root);
    9. finishdisk();

获取时间

又新增一个syscall,这里不再累述,利用mc146818_read获取cmos时间即可。

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

实机运行输出

  1. check_page_free_list() succeeded!
  2. check_page_alloc() succeeded!
  3. check_page() succeeded!
  4. check_kern_pgdir() succeeded!
  5. check_page_free_list() succeeded!
  6. check_page_installed_pgdir() succeeded!
  7. ====Graph mode on====
  8. scrnx = 1024
  9. scrny = 768
  10. MMIO VRAM = 0xef803000
  11. =====================
  12. SMP: CPU 0 found 1 CPU(s)
  13. enabled interrupts: 1 2 4
  14. FS is running
  15. FS can do I/O
  16. Device 1 presence: 1
  17. block cache is good
  18. superblock is good
  19. bitmap is good
  20. # msh in / [12: 4:28]
  21. $ cd documents
  22. # msh in /documents/ [12: 4:35]
  23. $ echo hello liu > hello
  24. # msh in /documents/ [12: 4:45]
  25. $ cat hello
  26. hello liu
  27. # msh in /documents/ [12: 4:49]
  28. $ cd /bin
  29. # msh in /bin/ [12: 4:54]
  30. $ ls -l -F
  31. - 37 newmotd
  32. - 92 motd
  33. - 447 lorem
  34. - 132 script
  35. - 2916 testshell.key
  36. - 113 testshell.sh
  37. - 20308 cat
  38. - 20076 echo
  39. - 20508 ls
  40. - 20332 lsfd
  41. - 25060 sh
  42. - 20076 hello
  43. - 20276 pwd
  44. - 20276 mkdir
  45. - 20280 touch
  46. - 29208 msh
  47. # msh in /bin/ [12: 4:57]
  48. $

[自制操作系统] JOS文件系统详解&支持工作路径&MSH的更多相关文章

  1. [转帖]Linux文件系统详解

    Linux文件系统详解 https://www.cnblogs.com/alantu2018/p/8461749.html 贼复杂.. 从操作系统的角度详解Linux文件系统层次.文件系统分类.文件系 ...

  2. Java 详解 JVM 工作原理和流程

    Java 详解 JVM 工作原理和流程 作为一名Java使用者,掌握JVM的体系结构也是必须的.说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java ...

  3. iOS 后台持续定位详解(支持ISO9.0以上)

    iOS 后台持续定位详解(支持ISO9.0以上) #import <CoreLocation/CoreLocation.h>并实现CLLocationManagerDelegate 代理, ...

  4. proc文件系统详解(原创)

    Linux系统上的/proc目录是一种文件系统,即proc文件系统.与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过 ...

  5. 【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)

    1. HDFS概述 Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用 FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪 ...

  6. Linux Redhat 7.6 操作系统 下载安装详解

    redhat 系统镜像分享 [百度网盘分享] (https://pan.baidu.com/s/1ALM6v1dAtPwmEt2tmyTghg ) 提取码:2i4o redhat 7.6版本安装详解 ...

  7. Linux crontab 命令详解(含配置文件路径)

    编辑/etc/crontab 文件配置cron cron 服务每分钟不仅要读一次/var/spool/cron内的所有文件,还需要读一次/etc/crontab,因此我们配置这个文件也能运用cron服 ...

  8. ucore文件系统详解

    最近一直在mooc上学习清华大学的操作系统课程,也算是复习下基本概念和原理,为接下来的找工作做准备. 每次深入底层源码都让我深感操作系统实现的琐碎,即使像ucore这样简单的kernel也让我烦躁不已 ...

  9. FastDFS 分布式文件系统详解

    什么是文件系统 文件系统是操作系统用于在磁盘或分区上组织文件的方法和数据结构.磁盘空间是什么样的我们并不清楚,但文件系统可以给我们呈现一个非常清晰的表象,我们可以创建.删除.修改和复制这些文件,而实现 ...

随机推荐

  1. TP3.2 配置最新的阿里大于sdk

    TP3.2 配置最新的阿里大于sdk 最近公司买了阿里云的阿里大于短信验证 ,这里记录下本人接入短信验证的过程和心得. 大家是不是一开始都是和本人一样直接去百度下怎么有没有现成的demo 或者是封装好 ...

  2. c++ STL 容器——序列

    STL中11个容器类型分别是deque,list,queue,priority_queue,stack,vector,map,multimap,set,multiset,bieset(在比特级处理数据 ...

  3. 初识Java,猜字游戏

    import java.util.*; public class caizi{ public static void main(String[] args){ Scanner in=new Scann ...

  4. ArrayList 和Vector ,HashTable和HashMap异同

    相同点: 1.都实现了List接口(List接口继承自Collection接口) 2.有序集合,数据可重复,可按索引号取值(而HashSet无序,不可重复) 不同点: 1.Vector是线程安全的,而 ...

  5. oracle-使用数据泵对不同用户和不同表空间的数据迁移

    oracle-使用数据泵对不同用户和不同表空间的数据迁移 ---------------------------------------------------2013/11/13 expdp和imp ...

  6. 关于JS脚本语言的基础语法

    JS脚本语言的基础语法:输出语法  alert("警告!");  confirm("确定吗?");   prompt("请输入密码");为弱 ...

  7. Opentk教程系列-1绘制一个三角形

    本系列教程翻译自Neo Kabuto's Blog.已经取得作者授权. 本文原文地址http://neokabuto.blogspot.com/2013/02/opentk-tutorial-1-op ...

  8. javascript如何创建一个换行节点

    换行节点和其他节点的创建方式一样,使用document.createElement("br");创建节点, 并使用parentNode.appendChild()将节点插入到相应的 ...

  9. java静态内部类理解

    在Java世界里,经常被提到静态这个概念,static作为静态成员变量和成员函数的修饰符,意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见. ...

  10. .Net Framework下对Dapper二次封装迁移到.Net Core2.0遇到的问题以及对Dapper的封装介绍

    今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和大家分享一下.下面我会还原迁移的每一个过程 ...