操作系統(tǒng)實驗:Lab8 文件系統(tǒng)

清華大學(xué)操作系統(tǒng)Lab8實驗報告
課程主頁:http://os.cs.tsinghua.edu.cn/oscourse/OS2018spring
實驗指導(dǎo)書:https://chyyuu.gitbooks.io/ucore_os_docs/content/
github:https://github.com/chyyuu/ucore_os_lab

實驗?zāi)康?/h3>
  • 了解基本的文件系統(tǒng)系統(tǒng)調(diào)用的實現(xiàn)方法;
  • 了解一個基于索引節(jié)點組織方式的Simple FS文件系統(tǒng)的設(shè)計與實現(xiàn);
  • 了解文件系統(tǒng)抽象層-VFS的設(shè)計與實現(xiàn);

練習(xí)1: 完成讀文件操作的實現(xiàn)

用戶程序讀文件需要使用系統(tǒng)調(diào)用。在用戶程序執(zhí)行read操作時會調(diào)用sys_read系統(tǒng)調(diào)用。根據(jù)ucore的中斷機制實現(xiàn),系統(tǒng)調(diào)用將通過trap_dispatch分發(fā)給syscall,隨后分發(fā)給讀的系統(tǒng)調(diào)用sys_read內(nèi)核函數(shù)。

sys_read內(nèi)核函數(shù)需要進(jìn)一步調(diào)用sysfile_read內(nèi)核函數(shù),進(jìn)入到文件系統(tǒng)抽象層處理流程完成進(jìn)一步的讀文件操作。sysfile_read函數(shù)調(diào)用file_read函數(shù),file_read函數(shù)調(diào)用vop_read函數(shù)接口進(jìn)入到文件系統(tǒng)實例的讀操作接口。

vop_read函數(shù)實際上是對sfs_read的包裝。sfs_read函數(shù)調(diào)用sfs_io函數(shù)。它有三個參數(shù),node是對應(yīng)文件的inode,iob是緩存,write表示是讀還是寫的布爾值( 0表示讀,1表示寫) ,這里是0。函數(shù)先找到inode對應(yīng)sfs和sin,然后調(diào)用sfs_io_nolock函數(shù)進(jìn)行讀取文件操作,最后調(diào)用iobuf_skip函數(shù)調(diào)整iobuf的指針。

sfs_io_nolock函數(shù)主要用來將磁盤中的一段數(shù)據(jù)讀入到內(nèi)存中或者將內(nèi)存中的一段數(shù)據(jù)寫入磁盤。需要補充的代碼用來對一段地址對應(yīng)的磁盤塊讀或?qū)憽?/p>

// 讀取第一頁,可能不對齊
    if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno, &ino)) != 0) {
        goto out;
    }
    blkoff = offset % SFS_BLKSIZE;
    size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset);
    if ((ret = sfs_buf_op(sfs, buf, size, ino, blkoff)) != 0) {
        goto out;
    }
    alen += size;
// 如果超過一頁的話,
    if (nblks != 0) {
// 讀取第二頁到第n-1頁,這些頁大小均為SFS_BLKSIZE
        for (int i = blkno + 1; i < blkno + nblks; ++i) {
            if ((ret = sfs_bmap_load_nolock(sfs, sin, i, &ino)) != 0) {
                goto out;
            }
            if ((ret = sfs_block_op(sfs, buf + alen, ino, 1)) != 0) {
                goto out;
            }
            alen += SFS_BLKSIZE;
        }
// 最后一頁,可能不對齊
        if (endpos % SFS_BLKSIZE != 0) {
            if ((ret = sfs_bmap_load_nolock(sfs, sin, blkno + nblks, &ino)) != 0) {
                goto out;
            }
            if ((ret = sfs_buf_op(sfs, buf + alen, endpos % SFS_BLKSIZE, ino, 0)) != 0) {
                goto out;
            }
            alen += endpos % SFS_BLKSIZE;
        }
    }
