MIT6.s081_Lab9 fs: File system

MIT6.s081 Lab9:file system

不太顺利的一个Lab,Large files写的还行,写Symbolic links卡了很久,全是细节。

代码
这里补充一点,xv6里的cache似乎是在disk中,我理解的是在内存中,好像现在有那种带缓存的固态和不带的缓存的固态,可能区别在这里。

1. Large files

每个inode中有文件的一些信息,真正存放文件的地方放在inodeaddr中,addr存放块的块号,xv6addr数组大小为13,其中12个为直接索引,1个位间接索引,要创建更大的文件,增加块数即可。实验要求是将其中一个直接索引换成二级间接索引。

  1. 修改参数。

    #define NDIRECT 11
    #define NINDIRECT (BSIZE / sizeof(uint))
    #define NNINDIRECT ((BSIZE / sizeof(uint)) * (BSIZE / sizeof(uint)))
    #define MAXFILE (NDIRECT + NINDIRECT + NNINDIRECT)
    
    struct dinode {
    ……          
      uint addrs[NDIRECT+2];   // Data block addresses
    };
    
    struct inode {
    ……
      uint addrs[NDIRECT+2];
    };
    
  2. 修改bmap,增加映射空间,这里耽误了一点时间,忘记了将bp写入log,还是细节问题,对文件系统不够熟悉。

    static uint
    bmap(struct inode *ip, uint bn)
    {
      ……
      if(bn < NINDIRECT){
        ……
      }
      bn -= NINDIRECT;
      if(bn < NNINDIRECT){
        if((addr = ip->addrs[NDIRECT + 1]) == 0)
          ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
        bp = bread(ip->dev, addr);
        int indirect = (int)(bn / NINDIRECT);
        a = (uint*)bp->data;
        if((addr = a[indirect]) == 0){
          a[indirect] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        bp = bread(ip->dev, addr);
        bn %= NINDIRECT;
        a = (uint*)bp->data;
        if((addr = a[bn]) == 0){
          a[bn] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        return addr;
      }
    
      panic("bmap: out of range");
    }
    
  3. 修改itrunc,主要是问了清除文件的时候也能将大文件的二级映射也清除,逻辑和前面清除一级的很像。

    void
    itrunc(struct inode *ip)
    {
      int i, j, k;
      struct buf *bp, *ibp;
      uint *a,*b;
    
      for(i = 0; i < NDIRECT; i++){
        ……
      }
    
      if(ip->addrs[NDIRECT]){
        ……
      }
    
      if(ip->addrs[NDIRECT + 1]){
        bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
        a = (uint*)bp->data;
        for(j = 0; j < NINDIRECT; j++){
          ibp = bread(ip->dev, a[j]);
          b = (uint*)ibp->data;
          for(k = 0; k < NINDIRECT; k++) {
            if(b[k])
              bfree(ip->dev, b[k]);
          }
          brelse(ibp);
          bfree(ip->dev, a[j]);
        }
        brelse(bp);
        bfree(ip->dev, ip->addrs[NDIRECT + 1]);
        ip->addrs[NDIRECT + 1] = 0;
      }
    
      ip->size = 0;
      iupdate(ip);
    }
    

这个问题需要对fs.c中的函数了解有一定程度,否则写起来比较吃力,也不算困难,但是每次错误的释放都很关键。就像实验开头说的,可以理解路径名查找的工作原理。

  1. 先添加一些参数和函数信息(做到这里应该是比较简单了)。

    Makefile

    $U/_symlinktest\
    

    kernel/fcntl.h

    #define O_NOFOLLOW 0x800
    

    kernel/stat.h

    #define T_SYMLINK 4   // Symlink
    

    kernel/syscall.c

    extern uint64 sys_symlink(void);
    [SYS_symlink] sys_symlink,
    

    kernel/syscall.h

    #define SYS_symlink 22
    

    user/user.h

    int symlink(char *target, char *path);
    

    user/usys.pl

    entry("symlink");
    
  2. 剩下的就是增加这个系统调用的函数本体,以及修改open这个系统调用了,刚开始不熟悉,去看了看源码,然后回来重写的,一直在死锁,以为是sys_symlink实现的有问题,没想到两次都出现在open的问题上。

    sys_symlink实现,target是链接的目标路径,存放在文件的第一个块中,path就是创建的软链接路径,name是文件的名字。这里来来回回改了几遍,也算比较熟悉了。

    uint64
    sys_symlink(void)
    {
      char path[MAXPATH], target[MAXPATH], name[DIRSIZ];
      struct inode *ip, *dp;
      int n;
      
      //提取参数
      if((n = argstr(0, target, MAXPATH)) < 0 || argstr(1, path, MAXPATH) < 0)
        return -1;
    
      begin_op();//创建事务
      if((dp = nameiparent(path, name)) == 0){
          //获取父目录inode,和名字name,这里增加了父目录的引用个数
        end_op();//这里获取父目录失败了,直接结束事务
        return -1;
      }
      ilock(dp);//对父目录上锁,
      if((ip = dirlookup(dp, name, 0)) != 0){//查看目录下是否有同名文件
        iunlockput(dp);//释放父目录锁并减少引用计数
        iput(ip);//有同名文件就增加了一次引用,并不需要,所以减少一次
        end_op();//别忘记结束事务
        return -1;
      }
      if((ip = ialloc(dp->dev, T_SYMLINK)) == 0){
          //分配一个inode,这里的dinode信息是不加载到内存中的,同时valid是0
        iunlockput(dp); // 分配失败,记得释放 dp,减少引用
        end_op();//结束事务
        return -1;
      }
      ilock(ip);//对inode上锁,如果valid为0,加载dinode信息到内存
      ip->major = 0;
      ip->minor = 0;
      ip->nlink = 1;
      if(writei(ip, 0, (uint64)target, 0, n) <= 0){//写入路径到第一个块
        ip->nlink = 0;//写入目标路径失败,释放文件
        iupdate(ip);//更新log中的dinode,主要有logwrite这一步,写到磁盘
        iunlockput(ip);//解锁减少引用
        iunlockput(dp);
        end_op();
        return -1;
      }
      if(dirlink(dp, name, ip->inum) < 0){//将文件(name,inode号)放到目录中
        ip->nlink = 0;
        iupdate(ip);
        iunlockput(ip);
        iunlockput(dp);
        end_op();
        return -1;
      }
      iupdate(ip);//这个update放到ip->nlink = 1;之后也可以
      iunlockput(ip);
      iunlockput(dp);
      end_op();
      
      return 0;
    }
    

    修改sys_open,因为现在需要打开软链接的文件,有两种情况,一种是直接打开软链接,数据就是target path,另一种是打开软链接指向的文件,这里需要处理一下,打开软链接指向的文件。

    增加下面这一段

      if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
          //判断不是nofollow,就代表要读取指向的文件
        struct buf *bp;
        uint accessed_inode[30];//记录访问过的dinode,防止出现循环。这里只设置了30
        int i = 0;
        accessed_inode[i++] = ip->inum;//先记录当前的文件的dinode号
        while(ip->type == T_SYMLINK) {//循环到最后的指向的文件
          uint addr = ip->addrs[0];//获取第一个块。
          bp = bread(ip->dev, addr);//读取块的内容到buf,同时获取睡眠锁
          iunlockput(ip);//释放ip的锁,并减少引用,因为下面要更新ip了
          if((ip = namei((char*)bp->data)) == 0){//获取路径的inode
            brelse(bp);
            end_op();
            return -1;
          }
          brelse(bp);
            //注意释放buf的引用,这里就除了问题,如果不释放的话,这个buf就会一直锁死
          ilock(ip);//获取新的inode的锁,防止其他文件访问
          for(int j = 0; j < i; j++) {//判断是否有循环出现,出现了就退出
            if(ip->inum == accessed_inode[j]){//这里注意资源释放,第二次出问题
                //之前直接返回-1,没有对已经上锁的inode释放锁,也没有结束事务,导致死锁
              iunlockput(ip);
              end_op();
              return -1;
            }
          }
          accessed_inode[i++] = ip->inum;//增加访问记录
        }
      }
    

    主要还是细节问题,对file system有一定的了解后还是不难的。

3. 附录:fs.c主要函数

先说明一下,inode是内存中的文件存在形式,dinode才是存放在磁盘中的数据。inum一般是dinode在磁盘中的位置。

下面的都是AI写的,自己手写的懒得打了,而且文笔没有AI好。lab9_note_write自看。

xv6 kernel/fs.c 函数详解

fs.c 文件负责管理文件系统的磁盘布局。它处理底层的块分配、inode 分配,并实现从inode 读写数据等核心功能。

关键磁盘数据结构 (引用自 kernel/fs.h)

在分析函数之前,必须了解它们操作的核心磁盘结构:

  • struct superblock sb: 文件系统的“元数据”,位于磁盘的第1块。它记录了文件系统的总大小、数据块数量、inode 数量以及日志区的起始位置。
  • struct dinode: inode 在磁盘 (Disk) 上的表示形式。它包含了文件的类型、大小、硬链接数以及指向数据块的地址数组 (addrs[])。
  • 位图 (Bitmaps): 用于跟踪 inode 和数据块的分配情况。某一位是 1 表示对应的块/inode 已被使用,0 表示空闲。

主要函数分析

1. readsb(int dev, struct superblock *sb)

  • 作用: 读取超级块 (Read Superblock)。
  • 参数:
    • int dev: 设备号,指明要从哪个磁盘设备读取。
    • struct superblock *sb: 一个指向内存中 superblock 结构体的指针,函数会将从磁盘读取的数据填充到这里。
  • 核心逻辑:
    1. 调用 bread(dev, 1) 从设备的第1块(超级块所在的位置)读取数据到缓冲区。
    2. 使用 memmove() 将缓冲区中的数据拷贝到 sb 指针指向的结构体中。
    3. 释放缓冲区 (brelse)。
  • 引用: bread(), brelse() (来自 bio.c)。

2. fsinit(int dev)

  • 作用: 初始化文件系统 (File System Initialize)。
  • 参数:
    • int dev: 要初始化的文件系统所在的设备号。
  • 核心逻辑:
    1. 调用 readsb() 读取超级块,了解文件系统的整体布局。
    2. 调用 initlog(dev, &sb) 初始化日志系统,将超级块中的日志信息传递给日志层。

3. balloc(uint dev)

  • 作用: 分配一个空闲的数据块 (Block Allocate)。
  • 参数:
    • uint dev: 设备号。
  • 核心逻辑:
    1. 在一个循环中,遍历磁盘上所有的块位图 (block bitmap) 块。
    2. 对于每一个位图块,调用 bread() 将其读入内存。
    3. 在位图块内部,逐位检查,寻找一个值为 0 的位(表示空闲)。
    4. 一旦找到,将该位置 1(标记为已使用)。
    5. 调用 log_write() 将修改后的位图块写入日志。
    6. 释放位图块的缓冲区 (brelse)。
    7. 返回新分配的数据块的块号
  • 引用: BBLOCK() (计算位图块位置的宏), bread(), log_write(), brelse()

4. bfree(int dev, uint b)

  • 作用: 释放一个数据块 (Block Free)。
  • 参数:
    • int dev: 设备号。
    • uint b: 要释放的数据块的块号。
  • 核心逻辑:
    1. 根据块号 b,使用 BBLOCK() 宏计算出它在哪个位图块中。
    2. bread() 读取该位图块。
    3. 检查块 b 对应的位是否为 1(如果是 0 则说明发生了错误,panic)。
    4. 将该位置 0(标记为空闲)。
    5. log_write() 将修改后的位图块写入日志。
    6. brelse() 释放缓冲区。

5. ialloc(uint dev, short type)

  • 作用: 分配一个 inode (Inode Allocate)。
  • 参数:
    • uint dev: 设备号。
    • short type: 要创建的 inode 的类型(如 T_FILE, T_DIR)。
  • 核心逻辑:
    1. balloc 类似,遍历所有的 inode 位图块,寻找一个空闲的 inode 位。
    2. 找到后,获取其inode 编号 (inum)
    3. 调用 iget(dev, inum) 从 inode 缓存中获取一个内存中的 inode 结构体(此时是新分配的,内容为空)。
    4. 初始化这个 inode 的元数据:设置 type, nlink=1, size=0 等。
    5. 调用 iupdate(ip) 将这个新的 inode 信息写回磁盘。
    6. 返回这个内存中的 inode 指针。
  • 引用: I_BUSY (inode 状态), iget(), iupdate() (来自 inode.c)。

6. iupdate(struct inode *ip)

  • 作用: 将内存中 inode 的改动写回磁盘 (Inode Update)。
  • 参数:
    • struct inode *ip: 指向内存中被修改过的 inode
  • 核心逻辑:
    1. 根据 ip->inum 计算出这个 inode 在磁盘上属于哪个块。
    2. bread() 读取该块。
    3. 在块内找到对应的 dinode 结构体的位置。
    4. 将内存 inode (ip) 的内容拷贝到磁盘 dinode 结构体中。
    5. log_write() 将修改后的块写入日志。
    6. brelse() 释放缓冲区。

7. itrunc(struct inode *ip)

  • 作用: 截断一个文件,即释放其所有数据块 (Inode Truncate)。
  • 参数:
    • struct inode *ip: 要被截断的文件的 inode。
  • 核心逻辑:
    1. 遍历 inode 的 addrs[] 数组中的所有直接数据块指针,对每一个有效的指针调用 bfree()
    2. 如果存在间接块bread() 读取它,然后遍历其中的所有指针,对每一个都调用 bfree()。最后再 bfree() 这个间接块本身。
    3. 将 inode 的 size 设置为0。
    4. 调用 iupdate() 将 inode 的变动(size 和 addrs 都被清空)写回磁盘。

8. stati(struct inode *ip, struct stat *st)

  • 作用: 获取 inode 的状态信息 (Status of Inode)。
  • 参数:
    • struct inode *ip: 源 inode。
    • struct stat *st: 目标结构体,用于返回给用户空间。
  • 核心逻辑: 这是一个简单的结构体拷贝过程,将 ip 中的 dev, inum, type, nlink, size 等信息,拷贝到 st 结构体对应的字段中。

9. readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)

  • 作用: 从 inode 读取数据 (Read from Inode)。
  • 参数:
    • struct inode *ip: 从哪个 inode 读取。
    • int user_dst: 标志位,1 表示 dst 是用户虚拟地址,0 表示是内核地址。
    • uint64 dst: 目标地址。
    • uint off: 从文件的哪个偏移量开始读。
    • uint n: 要读取的字节数。
  • 核心逻辑:
    1. 在一个循环中,根据当前的偏移量 off 计算出它属于文件的第几个逻辑块。
    2. 调用 bmap(ip, ...) 将逻辑块号翻译成物理磁盘块号。
    3. 如果 bmap 成功,bread() 读取该数据块。
    4. 计算出在块内的起始读取位置和本次能读取的字节数。
    5. 调用 either_copyout()(如果 user_dst 为真)或 memmove()(如果为假)将数据从缓冲区拷贝到目标地址 dst
    6. 更新 offn,继续循环直到读取完毕。

