Contents
0 操作系统接口
1 第一个进程
2 页表
3 陷阱(traps), 中断,以及驱动
4 锁
5 调试 (scheduling)
6 文件系统
A PC 硬件
B boot loader
index
前言以及致谢
这个是为操作系统课程写的草稿,它通过一个叫xv6的内核来解释操作系统中的主要概念,xv6 重新实现的Dennis Ritchie和Ken
Thompson的UNIX 第六版。xv6 在结构和风格上面基本上和v6一样,但是它是基于X86多处理器用ANSI C实现的。
这本书应该和 xv6的源代码一起阅读.这个方法的灵感来源于John Lions对UNIX 第六版的评注。
我们有在MIT6.828操作系统课程中使用这本教材,我们感谢所有教职员工,助教,以及所有直接或间接对xv6作出贡献的人。
特别的,我们想感谢Austin Clements和Nickolai Zeldovich.
第0章
操作系统接口
操作系統的职责是将一台计算机分配给多个程序并且提供一系列比单纯硬件更有用的服务,操作系统管理以及抽象底层硬件,所以
文字处理软件不用关心他在哪种类型的磁盘上工作,它还可以协调各种硬件,使得多个程序共享硬件资源并且看起来在同时运行,最后,
操作系统提供
第六章
文件系统
文件系统的作用是组织和存储数据,一个典型的用途就是在用户和应用程序之间传递数据,还有数据持久化(persistence),这
样,在重启之后,数据仍然存在。
xv6 文件系统提供了unix-like 的文件,文件夹(directories) 还有路径(pathname). 文件存放在ide 磁盘上面。一个文件系统主要面临着下面这些
问题:
- 需要用磁盘上的数据结构去表示文件夹(directory) 文件, 还有标识block 的各种信息,block中的内容还有哪些block是空的。
- 文件系统,必须要支持灾难恢复(crash recover). 就是说,如果发生了crash(比如突然掉电了),文件系统必须是重启之后,能够正常工作
- 不同的进程可能同时对一个文件进行操作, 所以需要文件系统去维护一致性(maintain invariant).
- 访问磁盘要比访问内存慢几个数量级,所以,需要将经常用到的文件cache 到主存上面。
在剩下来的章节里,我们就详细讲解各层的实现。
buffer cache 负责两项事情: 1,将磁盘的上block 同步到主存上面,同时保证,同一时间,内核里只有一个线程在使用它。 2。 将最常用到的block cache 到内存中,这样避免了多次去读很慢的磁盘上去读数据。
1. Buffer cache 层
buffer cache 层主要暴露(exported) 两个接口(interface) : bread 和 bread;前者负责从磁盘上获得一个block ,缓冲到内存上
代码:block allocator
// Blocks.
// Allocate a zeroed disk block.
static uint
balloc(uint dev)
{
int b, bi, m;
struct buf *bp;
struct superblock sb;
bp = 0;
readsb(dev, &sb);
for (b = 0; b <sb.size; b+= BPB) {
bp = bread(dev, BBLOCK(b, sb.ninodes));
for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
m = 1 << (bi % 8);
if ((bp->data[bi/8] & m) ==0) { // Is block free ?
bp->data[bi/8] |= m; // Mark block in use.
log_write(bp);
brelse(bp);
bzero(dev, b + bi);
return b + bi;
}
}
brelse(bp);
}
panic("balloc: out of blocks");
}
Bfree 找到要找的block,然后清掉相应的bit.
// Free a disk block.
static void
bfree(int dev, uint b)
{
struct buf *bp;
struct superblock sb;
int bi, m;
readsb(dev, &sb);
bp = bread(dev, BBLOCK(b, sb.ninodes));
bi = b % BPB;
m = 1 << (bi % 8);
if ((bp->data[bi/8] &m) == 0)
panic("free free block");
bp->data[bi/8] &= ~m;
log_write(bp);
brelse(bp);
}
代码:inode layer
// Free a disk block.
static void
bfree(int dev, uint b)
{
struct buf *bp;
struct superblock sb;
int bi, m;
readsb(dev, &sb);
bp = bread(dev, BBLOCK(b, sb.ninodes));
bi = b % BPB;
m = 1 << (bi % 8);
if ((bp->data[bi/8] &m) == 0)
panic("free free block");
bp->data[bi/8] &= ~m;
log_write(bp);
brelse(bp);
}
对于术语inode 有两个相关联的含义,一个是位于磁盘上的,一个是位于内存上的,内存上的基本是磁盘上的一份复制,但又加了
额外的信息。
所有 on-disk inode 都放在一个叫inode block 的连续区域。 每一个inode 都有着相同的大小,很容易的,给每一个inode 一个编号n.
事实上,这个编号n 叫inode 号,或者 i-number.在inode 的实现时面,也是用inode 去唯一标识一个inode.
on-disk(磁盘上)的inode 用一个叫dinode 的结构体去宝义,其中type 成员用于区分这个文件是 file, directory, 或者special files(device).
成员 nlink 用于计数有多少个directory entries 指向这个inode. 成员 size 记录了文件的大小, 数组 addrs 记录了存放文件内容数据的
block 号。
// On-disk inode structure
shruct 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 address
};
对于某个正在被用到的文件,内核会在内存中放一份inode. 成员 ref 指的是有多少个指针在引用这个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
short type; //copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+1];
};