請在實驗報告中給出設(shè)計實現(xiàn)”UNIX的PIPE機制“的概要設(shè)方案,鼓勵給出詳細(xì)設(shè)計方案

PIPE在UNIX體系里也認(rèn)為是一種文件,參考STDIN、STDOUT以及SFS的設(shè)計,可以將PIPE作為與這三者并列的一個子系統(tǒng)。

因此應(yīng)當(dāng)在初始化STDIN、STDOUT加入對于PIPE的初始化并創(chuàng)建PIPE的inode。同時為了完成PIPE中內(nèi)容的緩存,在內(nèi)存中開辟一塊區(qū)域給PIPE使用。

為了使用戶進(jìn)程使用PIPE,應(yīng)當(dāng)添加同PIPE相關(guān)的系統(tǒng)調(diào)用,包括

  • 將PIPE和兩個進(jìn)程相連
  • 向PIPE中寫入數(shù)據(jù)
  • 從PIPE中讀出數(shù)據(jù)

這些系統(tǒng)調(diào)用通過文件系統(tǒng)提供給的接口,

練習(xí)2: 完成基于文件系統(tǒng)的執(zhí)行程序機制的實現(xiàn)

首先,需要在alloc_proc函數(shù)中對進(jìn)程控制塊中和文件系統(tǒng)有關(guān)的部分進(jìn)行初始化:

    proc -> filesp = files_create();

在進(jìn)行fork時,將父進(jìn)程的文件系統(tǒng)信息復(fù)制到子進(jìn)程中去:

    int file_success = copy_files(clone_flags, proc);
    if (file_success != 0) {
        goto bad_fork_cleanup_fs;
    }

最后還要修改load_icode的代碼。之前的代碼是和操作系統(tǒng)一起load進(jìn)來的,現(xiàn)在要改為在啟動操作系統(tǒng)后,通過文件系統(tǒng)加載進(jìn)來。

static int
load_icode(int fd, int argc, char **kargv) {
    if (current->mm != NULL) {
        panic("load_icode: current->mm must be empty.\n");
    }
    int ret = -E_NO_MEM;
    struct mm_struct *mm;
//(1) create a new mm for current process
    if ((mm = mm_create()) == NULL) {
        goto bad_mm;
    }

//(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
    if (setup_pgdir(mm) != 0) {
        goto bad_pgdir_cleanup_mm;
    }

//(3) copy TEXT/DATA/BSS parts in binary to memory space of process
    struct Page *page;

//(3.1) read raw data content in file and resolve elfhdr
    struct elfhdr __elf, *elf = &__elf;
    ret = load_icode_read(fd, elf, sizeof(struct elfhdr), 0);
    if (ret != 0) {
        goto bad_elf_cleanup_pgdir;
    }

    if (elf -> e_magic != ELF_MAGIC) {
        ret = -E_INVAL_ELF;
        goto bad_elf_cleanup_pgdir;
    }

    struct proghdr __ph, *ph = &__ph;
    uint32_t vm_flags, perm, phnum;
    for (phnum = 0; phnum < elf->e_phnum; phnum ++) {
//(3.2) read raw data content in file and resolve proghdr based on info in elfhdr
        off_t phoff = elf->e_phoff + sizeof(struct proghdr) * phnum;
        if ((ret = load_icode_read(fd, ph, sizeof(struct proghdr), phoff)) != 0) {
            goto bad_cleanup_mmap;
        }
        if (ph->p_type != ELF_PT_LOAD) {
            continue ;
        }
        if (ph->p_filesz > ph->p_memsz) {
            ret = -E_INVAL_ELF;
            goto bad_cleanup_mmap;
        }
        if (ph->p_filesz == 0) {
            continue ;
        }
        vm_flags = 0, perm = PTE_U;
        if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
        if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
        if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ;
        if (vm_flags & VM_WRITE) perm |= PTE_W;
       
 //(3.3) call mm_map to build vma related to TEXT/DATA
        if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) {
            goto bad_cleanup_mmap;
        }
        off_t offset = ph->p_offset;
        size_t off, size;
        uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);

        ret = -E_NO_MEM;

