structFile *f_dir;// the pointer to the dir where this file is in, valid only in memory. u_char f_pad[BY2FILE - MAXNAMELEN - 4 - 4 - NDIRECT * 4 - 4 - 4]; // 占位用的 };
// Make new block contians link to files in a directory. intmake_link_block(struct File *dirf, int nblk) { int bno = next_block(BLOCK_FILE); save_block_link(dirf, nblk, bno); dirf->f_size += BY2BLK; return bno; }
// Overview: // Create new block pointer for a file under sepcified directory. // Notice that when we delete a file, we do not re-arrenge all // other file pointers, so we should be careful of existing empty // file pointers // // Post-Condition: // We ASSUME that this function will never fail // // Return: // Return a unused struct File pointer // Hint: // use make_link_block function struct File *create_file(struct File *dirf) { structFile *dirblk; int i, bno, found; int nblk = dirf->f_size / BY2BLK; int j;
for (i = 0; i < nblk; i++) { if (i < NDIRECT) { bno = dirf->f_direct[i]; } else { bno = ((int *)(disk[dirf->f_indirect].data))[i]; }
// Flush disk block usage to bitmap. voidflush_bitmap() { int i; // update bitmap, mark all bit where corresponding block is used. for (i = 0; i < nextbno; ++i) { ((uint32_t *)disk[2 + i / BIT2BLK].data)[(i % BIT2BLK) / 32] &= ~(1 << (i % 32)); } }
然后我们去写这个磁盘文件
就是很简单的先烧录 super,然后烧录其他的块。
voidfinish_fs(char *name) { int fd, i, k, n, r; uint32_t *p;
// Prepare super block. memcpy(disk[1].data, &super, sizeof(super));
// Dump data in `disk` to target image file. fd = open(name, O_RDWR | O_CREAT, 0666); for (i = 0; i < 1024; ++i) { reverse_block(disk + i); write(fd, disk[i].data, BY2BLK); }
if (strcmp(argv[2], "-r") == 0) { for (i = 3; i < argc; ++i) { write_directory(&super.s_root, argv[i]); } } else { for (i = 2; i < argc; ++i) { write_file(&super.s_root, argv[i]); } }
关于读写两个系统调用,主要是想强调参数问题,va 指的是一个 “正常” 的虚拟地址,而 dev 指的是一个 “不太正常” 的设备物理地址。写设备就是把 va 为起始地址长度为 len 的内容拷贝到以 dev 为起始地址的设备物理地址区域。读设备就是把 dev 为起始地址长度为 len 的内容拷贝到以 va 为起始地址的虚拟地址区域。
intsys_write_dev(int sysno, u_int va, u_int dev, u_int len) { if (dev >= 0x10000000 && dev + len <= 0x10000020 || dev >= 0x13000000 && dev + len <= 0x13004200 || dev >= 0x15000000 && dev + len <= 0x15000200) { bcopy(va, 0xa0000000 + dev, len); return0; } return -E_INVAL; }
intsys_read_dev(int sysno, u_int va, u_int dev, u_int len) { if (dev >= 0x10000000 && dev + len <= 0x10000020 || dev >= 0x13000000 && dev + len <= 0x13004200 || dev >= 0x15000000 && dev + len <= 0x15000200) { bcopy(0xa0000000 + dev, va, len); return0; } return -E_INVAL; }
3.2 设备地址接口
然后需要熟悉一下磁盘的接口地址,有如下总结:
以 0x1300_0000 为起始物理地址
偏移量
大小(字节)
功能
0x00
4
读写某个磁盘块的时候相对于磁盘开始位置的偏移量
0x10
4
读写某个磁盘块的 id
0x20
4
读操作写入 0,写操作写入 1
0x30
4
记录着上次读写操作是否成功
0x4000
512
读写缓存区,从磁盘读出的内容和写入磁盘的内容都会被先放到这里
然后 ide.c 里两个函数就显得很自然了
voidide_read(u_int diskno, u_int secno, void *dst, u_int nsecs) { // 0x200: the size of a sector: 512 bytes. int offset_begin = secno * 0x200; int offset_end = offset_begin + nsecs * 0x200; int offset = 0;
u_int zero = 0; u_int cur_offset = 0;
while (offset_begin + offset < offset_end) { // error occurred, then panic. cur_offset = offset_begin + offset; if (syscall_write_dev((u_int)&diskno, 0x13000010, 4) < 0) user_panic("ide_read panic"); if (syscall_write_dev((u_int)&cur_offset, 0x13000000, 4) < 0) user_panic("ide_read panic"); if (syscall_write_dev((u_int)&zero, 0x13000020, 4) < 0) user_panic("ide_read panic"); if (syscall_read_dev((u_int)(dst + offset), 0x13004000, 0x200) < 0) user_panic("ide_read panic"); u_int succ; if (syscall_read_dev((u_int)&succ, 0x13000030, 4) < 0) user_panic("ide_read panic"); if (!succ) user_panic("ide_read panic"); offset += 0x200; } }
voidide_write(u_int diskno, u_int secno, void *src, u_int nsecs) { int offset_begin = secno * 0x200; int offset_end = offset_begin + nsecs * 0x200; int offset = 0;
u_int one = 1; u_int cur_offset = 0;
// DO NOT DELETE WRITEF !!! writef("diskno: %d\n", diskno);
while (offset_begin + offset < offset_end) { // copy data from source array to disk buffer. // if error occur, then panic. cur_offset = offset_begin + offset; if (syscall_write_dev((u_int)(src + offset), 0x13004000, 0x200) < 0) user_panic("ide_write panic"); if (syscall_write_dev((u_int)&diskno, 0x13000010, 4) < 0) user_panic("ide_write panic"); if (syscall_write_dev((u_int)&cur_offset, 0x13000000, 4) < 0) user_panic("ide_write panic"); if (syscall_write_dev((u_int)&one, 0x13000020, 4) < 0) user_panic("ide_write panic"); u_int succ; if (syscall_read_dev((u_int)&succ, 0x13000030, 4) < 0) user_panic("ide_write panic"); if (!succ) user_panic("ide_read panic"); offset += 0x200; } }
// Overview: // Return the virtual address of this disk block. If the `blockno` is greater // than disk's nblocks, panic. u_int diskaddr(u_int blockno) { if (super != NULL && blockno > super->s_nblocks) { user_panic("diskaddr panic"); } return DISKMAP + blockno * BY2BLK; }
// Overview: // Check if this virtual address is mapped to a block. (check PTE_V bit) u_int va_is_mapped(u_int va) { return (((*vpd)[PDX(va)] & (PTE_V)) && ((*vpt)[VPN(va)] & (PTE_V))); }
// Overview: // Check if this disk block is mapped to a vitrual memory address. (check corresponding `va`) u_int block_is_mapped(u_int blockno) { u_int va = diskaddr(blockno); if (va_is_mapped(va)) { return va; } return0; }
// Overview: // Check if this virtual address is dirty. (check PTE_D bit) u_int va_is_dirty(u_int va) { return (*vpt)[VPN(va)] & PTE_D; }
// Overview: // Check if this block is dirty. (check corresponding `va`) u_int block_is_dirty(u_int blockno) { u_int va = diskaddr(blockno); return va_is_mapped(va) && va_is_dirty(va); }
// Overview: // Allocate a page to hold the disk block. // // Post-Condition: // If this block is already mapped to a virtual address(use `block_is_mapped`), // then return 0, indicate success, else alloc a page for this `va` address, // and return the result(success or fail) of `syscall_mem_alloc`. intmap_block(u_int blockno) { // Step 1: Decide whether this block has already mapped to a page of physical memory. if (block_is_mapped(blockno)) { return0; } // Step 2: Alloc a page of memory for this block via syscall. return syscall_mem_alloc(0, diskaddr(blockno), PTE_R | PTE_V); }
// Overview: // Unmap a block. voidunmap_block(u_int blockno) { int r; u_int addr; // Step 1: check if this block is mapped. addr = block_is_mapped(blockno); // Step 2: use block_is_free,block_is_dirty to check block, // if this block is used(not free) and dirty, it needs to be synced to disk: write_block // can't be unmap directly. if (!block_is_free(blockno) && block_is_dirty(blockno)) { write_block(blockno); } // Step 3: use 'syscall_mem_unmap' to unmap corresponding virtual memory. r = syscall_mem_unmap(0, addr); if (r < 0) { return r; } // Step 4: validate result of this unmap operation. user_assert(!block_is_mapped(blockno)); }
// Overview: // Make sure a particular disk block is loaded into memory. // // Post-Condition: // Return 0 on success, or a negative error code on error. // // If blk!=0, set *blk to the address of the block in memory. // // If isnew!=0, set *isnew to 0 if the block was already in memory, or // to 1 if the block was loaded off disk to satisfy this request. (Isnew // lets callers like file_get_block clear any memory-only fields // from the disk blocks when they come in off disk.) // // Hint: // use diskaddr, block_is_mapped, syscall_mem_alloc, and ide_read. intread_block(u_int blockno, void **blk, u_int *isnew) { u_int va;
// Step 1: validate blockno. Make file the block to read is within the disk. if (super && blockno >= super->s_nblocks) { user_panic("reading non-existent block %08x\n", blockno); }
// Step 2: validate this block is used, not free. // Hint: // If the bitmap is NULL, indicate that we haven't read bitmap from disk to memory // until now. So, before we check if a block is free using `block_is_free`, we must // ensure that the bitmap blocks are already read from the disk to memory. if (bitmap && block_is_free(blockno)) { user_panic("reading free block %08x\n", blockno); }
// Step 3: transform block number to corresponding virtual address. va = diskaddr(blockno);
// Step 4: read disk and set *isnew. // Hint: // If this block is already mapped, just set *isnew, else alloc memory and // read data from IDE disk (use `syscall_mem_alloc` and `ide_read`). // We have only one IDE disk, so the diskno of ide_read should be 0. if (block_is_mapped(blockno)) { // the block is in memory if (isnew) { *isnew = 0; } } else { // the block is not in memory if (isnew) { *isnew = 1; } syscall_mem_alloc(0, va, PTE_V | PTE_R); ide_read(0, blockno * SECT2BLK, (void *)va, SECT2BLK); }
// Step 5: if blk != NULL, set `va` to *blk. if (blk) { *blk = (void *)va; } return0; }
这个 write_block 是一个写回操作,不是写到缓存里去,而是写到磁盘里去。
// Overview: // Wirte the current contents of the block out to disk. voidwrite_block(u_int blockno) { u_int va;
// Step 1: detect is this block is mapped, if not, can't write it's data to disk. if (!block_is_mapped(blockno)) { user_panic("write unmapped block %08x", blockno); }
// Step2: write data to IDE disk. (using ide_write, and the diskno is 0) va = diskaddr(blockno); ide_write(0, blockno * SECT2BLK, (void *)va, SECT2BLK);
syscall_mem_map(0, va, 0, va, (PTE_V | PTE_R | PTE_LIBRARY)); }
底下都是辅助子函数:理解都较为简单
// Overview: // Check to see if the block 'blockno' is free via bitmap. // // Post-Condition: // Return 1 if the block is free, else 0. intblock_is_free(u_int blockno) { if (super == 0 || blockno >= super->s_nblocks) { return0; }
// Overview: // Mark a block as free in the bitmap. voidfree_block(u_int blockno) { // Step 1: Check if the parameter `blockno` is valid (`blockno` can't be zero). if (blockno == 0 || (super != NULL && blockno >= super->s_nblocks)) { return; } // Step 2: Update the flag bit in bitmap. bitmap[blockno / 32] |= 1 << (blockno & 0x1f); }
// Overview: // Search in the bitmap for a free block and allocate it. // // Post-Condition: // Return block number allocated on success, // -E_NO_DISK if we are out of blocks. intalloc_block_num(void) { int blockno; // walk through this bitmap, find a free one and mark it as used, then sync // this block to IDE disk (using `write_block`) from memory. for (blockno = 3; blockno < super->s_nblocks; blockno++) { if (bitmap[blockno / 32] & (1 << (blockno % 32))) { // the block is free bitmap[blockno / 32] &= ~(1 << (blockno % 32)); write_block(blockno / BIT2BLK + 2); // write to disk. return blockno; } } // no free blocks. return -E_NO_DISK; }
// Overview: // Allocate a block -- first find a free block in the bitmap, then map it into memory. intalloc_block(void) { int r, bno; // Step 1: find a free block. if ((r = alloc_block_num()) < 0) { // failed. return r; } bno = r;
// Step 2: map this block into memory. if ((r = map_block(bno)) < 0) { free_block(bno); return r; }
// Step 3: return block number. return bno; }
3.4.3 读写特殊块
其实就是磁盘中的 Super 和 Bitmap
// Overview: // Read and validate the file system super-block. // // Post-condition: // If error occurred during read super block or validate failed, panic. voidread_super(void) { int r; void *blk;
// Step 2: Check fs magic nunber. if (super->s_magic != FS_MAGIC) { user_panic("bad file system magic number %x %x", super->s_magic, FS_MAGIC); }
// Step 3: validate disk size. if (super->s_nblocks > DISKMAX / BY2BLK) { user_panic("file system is too large"); }
writef("superblock is good\n"); }
// Overview: // Read and validate the file system bitmap. // // Hint: // Read all the bitmap blocks into memory. // Set the "bitmap" pointer to point ablocknot the beginning of the first bitmap block. // For each block i, user_assert(!block_is_free(i))).Check that they're all marked as inuse voidread_bitmap(void) { u_int i; void *blk = NULL;
// Step 1: calculate this number of bitmap blocks, and read all bitmap blocks to memory. nbitmap = super->s_nblocks / BIT2BLK + 1; for (i = 0; i < nbitmap; i++) { read_block(i + 2, blk, 0); }
bitmap = (u_int *)diskaddr(2);
// Step 2: Make sure the reserved and root blocks are marked in-use. // Hint: use `block_is_free` user_assert(!block_is_free(0)); user_assert(!block_is_free(1));
// Step 3: Make sure all bitmap blocks are marked in-use. for (i = 0; i < nbitmap; i++) { user_assert(!block_is_free(i + 2)); }
structFile *f_dir;// the pointer to the dir where this file is in, valid only in memory. u_char f_pad[BY2FILE - MAXNAMELEN - 4 - 4 - NDIRECT * 4 - 4 - 4]; };
structOpen { structFile *o_file;// mapped descriptor for open file u_int o_fileid; // file id int o_mode; // open mode structFilefd *o_ff;// va of filefd page };
// Initial array opentab. for (i = 0; i < MAXOPEN; i++) { opentab[i].o_fileid = i; opentab[i].o_ff = (struct Filefd *)va; va += BY2PG; } }
4.4 服务进程的结构
serve 进程用一个死循环结合 switch 的操作来根据收到的请求不同来提供不同的服务
voidserve(void) { u_int req, whom, perm;
for (;;) { perm = 0;
req = ipc_recv(&whom, REQVA, &perm); // block here
// All requests must contain an argument page if (!(perm & PTE_V)) { writef("Invalid request from %08x: no argument page\n", whom); continue; // just leave it hanging, waiting for the next request. }
switch (req) { case FSREQ_OPEN: serve_open(whom, (struct Fsreq_open *)REQVA); break;
case FSREQ_MAP: serve_map(whom, (struct Fsreq_map *)REQVA); break;
case FSREQ_SET_SIZE: serve_set_size(whom, (struct Fsreq_set_size *)REQVA); break;
case FSREQ_CLOSE: serve_close(whom, (struct Fsreq_close *)REQVA); break;
case FSREQ_DIRTY: serve_dirty(whom, (struct Fsreq_dirty *)REQVA); break;
case FSREQ_REMOVE: serve_remove(whom, (struct Fsreq_remove *)REQVA); break;
if (filebno < NDIRECT) { // Step 1: if the target block is corresponded to a direct pointer, just return the // disk block number. ptr = &f->f_direct[filebno]; } elseif (filebno < NINDIRECT) { // Step 2: if the target block is corresponded to the indirect block, but there's no // indirect block and `alloc` is set, create the indirect block. if (f->f_indirect == 0) { if (alloc == 0) { return -E_NOT_FOUND; }
structOpen { structFile *o_file;// mapped descriptor for open file u_int o_fileid; // file id int o_mode; // open mode structFilefd *o_ff;// va of filefd page };
intdir_lookup(struct File *dir, char *name, struct File **file) { int r; u_int i, j, nblock; void *blk; structFile *f;
// Step 1: Calculate nblock: how many blocks are there in this dir? nblock = dir->f_size / BY2BLK; for (i = 0; i < nblock; i++) { // Step 2: Read the i'th block of the dir. // Hint: Use file_get_block. r = file_get_block(dir, i, &blk); if (r) { return r; } f = (struct File *)blk; // Step 3: Find target file by file name in all files on this block. // If we find the target file, set the result to *file and set f_dir field. for (j = 0; j < FILE2BLK; ++j) { if (strcmp(name, f[j].f_name) == 0) { *file = f + j; f[j].f_dir = dir; return0; } } }