10. writei(struct inode *ip, int user_src, uint64 src, uint off, uint n)

  • 作用: 向 inode 写入数据 (Write to Inode)。
  • 参数:
    • struct inode *ip: 写入哪个 inode。
    • int user_src: 标志位,1 表示 src 是用户虚拟地址。
    • uint64 src: 源数据地址。
    • uint off: 写入文件的哪个偏移量。
    • uint n: 要写入的字节数。
  • 核心逻辑:
    1. readi 类似,在一个循环中根据 off 计算逻辑块号。
    2. 调用 bmap(ip, ...) 翻译块号。关键区别:如果 bmap 发现该逻辑块尚未分配,它会内部调用 balloc() 分配一个新块并更新 inode
    3. bread() 读取该数据块。
    4. 调用 either_copyin()memmove() 将源数据 src 拷贝到缓冲区中。
    5. log_write() 将修改后的数据块写入日志。
    6. 更新 offn,继续循环。
    7. 循环结束后,如果文件大小 ip->size 增加了,调用 iupdate() 更新 inode。

11. bmap(struct inode *ip, uint bn)

  • 作用: 将 inode 内的逻辑块号映射到物理块号 (Block Map)。
  • 参数:
    • struct inode *ip: 在哪个 inode 中查找。
    • uint bn: 逻辑块号 (Logical Block Number)。
  • 核心逻辑:
    1. 如果 bn 在直接块的范围内,直接从 ip->addrs[bn] 读取物理块号。如果该块尚未分配,则调用 balloc() 分配一个,更新 ip->addrs[bn],并标记 inode 为脏。
    2. 如果 bn 在间接块的范围内,先读取间接块,然后在间接块中查找对应的物理块号。如果间接块或最终的数据块未分配,同样调用 balloc() 进行分配。
    3. 返回最终的物理块号。