//(3.4) callpgdir_alloc_page to allocate page for TEXT/DATA, read contents in file
        end = ph->p_va + ph->p_filesz;
        while (start < end) {
            if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
                ret = -E_NO_MEM;
                goto bad_cleanup_mmap;
            }
            off = start - la, size = PGSIZE - off, la += PGSIZE;
            if (end < la) {
                size -= la - end;
            }
            if ((ret = load_icode_read(fd, page2kva(page) + off, size, offset)) != 0) {
                goto bad_cleanup_mmap;
            }
            start += size, offset += size;
        }
        end = ph->p_va + ph->p_memsz;

(3.5) callpgdir_alloc_page to allocate pages for BSS, memset zero in these pages
        if (start < la) {
            if (start == end) {
                continue ;
            }
            off = start + PGSIZE - la, size = PGSIZE - off;
            if (end < la) {
                size -= la - end;
            }
            memset(page2kva(page) + off, 0, size);
            start += size;
            assert((end < la && start == end) || (end >= la && start == la));
        }
        while (start < end) {
            if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
                ret = -E_NO_MEM;
                goto bad_cleanup_mmap;
            }
            off = start - la, size = PGSIZE - off, la += PGSIZE;
            if (end < la) {
                size -= la - end;
            }
            memset(page2kva(page) + off, 0, size);
            start += size;
        }
    }
    sysfile_close(fd);

//(4) call mm_map to setup user stack, and put parameters into user stack
    vm_flags = VM_READ | VM_WRITE | VM_STACK;
    if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {
        goto bad_cleanup_mmap;
    }
    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);
    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);
    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);
    assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);

//(5) setup current process's mm, cr3, reset pgidr (using lcr3 MARCO)
    mm_count_inc(mm);
    current->mm = mm;
    current->cr3 = PADDR(mm->pgdir);
    lcr3(PADDR(mm->pgdir));

//(6) setup uargc and uargv in user stacks
    uint32_t argv_size=0, i;
    for (i = 0; i < argc; i ++) {
        argv_size += strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
    }

    uintptr_t stacktop = USTACKTOP - (argv_size/sizeof(long)+1)*sizeof(long);
    char** uargv=(char **)(stacktop  - argc * sizeof(char *));

    argv_size = 0;
    for (i = 0; i < argc; i ++) {
        uargv[i] = strcpy((char *)(stacktop + argv_size ), kargv[i]);
        argv_size +=  strnlen(kargv[i],EXEC_MAX_ARG_LEN + 1)+1;
    }

    stacktop = (uintptr_t)uargv - sizeof(int);
    *(int *)stacktop = argc;

//(7) setup trapframe for user environment
    struct trapframe *tf = current->tf;
    memset(tf, 0, sizeof(struct trapframe));
    tf->tf_cs = USER_CS;
    tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
    tf->tf_esp = stacktop;
    tf->tf_eip = elf->e_entry;
    tf->tf_eflags = FL_IF;
    ret = 0;

//(8) if up steps failed, you should cleanup the env.
out:
    return ret;
bad_cleanup_mmap:
    exit_mmap(mm);
bad_elf_cleanup_pgdir:
    put_pgdir(mm);
bad_pgdir_cleanup_mm:
    mm_destroy(mm);
bad_mm:
    goto out;
}
請在實驗報告中給出設(shè)計實現(xiàn)基于”UNIX的硬鏈接和軟鏈接機制“的概要設(shè)方案,鼓勵給出詳細(xì)設(shè)計方案

覆蓋的知識點

與參考答案的區(qū)別

  • 練習(xí)1:自己完成。
  • 練習(xí)2:load_icode部分參考了答案。

總結(jié)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容