MIT 6.S081 Lab9 File System
MIT 6.S081 Lab9 : File System
Task 1 二级文件索引
block 结构:
- 直接块 11个 [NDIRECT]
- 一级间接块1个**[NDIRECT + 1]**
- 二级间接块1个**[NDIRECT + 2]**
更改内存上的 inode 结构:
// kernel/file.h
// in-memory copy of an inode
struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+2]; // NDIRECT+1 -> NDIRECT+2
};
更改磁盘上的 inode 结构:
// kernel/fs.h
#define NDIRECT 11 // 12 -> 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT * NINDIRECT) // 一定要注意这个
// On-disk inode structure
struct dinode {
short type; // File type
short major; // Major device number (T_DEVICE only)
short minor; // Minor device number (T_DEVICE only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+2]; // Data block addresses (NDIRECT+1 -> NDIRECT+2)
};
增加文件的二级索引
// kernel/fs.c
// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a; // addr用于存储块地址,*a用于指向间接块数据
struct buf *bp; // 缓冲区指针,用于读取磁盘块到内存
// 如果bn小于NDIRECT,表示这是一个直接块
if(bn < NDIRECT){
// 如果对应的直接块尚未分配(即addrs[bn]为0),则分配一个新的块
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev); // 分配新块,并更新inode中的块地址
return addr; // 返回块地址
}
// 计算超出直接块范围后的逻辑块号
bn -= NDIRECT;
// 如果bn小于NINDIRECT,表示这是一个一级间接块
if(bn < NINDIRECT){
// 如果一级间接块尚未分配,则分配一个新的间接块
if((addr = ip->addrs[NDIRECT]) == 0)
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
// 读取一级间接块的数据到缓冲区bp
bp = bread(ip->dev, addr);
a = (uint*)bp->data; // 将缓冲区数据转换为uint指针
// 如果指定的间接块条目尚未分配,则分配一个新的数据块
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev); // 分配新块,并更新间接块中的条目
log_write(bp); // 将修改写入日志以确保可靠性
}
brelse(bp); // 释放缓冲区
return addr; // 返回找到或新分配的数据块地址
}
// 计算超出一级间接块范围后的逻辑块号
bn -= NINDIRECT;
// 如果bn小于NINDIRECT*NINDIRECT,表示这是一个二级间接块
if(bn < NINDIRECT * NINDIRECT) {
// 如果二级间接块尚未分配,则分配一个新的间接块
if((addr = ip->addrs[NDIRECT+1]) == 0)
ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
// 读取二级间接块的数据到缓冲区bp
bp = bread(ip->dev, addr);
a = (uint*)bp->data; // 将缓冲区数据转换为uint指针
// 如果第一级间接块条目尚未分配,则分配一个新的间接块 这里值存储地址信息,指向他代表的数据块
if((addr = a[bn/NINDIRECT]) == 0){
a[bn/NINDIRECT] = addr = balloc(ip->dev); // 分配新块,并更新间接块中的条目
log_write(bp); // 将修改写入日志以确保可靠性
}
brelse(bp); // 释放缓冲区
// 计算第二级间接块中的具体位置
bn %= NINDIRECT;
// 读取第二级间接块的数据到缓冲区bp
bp = bread(ip->dev, addr);
a = (uint*)bp->data; // 将缓冲区数据转换为uint指针
// 如果具体的块尚未分配,则分配一个新的数据块
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev); // 分配新块,并更新间接块中的条目
log_write(bp); // 将修改写入日志以确保可靠性
}
brelse(bp); // 释放缓冲区
return addr; // 返回找到或新分配的数据块地址
}
panic("bmap: out of range"); // 如果逻辑块号超出支持范围,则触发panic
}
释放inode
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
uint *a;
// 遍历直接块并释放它们
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]); // 释放块
ip->addrs[i] = 0; // 清空该位置的块地址
}
}
// 释放一级间接块及其包含的所有数据块
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]); // 读取一级间接块
a = (uint*)bp->data; // 将缓冲区数据转换为uint指针
for(j = 0; j < NINDIRECT; j++){
if(a[j])
bfree(ip->dev, a[j]); // 释放每个数据块
}
brelse(bp); // 释放缓冲区
bfree(ip->dev, ip->addrs[NDIRECT]); // 释放一级间接块本身
ip->addrs[NDIRECT] = 0; // 清空一级间接块地址
}
// 释放二级间接块及其包含的所有数据块
if(ip->addrs[NDIRECT+1]){
bp = bread(ip->dev, ip->addrs[NDIRECT+1]); // 读取二级间接块
a = (uint*)bp->data; // 将缓冲区数据转换为uint指针
for(j = 0; j < NINDIRECT; j++){
if(a[j]) {
struct buf *bp2 = bread(ip->dev, a[j]); // 读取一级间接块
uint *a2 = (uint*)bp2->data; // 将缓冲区数据转换为uint指针
for(int k = 0; k < NINDIRECT; k++){
if(a2[k])
bfree(ip->dev, a2[k]); // 释放每个数据块
}
brelse(bp2); // 释放缓冲区
bfree(ip->dev, a[j]); // 释放一级间接块本身
}
}
brelse(bp); // 释放缓冲区
bfree(ip->dev, ip->addrs[NDIRECT+1]); // 释放二级间接块本身
ip->addrs[NDIRECT + 1] = 0; // 清空二级间接块地址
}
ip->size = 0; // 将文件大小设为0
iupdate(ip); // 更新inode元数据
}
其中分配块的函数:
static uint
balloc(uint dev)
{
int b, bi, m; // b: 当前处理的块组起始块号, bi: 块组内的块索引, m: 位掩码
struct buf *bp; // 缓冲区指针,用于读取和写入磁盘块
bp = 0;
// 遍历所有块组
for(b = 0; b < sb.size; b += BPB){ // sb.size是文件系统的总块数,BPB是一个块组中的块数
bp = bread(dev, BBLOCK(b, sb)); // 从磁盘读取块组到缓冲区bp中
// 遍历当前块组中的每个块
for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
m = 1 << (bi % 8); // 计算位掩码,bi % 8是为了确定字节内的具体位位置
// 检查当前块是否空闲(即对应的位为0)
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
panic("balloc: out of blocks");
}
用户态文件
// user/bigfile
#include "kernel/types.h" // 包含基本类型定义
#include "kernel/stat.h" // 包含文件状态结构体定义
#include "user/user.h" // 包含用户空间函数声明(如printf, exit等)
#include "kernel/fcntl.h" // 包含文件控制选项定义(如O_CREATE, O_WRONLY等)
#include "kernel/fs.h" // 文件系统相关定义
int main()
{
char buf[BSIZE]; // 定义一个缓冲区buf,大小为块大小(BSIZE),用于读写操作
int fd, i, blocks; // fd是文件描述符;i是循环变量;blocks记录已写入的块数
// 打开或创建一个名为"big.file"的文件,以只写模式打开并创建(如果不存在)
fd = open("big.file", O_CREATE | O_WRONLY);
if(fd < 0){ // 如果打开文件失败
printf("bigfile: cannot open big.file for writing\n"); // 输出错误信息
exit(-1); // 程序退出
}
blocks = 0; // 初始化已写入的块数为0
while(1){
*(int*)buf = blocks; // 将当前块编号写入缓冲区的第一个整数位置
int cc = write(fd, buf, sizeof(buf)); // 尝试写入一个块的数据到文件中
if(cc <= 0) // 如果写入失败或没有写入任何数据
break; // 退出循环
blocks++; // 增加已写入块数计数
if (blocks % 100 == 0) // 每写入100个块输出一个点,作为进度指示
printf(".");
}
printf("\nwrote %d blocks\n", blocks); // 输出总共写入了多少块
if(blocks != 65803) { // 如果写入的块数不等于预期值65803
printf("bigfile: file is too small\n"); // 输出错误信息
exit(-1); // 程序退出
}
close(fd); // 关闭文件描述符fd
fd = open("big.file", O_RDONLY); // 重新以只读模式打开"big.file"
if(fd < 0){ // 如果打开文件失败
printf("bigfile: cannot re-open big.file for reading\n"); // 输出错误信息
exit(-1); // 程序退出
}
for(i = 0; i < blocks; i++){ // 循环读取所有块
int cc = read(fd, buf, sizeof(buf)); // 尝试从文件中读取一个块的数据
if(cc <= 0){ // 如果读取失败或没有读取任何数据
printf("bigfile: read error at block %d\n", i); // 输出错误信息
exit(-1); // 程序退出
}
if(*(int*)buf != i){ // 如果读取的数据与预期的块编号不符
printf("bigfile: read the wrong data (%d) for block %d\n",
*(int*)buf, i); // 输出错误信息
exit(-1); // 程序退出
}
}
printf("bigfile done; ok\n"); // 输出成功完成的信息
exit(0); // 正常退出程序
}
执行情况:
执行时间还挺久的:
Task 2 Symbolic links
符号链接,就是linux中的软链接,本质上还是一个文件,存有的内容是指向文件的路径。
**任务:**您将实现 symlink(char *target, char *path)
系统调用,该调用在引用 更改为由 Target 命名的文件。
要点:
- 首先,为 symlink 创建一个新的系统调用号,添加一个条目 添加到 user/usys.pl、user/user.h 中,并在 kernel/sysfile.c 中实现一个空sys_symlink。
- 将新的文件类型 (
T_SYMLINK
) 添加到 kernel/stat.h 到 表示符号链接。 - 在 kernel/fcntl.h 中添加一个新标志 (
O_NOFOLLOW
),该标志可以 与Open
System 调用一起使用。请注意:传递给open
的标志是使用按位 OR 运算符组合的,因此您的新 标志不应与任何现有标志重叠。这将让您 将 user/symlinktest.c 添加到 Makefile 后编译它。 - 实现
symlink(target, path)
系统调用来创建 在 path 处引用 target 的新符号链接。请注意,目标 不需要存在,系统调用即可成功。您将需要 要选择某个位置来存储符号链接的目标路径,请将 示例,在 inode 的数据块中。symlink
应返回一个整数 表示成功 (0) 或失败 (-1),类似于Link
和Unlink
。 - 修改
open
系统调用以处理路径 引用符号链接。如果文件不存在,则 open
必须失败。当进程在标志中指定O_NOFOLLOW
时 要打开
,open
应该打开符号链接(而不是 点击符号链接)。 - 如果链接的文件也是符号链接,则必须递归 一直按照它操作,直到到达非链接文件。如果链接形成一个循环, 您必须返回错误代码。您可以通过返回 如果链接深度达到某个阈值(例如,10),则为错误代码。
更改kernel/sysfile.c中实现
sys_symlink()
// sysfile.c
uint64
sys_symlink(void) {
char target[MAXPATH], path[MAXPATH]; // 定义两个字符数组用于存储目标路径和符号链接路径,MAXPATH为预定义的最大路径长度。
struct inode *ip; // 定义一个指向inode结构体的指针,inode通常用于文件系统中表示文件或目录。
int n; // 变量n用于存储函数argstr的返回值,即获取到的字符串长度。
// 使用argstr从用户空间参数获取目标路径和符号链接路径。如果任一操作失败,则返回-1。
if(n = argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0){
return -1;
printf("sys_symlink: argstr error\n"); // 注意:这里的printf实际上不会被执行,因为前面已经有return语句。
}
begin_op(); // 开始一个文件系统操作
// 创建一个新的inode,类型为T_SYMLINK(代表这是一个符号链接),权限设置为0(具体权限可能需要根据实际情况调整)。
// 如果创建失败,则结束操作并返回-1。
if((ip = create(path, T_SYMLINK, 0, 0)) == 0){
end_op();
return -1;
}
// 将目标路径写入新创建的符号链接inode的数据块中。如果写入的字节数与预期不符,则释放inode并返回错误。
if(writei(ip, 0, target, 0, n) != n){
iunlockput(ip); // 解锁并释放inode
end_op(); // 结束操作
return -1;
printf("sys_symlink: writei error\n"); // 同样的,这个printf也不会被执行。
}
// 如果所有操作都成功完成,则解锁并释放inode,然后结束操作,最后返回0表示成功。
iunlockput(ip);
end_op();
return 0;
}
可能这样还是不好理解,我们带入实际路径更好理解一些,以下是他的执行流程:
场景描述:
- 目标路径(target):
/home/user/documents/report.txt
- 符号链接路径(path):
/home/user/desktop/report_link
我们的目的是在桌面上创建一个名为 report_link
的符号链接,它指向位于/home/user/documents/report.txt
的报告文件。
代码执行流程
-
获取参数:
当调用
sys_symlink()
时,首先需要从用户空间获取两个字符串参数:目标路径和符号链接路径。这里,argstr(0, target, MAXPATH)
和argstr(1, path, MAXPATH)
分别获取这两个路径,并存储在target
和path
变量中。 -
检查获取的参数:
如果任一参数获取失败(例如,路径过长或不存在),函数将返回
-1
并结束。在这个例子中,假设两个路径都是有效的,则继续执行。 -
开始文件系统操作:
调用
begin_op()
开始一个新的文件系统操作事务。这是确保文件系统的一致性的一种方式。 -
创建新的inode:
使用
create(path, T_SYMLINK, 0, 0)
在指定的路径(即/home/user/desktop/report_link
)下创建一个新的 inode,类型为符号链接(T_SYMLINK)。这个步骤成功后,返回新创建的 inode 指针ip
。 -
写入目标路径到inode:
使用
writei(ip, 0, target, 0, n)
将目标路径(/home/user/documents/report.txt
)写入新创建的符号链接inode的数据块中。如果写入过程中出现错误(比如磁盘满),则释放inode并返回-1
。 -
完成操作:
如果所有步骤都成功,调用
iunlockput(ip)
解锁并释放inode资源,并通过end_op()
结束文件系统操作事务。最后,函数返回0
表示成功创建了符号链接。
修改 kernel/sysfile
的 sys_open()
函数
符号链接是为了打开文件服务的,打开符号链接需要注意以下方面:
- 一个符号文件自己调用自己导致无限的循环没有意义且耗费资源
- 循环检测对符号链接深度进行检测
- 在深度检测范围内跟踪到真正的文件
关键函数
-
目标路径(target):
/home/user/documents/report.txt
-
符号链接路径(path):
/home/user/desktop/report_link
-
readi() 函数是读出目标文件的路径 ,即
/home/user/documents/report.txt
-
namei() 函数 读出目标路径对应的inode
-
O_NOFOLLOW
标志的作用是防止符号链接的自动解析,从而避免潜在的安全风险。
此函数用于检测符号链接相关解析的问题:
// 定义一个静态函数,用于解析符号链接
static struct inode* follow_symlink(struct inode* ip) {
// 定义一个数组,用于存储已经访问过的 inode 编号,以检测循环引用
uint inums[NSYMLINK];
int i, j; // 循环变量
char target[MAXPATH]; // 用于存储符号链接指向的目标路径
// 循环解析符号链接,最多解析 NSYMLINK 次,防止无限循环
for(i = 0; i < NSYMLINK; ++i) {
// 将当前 inode 的编号存储在 inums 数组中
inums[i] = ip->inum;
// 从符号链接文件中读取目标路径
// 参数解释:
// ip: 当前 inode 指针,表示要读取的符号链接文件
// 0: user_dst 标志,通常为 0 表示内核空间
// (uint64)target: 目标缓冲区的地址,将读取的数据存储在 target 数组中
// 0: 从文件的偏移量 0 开始读取
// MAXPATH: 要读取的最大字节数
if(readi(ip, 0, (uint64)target, 0, MAXPATH) <= 0) {
// 如果读取失败,释放当前 inode 并输出错误信息
iunlockput(ip);
printf("open_symlink: open symlink failed\n");
return 0; // 返回空指针,表示解析失败
}
// 释放当前 inode,避免持有不必要的锁
iunlockput(ip);
// 根据读取到的目标路径获取对应的 inode
// namei 函数用于根据路径名查找 inode
if((ip = namei(target)) == 0) {
// 如果目标路径不存在,输出错误信息并返回
printf("open_symlink: path \"%s\" is not exist\n", target);
return 0;
}
// 检查新获取的 inode 是否已经在 inums 数组中,防止循环引用
for(j = 0; j <= i; ++j) {
if(ip->inum == inums[j]) {
// 如果发现循环引用,输出错误信息并返回
printf("open_symlink: links form a cycle\n");
return 0;
}
}
// 锁定新的 inode,以便后续操作
ilock(ip);
// 检查新的 inode 是否仍然是一个符号链接
if(ip->type != T_SYMLINK) {
// 如果不是符号链接,解析完成,返回该 inode
return ip;
}
// 如果仍是符号链接,继续循环解析
}
// 如果超过了最大解析深度,释放 inode 并输出错误信息
iunlockput(ip);
printf("open_symlink: the depth of links reaches the limit\n");
return 0; // 返回空指针,表示解析失败
}
接下来是完善sys_open函数
uint64
sys_open(void)
{
char path[MAXPATH];
int fd, omode;
struct file *f;
struct inode *ip;
int n;
if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
return -1;
begin_op();
if(omode & O_CREATE){
ip = create(path, T_FILE, 0, 0);
if(ip == 0){
end_op();
return -1;
}
} else {
if((ip = namei(path)) == 0){
end_op();
return -1;
}
ilock(ip);
if(ip->type == T_DIR && omode != O_RDONLY){
iunlockput(ip);
end_op();
return -1;
}
}
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
return -1;
}
// handle the symlink - lab9-2
if(ip->type == T_SYMLINK && (omode & O_NOFOLLOW) == 0) {
if((ip = follow_symlink(ip)) == 0) {
end_op();
return -1;
}
}
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
if(f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}
if(ip->type == T_DEVICE){
f->type = FD_DEVICE;
f->major = ip->major;
} else {
f->type = FD_INODE;
f->off = 0;
}
f->ip = ip;
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
if((omode & O_TRUNC) && ip->type == T_FILE){
itrunc(ip);
}
iunlock(ip);
end_op();
return fd;
}
用户态测试文件symlinktest.c:
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/riscv.h"
#include "kernel/fcntl.h"
#include "kernel/spinlock.h"
#include "kernel/sleeplock.h"
#include "kernel/fs.h"
#include "kernel/file.h"
#include "user/user.h"
// 宏定义用于简化错误处理
#define fail(msg) do {printf("FAILURE: " msg "\n"); failed = 1; goto done;} while (0);
static int failed = 0;
// 函数声明
static void testsymlink(void);
static void concur(void);
static void cleanup(void);
int main(int argc, char *argv[])
{
// 清理之前的测试数据
cleanup();
// 执行符号链接功能测试
testsymlink();
// 执行并发符号链接测试
concur();
// 根据failed标志决定程序退出状态
exit(failed);
}
// 清理测试相关的路径和文件
static void cleanup(void)
{
unlink("/testsymlink/a");
unlink("/testsymlink/b");
unlink("/testsymlink/c");
unlink("/testsymlink/1");
unlink("/testsymlink/2");
unlink("/testsymlink/3");
unlink("/testsymlink/4");
unlink("/testsymlink/z");
unlink("/testsymlink/y");
unlink("/testsymlink");
}
// 使用O_NOFOLLOW选项stat一个符号链接
static int stat_slink(char *pn, struct stat *st)
{
int fd = open(pn, O_RDONLY | O_NOFOLLOW); // 不跟随符号链接打开文件
if(fd < 0)
return -1;
if(fstat(fd, st) != 0) // 获取文件状态信息
return -1;
return 0;
}
// 测试符号链接功能
static void testsymlink(void)
{
int r, fd1 = -1, fd2 = -1;
char buf[4] = {'a', 'b', 'c', 'd'};
char c = 0, c2 = 0;
struct stat st;
printf("Start: test symlinks\n");
mkdir("/testsymlink"); // 创建目录
fd1 = open("/testsymlink/a", O_CREATE | O_RDWR); // 创建文件
if(fd1 < 0) fail("failed to open a");
r = symlink("/testsymlink/a", "/testsymlink/b"); // 创建符号链接
if(r < 0)
fail("symlink b -> a failed");
if(write(fd1, buf, sizeof(buf)) != 4) // 写入数据到文件
fail("failed to write to a");
if(stat_slink("/testsymlink/b", &st) != 0) // 检查符号链接的状态
fail("failed to stat b");
if(st.type != T_SYMLINK)
fail("b isn't a symlink");
fd2 = open("/testsymlink/b", O_RDWR); // 打开符号链接指向的文件
if(fd2 < 0)
fail("failed to open b");
read(fd2, &c, 1); // 读取数据
if(c != 'a')
fail("failed to read bytes from b");
unlink("/testsymlink/a"); // 删除原文件
if(open("/testsymlink/b", O_RDWR) >= 0) // 尝试打开已删除原文件的符号链接
fail("Should not be able to open b after deleting a");
r = symlink("/testsymlink/b", "/testsymlink/a"); // 创建循环符号链接
if(r < 0)
fail("symlink a -> b failed");
r = open("/testsymlink/b", O_RDWR); // 尝试打开循环符号链接
if(r >= 0)
fail("Should not be able to open b (cycle b->a->b->..)\n");
r = symlink("/testsymlink/nonexistent", "/testsymlink/c"); // 创建指向不存在文件的符号链接
if(r != 0)
fail("Symlinking to nonexistent file should succeed\n");
// 创建一系列符号链接链
r = symlink("/testsymlink/2", "/testsymlink/1");
if(r) fail("Failed to link 1->2");
r = symlink("/testsymlink/3", "/testsymlink/2");
if(r) fail("Failed to link 2->3");
r = symlink("/testsymlink/4", "/testsymlink/3");
if(r) fail("Failed to link 3->4");
close(fd1);
close(fd2);
fd1 = open("/testsymlink/4", O_CREATE | O_RDWR); // 创建文件
if(fd1<0) fail("Failed to create 4\n");
fd2 = open("/testsymlink/1", O_RDWR); // 打开符号链接指向的文件
if(fd2<0) fail("Failed to open 1\n");
c = '#';
r = write(fd2, &c, 1); // 写入数据
if(r!=1) fail("Failed to write to 1\n");
r = read(fd1, &c2, 1); // 读取数据
if(r!=1) fail("Failed to read from 4\n");
if(c!=c2)
fail("Value read from 4 differed from value written to 1\n");
printf("test symlinks: ok\n");
done:
close(fd1);
close(fd2);
}
// 并发测试符号链接
static void concur(void)
{
int pid, i;
int fd;
struct stat st;
int nchild = 2;
printf("Start: test concurrent symlinks\n");
fd = open("/testsymlink/z", O_CREATE | O_RDWR); // 创建文件
if(fd < 0) {
printf("FAILED: open failed");
exit(1);
}
close(fd);
for(int j = 0; j < nchild; j++) {
pid = fork(); // 创建子进程
if(pid < 0){
printf("FAILED: fork failed\n");
exit(1);
}
if(pid == 0) {
int m = 0;
unsigned int x = (pid ? 1 : 97);
for(i = 0; i < 100; i++){
x = x * 1103515245 + 12345; // 伪随机数生成
if((x % 3) == 0) {
symlink("/testsymlink/z", "/testsymlink/y"); // 创建符号链接
if(stat_slink("/testsymlink/y", &st) == 0) {
m++;
if(st.type != T_SYMLINK) {
printf("FAILED: not a symbolic link\n", st.type);
exit(1);
}
}
} else {
unlink("/testsymlink/y"); // 删除符号链接
}
}
exit(0);
}
}
int r;
for(int j = 0; j < nchild; j++) {
wait(&r); // 等待子进程结束
if(r != 0) {
printf("test concurrent symlinks: failed\n");
exit(1);
}
}
printf("test concurrent symlinks: ok\n");
}
参考资料
重复造轮子虽没有必要,但能够加深自己的思考,以下博客写的更为详尽,感谢分享自己所学的人!
MIT 6.S081] Lab 9: file system_操作系统 mit 6.s081 lab9: file system-CSDN博客
mit6.s081] 笔记 Lab9: File System | 文件系统 | Miigon’s blog
Lab: file system