12. skipelem(char *path, char *name) (位于 kernel/path.c)

  • 作用: 这是一个路径解析的辅助工具。它的功能是从一个完整的路径字符串中,跳过并提取出第一个路径元素(文件名或目录名)
  • 参数:
    • char *path: 指向当前正在解析的完整路径字符串的指针。例如 "/usr/bin/cat"
    • char *name: 一个输出缓冲区,函数会将提取出的路径元素(不含/)拷贝到这里。
  • 核心逻辑:
    1. 跳过前导斜杠: 首先,它会跳过 path 字符串开头的所有 / 字符。
    2. 提取元素: 接着,它开始逐字符地将 path 的内容拷贝到 name 缓冲区,直到遇到下一个 / 或者字符串的结尾 (\0) 为止。
    3. 返回剩余路径: 函数的返回值是一个指针,指向 path 字符串中紧跟在被提取元素之后的位置。
  • 示例:
    • 如果调用 skipelem("/usr/bin/cat", name):
      • name 缓冲区会被填充为 "usr"
      • 函数会返回一个指向 "bin/cat" 的指针。
    • 如果再次用返回的指针调用 skipelem("bin/cat", name):
      • name 会被填充为 "bin"
      • 函数会返回一个指向 "cat" 的指针。
  • 引用: 这个函数是路径解析函数 namex() 的核心组成部分,本身不直接引用其他文件系统函数。

