文件系統部分 buf.h fcntl.h stat.h fs.h file.h ide.c bio.c log.c fs.c file.c sysfile.c exec.chtml
1.buf.h:對xv6中磁盤塊數據結構進行定義,塊大小爲512字節。node
// xv6中磁盤塊數據結構,塊大小512字節 struct buf { int flags; // DIRTY, VALID uint dev; uint sector; // 對應扇區 struct buf *prev; // LRU cache list struct buf *next; // 鏈式結構用於鏈接 struct buf *qnext; // disk queue uchar data[512]; }; #define B_BUSY 0x1 // buffer is locked by some process #define B_VALID 0x2 // buffer has been read from disk #define B_DIRTY 0x4 // buffer needs to be written to disk
2.fcntl.h:宏定義操做權限。c++
#define O_RDONLY 0x000 // 只讀 #define O_WRONLY 0x001 // 只寫 #define O_RDWR 0x002 // 讀寫 #define O_CREATE 0x200 // 建立
3.stat.h:聲明文件或目錄屬性數據結構。git
#define T_DIR 1 // Directory #define T_FILE 2 // File #define T_DEV 3 // Device struct stat { short type; // Type of file int dev; // File system's disk device uint ino; // Inode number short nlink; // Number of links to file uint size; // Size of file in bytes };
4.fs.h / fs.c:聲明超級塊、dinode、文件和目錄數據結構,以及相關的宏定義。程序員
#define ROOTINO 1 // root i-number #define BSIZE 512 // block size // File system super block struct superblock { uint size; // Size of file system image (blocks) uint nblocks; // Number of data blocks uint ninodes; // Number of inodes. uint nlog; // Number of log blocks }; #define NDIRECT 12 #define NINDIRECT (BSIZE / sizeof(uint)) #define MAXFILE (NDIRECT + NINDIRECT) // 磁盤上inode節點體現形式 // On-disk inode structure struct dinode { short type; // File type short major; // Major device number (T_DEV only) short minor; // Minor device number (T_DEV only) short nlink; // Number of links to inode in file system uint size; // Size of file (bytes) uint addrs[NDIRECT+1]; // Data block addresses }; // Inodes per block. #define IPB (BSIZE / sizeof(struct dinode)) // Block containing inode i #define IBLOCK(i) ((i) / IPB + 2) // Bitmap bits per block #define BPB (BSIZE*8) // Block containing bit for block b #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) // Directory is a file containing a sequence of dirent structures. #define DIRSIZ 14 // 文件或目錄據結構,目錄自己是以文件的方式存儲到磁盤上的,叫作目錄文件。 struct dirent { ushort inum; // i節點 char name[DIRSIZ]; // 文件或目錄名 };
5.file.h:聲明inode、file數據結構。編程
struct file { // 分爲管道文件,設備文件,普通文件 enum { FD_NONE, FD_PIPE, FD_INODE } type; int ref; // reference count char readable; char writable; struct pipe *pipe; struct inode *ip; // 指向inode節點 uint off; }; // 在內存中inode節點體現形式 // in-memory copy of an inode struct inode { uint dev; // Device number uint inum; // Inode number int ref; // Reference count int flags; // I_BUSY, I_VALID // 下面這些編程都是dinode的拷貝 // copy of disk inode short type; short major; short minor; short nlink; uint size; uint addrs[NDIRECT+1]; }; #define I_BUSY 0x1 #define I_VALID 0x2 // table mapping major device number to device functions struct devsw { int (*read)(struct inode*, char*, int); int (*write)(struct inode*, char*, int); }; extern struct devsw devsw[]; #define CONSOLE 1
6.ide.c:磁盤IO的具體實現,xv6維護了一個進程請求磁盤操做的隊列(idequeue)。當進程調用**void iderw(struct buf *b)**請求讀寫磁盤時,該請求被加入等待隊列idequeue,同時進程進入睡眠狀態。當一個磁盤讀寫操做完成時,會觸發一箇中斷,中斷處理程序ideintr()會移除隊列開頭的請求,喚醒隊列開頭請求所對應的進程。數組
// idequeue points to the buf now being read/written to the disk. // idequeue->qnext points to the next buf to be processed. // You must hold idelock while manipulating queue. static struct spinlock idelock; // 保護 idequeue static struct buf *idequeue; // 磁盤讀寫操做的請求隊列 …… // 等待磁盤進入空閒狀態 // Wait for IDE disk to become ready. static int idewait(int checkerr) { …… // while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY); …… } // 初始化IDE磁盤IO void ideinit(void) { …… } // 開始一個磁盤讀寫請求 // Start the request for b. Caller must hold idelock. static void idestart(struct buf *b) { …… } // 當磁盤請求完成後中斷處理程序會調用的函數 // Interrupt handler. void ideintr(void) { …… // 處理完一個磁盤IO請求後,喚醒等待在等待隊列頭的那個進程 wakeup(b); // 若是隊列不爲空,繼續處理下一個磁盤IO任務 // Start disk on next buf in queue. if(idequeue != 0) idestart(idequeue); …… } //PAGEBREAK! 上層文件系統調用的磁盤IO接口 // Sync buf with disk. // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. // Else if B_VALID is not set, read buf from disk, set B_VALID. void iderw(struct buf *b) { …… // 競爭鎖 acquire(&idelock); //DOC:acquire-lock // Append b to idequeue. b->qnext = 0; for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue ; *pp = b; // Start disk if necessary. 開始處理一個磁盤IO任務 if(idequeue == b) idestart(b); // Wait for request to finish. 睡眠等待 while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ sleep(b, &idelock); } release(&idelock); // 釋放鎖 }
7.bio.c:Buffer Cache的具體實現。由於讀寫磁盤操做效率不高,根據時間與空間局部性原理,這裏將最近常常訪問的磁盤塊緩存在內存中。主要接口有struct buf* bread(uint dev, uint sector)、void bwrite(struct buf *b),bread會首先從緩存中去尋找塊是否存在,若是存在直接返回,若是不存在則請求磁盤讀操做,讀到緩存中後再返回結果。bwrite直接將緩存中的數據寫入磁盤。
8.log.c:該模塊主要是維護文件系統的一致性。引入log模塊後,對於上層文件系統的所有磁盤操做都被切分爲transaction,每一個transaction都會首先將數據和其對應磁盤號寫入磁盤上的log區域,且只有在log區域寫入成功後,纔將log區域的數據寫入真正存儲的數據塊。所以,若是在寫log的時候宕機,重啓後文件系統視爲該log區的寫入不存在,若是從log區寫到真實區域的時候宕機,則可根據log區域的數據恢復。
9.sysfile.c:主要定義了與文件相關的系統調用。主要接口及含義以下:緩存
// Allocate a file descriptor for the given file. // Takes over file reference from caller on success. static int fdalloc(struct file *f) { …… // 申請一個未使用的文件句柄 } int sys_dup(void) { …… // 調用filedup對文件句柄的引用計數+1 filedup(f); return fd; } int sys_read(void) { …… // 讀取文件數據 return fileread(f, p, n); } int sys_write(void) { …… // 向文件寫數據 return filewrite(f, p, n); } int sys_close(void) { …… // 釋放文件句柄資源 fileclose(f); return 0; } int sys_fstat(void) { …… // 修改文件統計信息 return filestat(f, st); } // Create the path new as a link to the same inode as old. int sys_link(void) { …… // 爲已有的inode建立一個新名字 } //PAGEBREAK! int sys_unlink(void) { …… // 解除inode中的某個名字, 若名字全被移除, inode回被釋放 } static struct inode* create(char *path, short type, short major, short minor) { …… // } int sys_mkdir(void) { …… // 建立一個目錄 } int sys_mknod(void) { …… // 建立一個新文件 } int sys_chdir(void) { …… // 切換目錄 } int sys_pipe(void) { …… // 建立一個管道文件 }
10.exec.c:只有一個exec接口,實質就是傳入elf格式的可執行文件,裝載到內存並分配內存頁,argv是一個指針數組,用於攜帶參數。bash
int exec(char *path, char **argv) { …… // 判斷文件是否存在 if((ip = namei(path)) == 0) return -1; ilock(ip); pgdir = 0; // Check ELF header 檢查elf頭是否合法 if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) goto bad; …… // Load program into memory. sz = 0; for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){ if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) goto bad; if(ph.type != ELF_PROG_LOAD) continue; if(ph.memsz < ph.filesz) goto bad; if((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0) goto bad; if(loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0) goto bad; } iunlockput(ip); ip = 0; // Allocate two pages at the next page boundary. // Make the first inaccessible. Use the second as the user stack. sz = PGROUNDUP(sz); if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0) goto bad; clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); sp = sz; // Push argument strings, prepare rest of stack in ustack. for(argc = 0; argv[argc]; argc++) { if(argc >= MAXARG) goto bad; sp = (sp - (strlen(argv[argc]) + 1)) & ~3; if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) goto bad; ustack[3+argc] = sp; } …… bad: if(pgdir) freevm(pgdir); if(ip) iunlockput(ip); return -1; }
1.瞭解 UNIX 文件系統的主要組成部分:超級塊(superblock),i節點(inode),數據塊(datablock),目錄塊(directoryblock),間接塊(indirectionblock)。分別解釋它們的做用。數據結構
boot | super block | dinode | free bitmap blocks | data blocks | log blocks |
---|---|---|---|---|---|
第0塊 | 第1塊 | superblock.ninodes塊 | 位圖管理空閒區塊 | superblock.nblocks塊 | superblock.nlog塊 |
2.閱讀文件ide.c。這是一個簡單的ide硬盤驅動程序,對其內容做大體瞭解。
System calls | File descriptors |
---|---|
Pathnames | Recursive lookup |
Directories | Directory inodes |
Files | Inodes and block allocator |
Transactions | Logging |
Blocks | Buffer cache |
3.閱讀文件buf.h,bio.c。瞭解 XV6 文件系統中buffer cache層的內容和實現。描述buffer雙鏈表數據結構及其初始化過程。瞭解 buffer的狀態。瞭解對buffer的各類操做。
// buf.h struct buf { int flags; uint dev; uint sector; struct buf *prev; // LRU cache list struct buf *next; struct buf *qnext; // disk queue uchar data[512]; }; // bio.c struct { struct spinlock lock; struct buf buf[NBUF]; // Linked list of all buffers, through prev/next. // head.next is most recently used. struct buf head; } bcache; void binit(void) { struct buf *b; initlock(&bcache.lock, "bcache"); //PAGEBREAK! 頭插法,每次都是插入到bcache.head的後面 // Create linked list of buffers bcache.head.prev = &bcache.head; bcache.head.next = &bcache.head; for(b = bcache.buf; b < bcache.buf+NBUF; b++){ b->next = bcache.head.next; b->prev = &bcache.head; b->dev = -1; bcache.head.next->prev = b; bcache.head.next = b; } } // Return a B_BUSY buf with the contents of the indicated disk sector. struct buf* bread(uint dev, uint sector) { struct buf *b; // 優先查找緩存 b = bget(dev, sector); if(!(b->flags & B_VALID)) iderw(b); // 命中失敗時調用下一次接口真真實實讀磁盤 return b; } // Write b's contents to disk. Must be B_BUSY. void bwrite(struct buf *b) { if((b->flags & B_BUSY) == 0) panic("bwrite"); b->flags |= B_DIRTY; iderw(b); // 當即寫, 未延遲寫 }
4.閱讀文件log.c,瞭解XV6文件系統中的logging和transaction機制;
日誌存在於磁盤末端已知的固定區域。它包含了一個起始塊,緊接着一連串的數據塊。起始塊包含了一個扇區號的數組,每個對應於日誌中的數據塊,起始塊還包含了日誌數據塊的計數。xv6 在提交後修改日誌的起始塊,而不是以前,而且在將日誌中的數據塊都拷貝到文件系統以後將數據塊計數清0。提交以後,清0以前的崩潰就會致使一個非0的計數值。
5.閱讀文件fs.h/fs.c。瞭解XV6文件系統的硬盤佈局。
// On-disk inode structure struct dinode { short type; // File type short major; // Major device number (T_DEV only) short minor; // Minor device number (T_DEV only) short nlink; // Number of links to inode in file system uint size; // Size of file (bytes) // NDIRECT = 12, 前12個爲直接索引, // 第13個爲間接索引, 可容納128個直接索引 uint addrs[NDIRECT+1]; // Data block addresses };
6.閱讀文件file.h/file.c。瞭解XV6的「文件」有哪些,以及文件,i節點,設備相關的數據結構。瞭解XV6對文件的基本操做有哪些。XV6最多支持多少個文件? 每一個進程最多能打開多少個文件?
// param.h #define NOFILE 16 // open files per process #define NFILE 100 // open files per system
7.閱讀文件sysfile.c。瞭解與文件系統相關的系統調用,簡述各個系統調用的做用。
參見源代碼閱讀部分,已經作出了完整解答。
[1] xv6中文文檔
[2] xv6文件系統博客園
[3] xv6文件系統CSDN
[4] xv6文件系統CSDN [5] 操做系統-文件系統課件