当前位置: 首页 > article >正文

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),类似于 LinkUnlink
  • 修改 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 的报告文件。

代码执行流程
  1. 获取参数:

    当调用 sys_symlink() 时,首先需要从用户空间获取两个字符串参数:目标路径和符号链接路径。这里,argstr(0, target, MAXPATH)argstr(1, path, MAXPATH) 分别获取这两个路径,并存储在 targetpath 变量中。

  2. 检查获取的参数:

    如果任一参数获取失败(例如,路径过长或不存在),函数将返回 -1 并结束。在这个例子中,假设两个路径都是有效的,则继续执行。

  3. 开始文件系统操作:

    调用 begin_op() 开始一个新的文件系统操作事务。这是确保文件系统的一致性的一种方式。

  4. 创建新的inode:

    使用 create(path, T_SYMLINK, 0, 0) 在指定的路径(即 /home/user/desktop/report_link)下创建一个新的 inode,类型为符号链接(T_SYMLINK)。这个步骤成功后,返回新创建的 inode 指针 ip

  5. 写入目标路径到inode:

    使用 writei(ip, 0, target, 0, n) 将目标路径(/home/user/documents/report.txt)写入新创建的符号链接inode的数据块中。如果写入过程中出现错误(比如磁盘满),则释放inode并返回 -1

  6. 完成操作:

    如果所有步骤都成功,调用 iunlockput(ip) 解锁并释放inode资源,并通过 end_op() 结束文件系统操作事务。最后,函数返回 0 表示成功创建了符号链接。

修改 kernel/sysfilesys_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


http://www.kler.cn/a/488270.html

相关文章:

  • jeecg-boot 表单选择一条数据保存
  • 深入学习Headers Exchange交换机
  • 打桩机:灾害救援中的 “应急尖兵”,稳固支撑的保障|鼎跃安全
  • 解锁无证身份核验:开启便捷安全新征程
  • 专精特新申报条件
  • 了解RabbitMQ的工作原理
  • tdengine数据库使用java连接
  • 使用 Docker 构建 preboot 交叉编译环境
  • 数据集-目标检测系列- 电话 测数据集 call_phone >> DataBall
  • Nginx安全加固系列:防范XSS
  • QEMU通过OVS实现联网
  • 计算机网络之---信号与编码
  • HDFS编程 - 使用HDFS Java API进行文件操作
  • 一、智能体强化学习——强化学习基础
  • component-动态控制 div width 的值 根据传入的变量决定width的值 vue
  • Qt重写webrtc的demo peerconnection
  • 【原型设计】Axure快速入门教程
  • 机器学习实战——决策树:从原理到应用的深度解析
  • 【SOC 芯片设计 DFT 学习专栏 -- RTL 中的信号名和 Netlist 中的信号名差异】
  • d2j-dex2jar classes.dex 执行报错:not support version 问题解决