13. dirlookup(struct inode *dp, char *name, uint *poff) (位于 kernel/dir.c)

  • 作用: 在一个指定的目录中,查找一个具有特定名称的文件或子目录,并返回其对应的内存 inode
  • 参数:
    • struct inode *dp: 指向要被搜索的目录 (directory) 的 inode 指针。d 代表 directory。
    • char *name: 要查找的文件或目录的名称,例如 "cat"
    • uint *poff: 一个可选的输出参数。如果非空,函数在找到目录项时,会把该目录项在目录文件中的字节偏移量存入 poff 指向的变量。
  • 核心逻辑:
    1. 权限检查: 首先,函数会检查 dp->type 是否确实为 T_DIR,确保我们正在搜索的是一个目录而不是一个普通文件。
    2. 遍历目录项: 目录的内容是一个 struct dirent (目录项) 的列表。函数在一个循环中,使用 readi() 逐块读取目录的数据。
    3. 比较名称: 在每个块中,它会遍历所有的 dirent 结构。对于每一个有效的目录项 (de.inum != 0),它会使用 strncmp() 将其名称 de.name 与要查找的 name 进行比较。
    4. 找到匹配: 如果找到了一个名称匹配的目录项:
      • 如果 poff 非空,就记录下当前的偏移量。
      • 使用 iget(dp->dev, de.inum) 根据找到的 inode 编号,获取其在内存中的 inode 结构。
      • 返回这个 inode 指针。
    5. 未找到: 如果遍历完所有目录项都没有找到匹配的名称,函数返回 0 (NULL)。
  • 引用: readi() (来自 fs.c), iget() (来自 inode.c)。

14. dirlink(struct inode *dp, char *name, uint inum) (位于 kernel/dir.c)

  • 作用: 在一个指定的目录中,创建一个新的链接。这个操作会在目录的数据中增加一个新的目录项,将一个名称和一个 inode 编号关联起来。这是 create, mkdir, link 等系统调用的基础。
  • 参数:
    • struct inode *dp: 指向要添加新链接的目录的 inode 指针。
    • char *name: 要创建的新链接的名称
    • uint inum: 该名称应该链接到的 inode 编号
  • 核心逻辑:
    1. 检查名称是否已存在: 函数首先会调用 dirlookup() 来检查 name 是否已经存在于目录 dp 中。如果存在,说明发生了错误(比如重复创建文件),函数会返回失败。
    2. 寻找空闲槽位: 接着,函数会再次遍历目录 dp 的所有数据块,这次是为了寻找一个空的目录项(即 de.inum == 0 的条目)。
    3. 创建新链接: 一旦找到一个空槽位:
      • 它会将 name 拷贝到 de.name 中。
      • 它会将 inum 赋值给 de.inum
      • 它会调用 writei() 将这个被修改过的、包含了新目录项的数据块写回磁盘。
      • 成功后,返回 0
    4. 目录已满: 如果遍历完整个目录都没有找到空槽位,说明目录已满,无法创建新文件,函数返回失败。
  • 引用: dirlookup(), writei() (来自 fs.c)。

15. namex(char *path, int nameiparent, char *name) (内部核心函数)

  • 作用: 这是一个通用的路径解析引擎。它的功能是根据给定的 path,从一个起始点(根目录或当前工作目录)开始,逐级地在目录中查找,直到解析完整个路径。根据 nameiparent 标志,它最终会返回路径末端目标或其父目录的 inode。
  • 参数:
    • char *path: 要解析的完整路径字符串,例如 "/a/b/c.txt"
    • int nameiparent: 一个标志位。
      • 如果为 0,函数会解析到路径的最后一个元素并返回其 inode(例如,返回 c.txt 的 inode)。
      • 如果为 1,函数会解析到路径的倒数第二个元素(即父目录),并返回父目录的 inode(例如,返回 b 的 inode)。
    • char *name: 一个输出缓冲区。
      • 如果 nameiparent1,函数会将路径的最后一个元素("c.txt")拷贝到 name 中。
  • 核心逻辑: 这是一个迭代查找的过程。
    1. 确定起点: 函数首先判断路径是绝对路径(以 / 开头)还是相对路径。
      • 如果是绝对路径,它从根目录 inode (iget(ROOTDEV, ROOTINO)) 开始查找。
      • 如果是相对路径,它从当前进程的工作目录 (myproc()->cwd) 开始查找。
    2. 进入循环: 函数进入一个 while 循环,只要路径还没解析完,循环就继续。
    3. 提取路径元素: 在循环的每一次迭代中,它调用 skipelem(path, name) 来从当前路径中提取出下一个要查找的目录或文件名(例如,第一次是 "a",第二次是 "b")。
    4. 加锁与权限检查:
      • 它调用 ilock(ip) 锁定当前目录的 inode,以防止并发访问冲突。
      • 它检查当前 inode 是否确实是一个目录 (ip->type == T_DIR),如果不是,说明路径非法(比如 /a/ba 是一个文件),解析失败。
    5. 查找下一级: 它调用 dirlookup(ip, name, 0) 在当前目录中查找刚刚提取出的 name,以获取下一级的 inode。
    6. 释放旧锁,更新状态: 查找到下一级 inode (next) 后,它调用 iunlockput(ip) 来解锁并减少对当前目录 inode 的引用计数。然后,它将 ip 更新为 next,准备下一次循环。
    7. 处理 nameiparent: 如果 nameiparent 标志为 1,并且路径已经解析到最后一个元素,循环会提前终止,此时 ip 指向的就是父目录。
    8. 循环结束: 当 skipelem 无法再提取出新的路径元素时,循环结束。
  • 返回值: 成功时返回一个已锁定的目标 inode(目标本身或其父目录)。失败时返回 0
  • 引用: skipelem() (来自 path.c), iget(), ilock(), iunlockput() (来自 inode.c), dirlookup() (来自 dir.c)。

16. namei(char *path)

  • 作用: "Name to Inode" 的缩写。这是一个简化的接口,它的唯一目的是根据一个完整的路径,返回路径最后一个元素所对应的 inode。

  • 参数:

    • char *path: 要查找的完整路径,例如 "/a/b/c.txt"
  • 核心逻辑: 它是一个对 namex 的简单封装。它直接调用 namex,并将 nameiparent 参数设置为 0,同时忽略 name 输出参数。

    struct inode*
    namei(char *path)
    {
      char name[DIRSIZ];
      return namex(path, 0, name); // nameiparent=0, 表示要找到路径的末端
    }
    
  • 使用场景: 任何需要根据完整路径获取文件或目录自身的系统调用,比如 open() (当不带 O_CREATE 标志时), stat(), chdir()

17. nameiparent(char *path, char *name)

  • 作用: "Name to Inode of Parent" 的缩写。这是另一个简化的接口,它的目的是找到一个路径的父目录,并把路径的最后一个文件名提取出来。

  • 参数:

    • char *path: 要查找的完整路径,例如 "/a/b/c.txt"
    • char *name: 一个输出缓冲区,用于存放路径的最后一个元素(例如 "c.txt")。
  • 核心逻辑: 它也是对 namex 的封装,但它将 nameiparent 参数设置为 1,明确表示它需要的是父目录的 inode。

posted @ 2025-07-30 15:21  BuerH  阅读(15)  评论(0)    收藏  举报