0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

xv6的文件系统是如何实现的

Linux阅码场 来源:Rand 作者:Rand 2021-10-12 18:00 次阅读

文件系统

本文继续来看 的文件系统部分, 将文件系统的设计分为 7 层: ,磁盘、缓存区、日志三个部分在前文已经说了,本文接着讲述 ,目录,路径三个层次。

这部分的理论知识可以参考文章:捋一捋文件系统。本文直接来看 xv6 的文件系统这部分是如何实现的。

文件系统布局

再来系统的看看 xv6 文件系统的布局图:

a8cd53e4-22a0-11ec-82a8-dac502259ad0.png

这个图与 文档给出的布局图有些不一样,主要是日志区的位置变化了。 文档给出的布局图日志区位于文件系统的末尾,但是根据源码来看日志区应该是位于超级块后面的。前文直接用的 文档中的图,应该是有误的,实在抱歉。我看了几个版本的 源码和文档,源码是日志区都是安排在超级块后面,而文档的布局图描述的是将日志区放在末尾。不过这不是重点,不影响咱们理解,不管位于哪儿,在超级块中做相应修改就行。

引导块、超级块

第 0 块是引导块,里面存放的启动程序也就是 ,详见前文:实例讲解多处理器下的计算机启动

第 1 块是超级块,存有文件系统的元信息,相关结构体定义如下:

structsuperblock{
uintsize;//Sizeoffilesystemimage(blocks)文件系统大小,也就是一共多少块
uintnblocks;//Numberofdatablocks数据块数量
uintninodes;//Numberofinodes.//i结点数量
uintnlog;//Numberoflogblocks//日志块数量
uintlogstart;//Blocknumberoffirstlogblock//第一个日志块块号
uintinodestart;//Blocknumberoffirstinodeblock//第一个i结点所在块号
uintbmapstart;//Blocknumberoffirstfreemapblock//第一个位图块块号
};

可以看出超级块实则就是文件系统布局的信息集合。在 中我们可以知道:

#defineNINODES200
#defineMAXOPBLOCKS10
#defineLOGSIZE(MAXOPBLOCKS*3)
#defineFSSIZE1000
#defineIPB(BSIZE/sizeof(structdinode))


intnbitmap=FSSIZE/(BSIZE*8)+1;
intninodeblocks=NINODES/IPB+1;
intnlog=LOGSIZE;

intnmeta=2+nlog+ninodeblocks+nbitmap;
intnblocks=FSSIZE-nmeta;

intlogstart=2;
intinodestart=2+nlog;
intbmapstart=2+nlog+ninodeblocks;

从上述代码可以看出,文件系统的各个部分从哪开始,到哪结束都是可以明确计算出来的,所以其实不管将日志区安排在哪,我们都可以从超级块中获取相应的位置大小信息。

数据区

紧接着超级块的区域应该是 ,但是 的内容有些多有些复杂,我们放在后面讲,先来看看数据区中数据块的组织与管理。

数据块的分配和释放由位图来管理,但位图管理的区域不止数据区,而是整个文件系统。有关位图的宏定义如下:

//Bitmapbitsperblock每个块能有多少个bit
#defineBPB(BSIZE*8)
//Blockoffreemapcontainingbitforblockb块b在哪个位图块上
#defineBBLOCK(b,sb)(b/BPB+sb.bmapstart)

分配回收

staticuintballoc(uintdev)
{
intb,bi,m;
structbuf*bp;

bp=0;
for(b=0;b< sb.size; b += BPB){
    bp = bread(dev, BBLOCK(b, sb));       //读取位图信息
for(bi=0;bi< BPB && b + bi < sb.size; bi++){
      m = 1<< (bi % 8);
if((bp->data[bi/8]&m)==0){//Isblockfree?如果该块空闲
bp->data[bi/8]|=m;//Markblockinuse.标记该块使用
log_write(bp);
brelse(bp);//释放锁
bzero(dev,b+bi);//将该块置0
returnb+bi;//返回块号
}
}
brelse(bp);//释放锁
}
panic("balloc:outofblocks");
}

staticvoidbfree(intdev,uintb)//释放一个数据块,相应位图清零
{
structbuf*bp;
intbi,m;

bp=bread(dev,BBLOCK(b,sb));
bi=b%BPB;
m=1<< (bi % 8);
if((bp->data[bi/8]&m)==0)
panic("freeingfreeblock");
bp->data[bi/8]&=~m;
log_write(bp);
brelse(bp);
}

位图块中每一位都代表着一块,该位置 1 表示相应的块正在使用,该位置 0 表示相应的块空闲。分配数据块就是在位图中寻找空闲位,然后将其置 1 就代表分配出去了。

上述代码涉及的都是比较简单的位运算,有详细的注释,就不说明了,释放一个数据块的操作就是分配的逆操作,也不再赘述。

inode

磁盘上的 dinode

紧接着超级块后面的区域是 区域, 定义的 共有 200 个,每个磁盘上的 定义如下:

structdinode{
shorttype;//Filetype
shortmajor;//Majordevicenumber(T_DEVonly)
shortminor;//Minordevicenumber(T_DEVonly)
shortnlink;//Numberoflinkstoinodeinfilesystem
uintsize;//Sizeoffile(bytes)
uintaddrs[NDIRECT+1];//Datablockaddresses
};
#defineNDIRECT12

表示该 指向的文件的类型,在 里面就只有三种类型,普通文件,目录文件,设备文

表示该文件的链接数,链接分为硬链接和软连接,这里与链接数目直接相关的是硬链接,后面实现文件系统调用的时候我们会看到, 系统调用会创建一个新目录项并且增加一个链接数。 系统调用将链接数减 1,如果该文件在内存中的引用数和链接数都为 0 的话,则会删除该文件。这部分咱们后面再慢慢聊,本文只捎带提一下。

表示文件的大小,这里是以字节为单位。

在 里面使用 和 来区分设备,同一类设备有着相同的 , 又表示该类设备中具体的某设备。后面我们会看到如果文件类型为设备,则读写的时候会根据 调用相应设备的读写程序。

每个 有 11 个一级指针,一个间接指针,用来指向数据块。这些都是可以改变的, 有个关于支持大文件的实现就是要增加一个间接索引来实现。

inode 缓存

磁盘上的 在内存中也有相应的缓存:

struct{
structspinlocklock;
structinodeinode[NINODE];
}icache;//磁盘上i结点在内存中的缓存

内存中的 定义如下:

structinode{
uintdev;//Devicenumber设备号
uintinum;//Inodenumberinode号
intref;//Referencecount引用数
structsleeplocklock;//protectseverythingbelowhere
intvalid;//inodehasbeenreadfromdisk?是否有效

shorttype;//copyofdiskinode
shortmajor;
shortminor;
shortnlink;
uintsize;
uintaddrs[NDIRECT+1];
};

内存中的 比磁盘上的 多了几个属性,首先是设备号, 里面一切皆文件,设备也是文件,所以设备号来表示什么设备。但是 xv6 没这么复杂,这里主要就是来区分磁盘的主盘和从盘。时为从盘, 时为主盘,这个值在读写磁盘的时候用到,用它来设置磁盘的 device 寄存器来指定主盘从盘。详见:带你了解磁盘驱动程序

表示引用数,这个要与 链接数作区别,目前可以暂且理解为 为磁盘上文件之间的关系,而 主要用于内存中引用该文件的次数,比如 关闭文件使引用数减 1。这部分在文件系统调用的时候再作详细讲解。

表示是否有效,跟磁盘那里缓存块中的数据是否有效一个意思,如果缓存中的数据是从磁盘中读取过来的,则有效。通常无效是因为 刚分配,所以里面的数据无效

整个 缓存区有一把自旋锁,每个 缓存有把休眠锁,为什么如此,道理还是同磁盘和缓存块。首先它们都是公共资源,需要锁来避免竞争条件。再者 的作用就是组织管理 ,像是一个分配器,访问 的临界区的时间是很短的,使用自旋锁就行。而一个进程对某个 的使用时间可能很长,最好使用休眠锁,其他进程也想要获取该 的使用权时就休眠让出 提高性能。

分配 inode

对于磁盘上的 , 并没有什么组织管理结构,分配空闲 的方法就是简单粗暴地从头至尾循环查找空闲 。

structinode*
ialloc(uintdev,shorttype)
{
intinum;
structbuf*bp;
structdinode*dip;

for(inum=1;inum< sb.ninodes; inum++){  
    bp = bread(dev, IBLOCK(inum, sb));      //读取第inum个i结点所在的位置
dip=(structdinode*)bp->data+inum%IPB;//该i结点地址
if(dip->type==0){//afreeinode找到空闲i结点
memset(dip,0,sizeof(*dip));
dip->type=type;
log_write(bp);//markitallocatedonthedisk
brelse(bp);
returniget(dev,inum);//以内存中的i结点形式返回
}
brelse(bp);
}
panic("ialloc:noinodes");
}

#defineIBLOCK(i,sb)((i)/IPB+sb.inodestart)
#defineIPB(BSIZE/sizeof(structdinode))

先来看看下面两个宏定义, 表示一个块中能有几个 , 表示第 个 在第几块。

分配数据块的时候有位图来组织管理,所以分配数据块的时候就“从头至尾”的查询空闲位,而 没有组织管理的机制,所以就直接从头至尾的查询 的使用情况。如果该 的 属性为 0 表示该 空闲,可以分配,反之正是用当中不可分配。

分配了之后将该 初始化,将其数据全部置 0,然后赋其 属性,表示该 指向一个 类型的文件。

分配了该 需要在磁盘上也将其标记为已分配,因为目前是在内存中操作的,得同步到磁盘上去,所以直接调用 将该 所在的缓存块同步到磁盘。当然并未真正地直接写到磁盘了,只是在将该缓存数据标记为脏,关于日志,读写磁盘的操作本文不赘述了,可以参考前文:如何实现一个简单的日志系统

回到分配 的函数上来,磁盘上的 已分配,得到了 号,但是文件系统实际工作的时候使用的是内存中的 缓存,所以调用 来分配(获取)一个内存中的 来缓存 数据:

staticstructinode*iget(uintdev,uintinum)
{
structinode*ip,*empty;

acquire(&icache.lock);

//Istheinodealreadycached?如果该dinode在内存中已缓存
empty=0;
for(ip=&icache.inode[0];ip< &icache.inode[NINODE]; ip++){
    if(ip->ref>0&&ip->dev==dev&&ip->inum==inum){//在缓存中找到该i结点
ip->ref++;//引用加1
release(&icache.lock);
returnip;
}
if(empty==0&&ip->ref==0)//Rememberemptyslot.记录icache中空闲的inode
empty=ip;
}

//Recycleaninodecacheentry.
if(empty==0)
panic("iget:noinodes");
//该dinode在内存中没有缓存,分配一个空闲inodeempty
//根据参数,初始化该空闲inode,还没读入数据,valid设为0
ip=empty;
ip->dev=dev;
ip->inum=inum;
ip->ref=1;
ip->valid=0;
release(&icache.lock);

returnip;
}

如果磁盘上的 在 中已有缓存,那么直接将该 的引用数加 1,再返回该 就行。如果没有缓存则分配一个空闲的 ,根据参数初始化 ,因为没有实际读入 的数据,所以 的 属性置 0 表示无效。

使用修改 inode

使用 之前需要加锁:

voidilock(structinode*ip)
{
structbuf*bp;
structdinode*dip;

if(ip==0||ip->ref< 1)//空指针引用小于1都是错误的
panic("ilock");

acquiresleep(&ip->lock);//上锁

if(ip->valid==0){//有效位为0,从磁盘读入数据
bp=bread(ip->dev,IBLOCK(ip->inum,sb));
dip=(structdinode*)bp->data+ip->inum%IPB;
ip->type=dip->type;
ip->major=dip->major;
ip->minor=dip->minor;
ip->nlink=dip->nlink;
ip->size=dip->size;
memmove(ip->addrs,dip->addrs,sizeof(ip->addrs));
brelse(bp);
ip->valid=1;
if(ip->type==0)
panic("ilock:notype");
}
}

分配 的时候并未从磁盘中 读入数据,只是将 的 置 0 表示数据无效,正式读入 数据在这加锁的时候进行。

对缓存中 的修改需要同步到磁盘上的 :

voidiupdate(structinode*ip)
{
structbuf*bp;
structdinode*dip;

bp=bread(ip->dev,IBLOCK(ip->inum,sb));//读取磁盘上的i结点
dip=(structdinode*)bp->data+ip->inum%IPB;
dip->type=ip->type;//update
dip->major=ip->major;
dip->minor=ip->minor;
dip->nlink=ip->nlink;
dip->size=ip->size;
memmove(dip->addrs,ip->addrs,sizeof(ip->addrs));
log_write(bp);
brelse(bp);
}

用完 需要 “放下” 它:

voidiput(structinode*ip)
{
acquiresleep(&ip->lock);//取锁
if(ip->valid&&ip->nlink==0){
//获取该i结点的引用数
acquire(&icache.lock);
intr=ip->ref;
release(&icache.lock);

if(r==1){
//inodehasnolinksandnootherreferences:truncateandfree.
itrunc(ip);
ip->type=0;
iupdate(ip);
ip->valid=0;
}
}
releasesleep(&ip->lock);

acquire(&icache.lock);
ip->ref--;
release(&icache.lock);
}

该函数将 的引用数减 1,如果本身就是该 的最后一个引用,且链接数也为 0,那么调用 将该 指向的所有数据块全部释放,也就相当于删除了 指向的文件

表示该 已释放,被回收到了 。将 信息更新到磁盘上的 之后将 置 0 表数据不再有效。

索引

的索引部分用来指向数据块

staticuintbmap(structinode*ip,uintbn)//给i结点第bn个索引指向的地方分配块
{
uintaddr,*a;
structbuf*bp;

//bn为直接索引
if(bn< NDIRECT){    
    //如果第bn个索引指向的块还未分配,则分配,否则返回块号
if((addr=ip->addrs[bn])==0)
ip->addrs[bn]=addr=balloc(ip->dev);
returnaddr;
}

bn-=NDIRECT;//bn为间接索引

if(bn< NINDIRECT){
    //Loadindirectblock,allocatingifnecessary.
if((addr=ip->addrs[NDIRECT])==0)//如果间接索引所在的块还未分配,分配
ip->addrs[NDIRECT]=addr=balloc(ip->dev);
bp=bread(ip->dev,addr);//读取一级索引块
a=(uint*)bp->data;
if((addr=a[bn])==0){//如果该索引指向的块还未分配,分配
a[bn]=addr=balloc(ip->dev);
log_write(bp);
}
brelse(bp);
returnaddr;//返回索引bn指向的块的块号
}

panic("bmap:outofrange");
}

返回索引 指向的数据块块号,如果该数据块未分配,则分配之后再返回该块块号。

staticvoiditrunc(structinode*ip)
{
inti,j;
structbuf*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;
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;
}

ip->size=0;
iupdate(ip);
}

,截断 ,不知怎样翻译,但这个函数的实际工作就是将 所指向的数据块全部释放,也就相当于删除文件了。具体的工作方式就是一个个的判断索引是否有效,如果有效就调用 释放,然后将该索引置为无效。基本上就与 做相反的工作。

读写数据

intreadi(structinode*ip,char*dst,uintoff,uintn)//从inode读取数据
{
uinttot,m;
structbuf*bp;

if(ip->type==T_DEV){//如果该inode指向的是设备文件
if(ip->major< 0||ip->major>=NDEV||!devsw[ip->major].read)
return-1;
returndevsw[ip->major].read(ip,dst,n);//使用设备特有的读取方式
}

if(off>ip->size||off+n< off)  //如果开始读取的位置超过文件末尾,如果读取的字节数是负数
return-1;
if(off+n>ip->size)//如果从偏移量开始的n字节超过文件末尾
n=ip->size-off;//则只能够再读取这么多字节

for(tot=0;tot//tol:目前总共已读的字节数,n:需要读取的字节数,off:从这开始读,dst:目的地
bp=bread(ip->dev,bmap(ip,off/BSIZE));//读取off所在的数据块到缓存块
m=min(n-tot,BSIZE-off%BSIZE);//一次性最多读取m字节
memmove(dst,bp->data+off%BSIZE,m);//赋值数据到dst
brelse(bp);//释放缓存块
}
returnn;
}

intwritei(structinode*ip,char*src,uintoff,uintn)
{
uinttot,m;
structbuf*bp;

if(ip->type==T_DEV){
if(ip->major< 0||ip->major>=NDEV||!devsw[ip->major].write)
return-1;
returndevsw[ip->major].write(ip,src,n);
}

if(off>ip->size||off+n< off)
    return-1;
if(off+n>MAXFILE*BSIZE)
return-1;

for(tot=0;totdev,bmap(ip,off/BSIZE));
m=min(n-tot,BSIZE-off%BSIZE);
memmove(bp->data+off%BSIZE,src,m);
log_write(bp);
brelse(bp);
}

函数用来读取数据,从 ip 指向的文件中,从 开始读,读取 字节到 中去。

如果 指向的文件类型是设备,那么读取数据要使用专门的读取方式,比如说控制台有着自己专门的读入数据方式,这些设备的读取方法集合在 数组当中。关于这部分我们后面的文章再详述,这里只说磁盘上的文件的读写。

紧接着判断了一下参数的合理性,比如 不应超过文件末尾,读取的字节数 不应该是负数,如果 超过了文件末尾,那么最多只能读取 个字节数,要修改 的值。

文件数据可能有多块, 表示 所在的块数,这个块数是相对于该文件开头的块号,调用 获取 所在的绝对块号。拿到 所在数据块的绝对块号之后调用 读取该数据块的数据到缓存块 。

数据在内存中已经准备完毕,可以赋值移动数据了。但是每次读取的数据有上限,不能超过 ,这是还需要读取的字节数,不能超过 ,这是每次最多能够读取的字节数。不能超过这两者,所以两者之中取较小值。

,将数据源 中的数据写 字节到 指向的文件中,从偏移量为 的地方开始写。

基本上是与 相反的操作,只是最后需要更新 信息,因为像文件中写数据,该文件会变大, 是文件的代表与文件一一对应,需要更新 的 size 属性,然后再调用 将 同步到磁盘上的 。

目录

目录也是文件,只是它的数据有些特殊,其数据是一个个目录项,其主要属性有文件名, 编号。

是一个文件的代言人,一个文件与一个 一一对应,但是 的属性里面并没有文件名。文件名这个属性在目录项这指出,目录项的主要作用就是将 和文件名联系起来。

结构定义

structdirent{//目录项结构
ushortinum;
charname[DIRSIZ];
};

#defineDIRSIZ14

中目录项只由两项组成,文件名和 编号。

查找目录项

structinode*dirlookup(structinode*dp,char*name,uint*poff)
{
uintoff,inum;
structdirentde;

if(dp->type!=T_DIR)//如果该文件不是目录文件
panic("dirlookupnotDIR");

for(off=0;off< dp->size;off+=sizeof(de)){
if(readi(dp,(char*)&de,off,sizeof(de))!=sizeof(de))//读取dp指向的目录文件,每次读一个目录项
panic("dirlookupread");
if(de.inum==0)//如果是根目录??????????????
continue;
if(namecmp(name,de.name)==0){//根据名字找文件
//entrymatchespathelement
if(poff)//记录该目录项在目录中的偏移
*poff=off;
inum=de.inum;//name文件的inode编号
returniget(dp->dev,inum);//或取该inode
}
}

return0;
}

这个函数用来在 指向的目录文件下寻找名为 的目录项,将该目录项的偏移量记录在 中,最后返回名字为 的文件的 。

因此根据文件名查找文件的是指就是在目录文件中查找目录项的过程,具体的查找方式就是一个个的比对目录项的名称和要查找的文件名是否相同,如果相同,则找到,反之说明该目录下并没有要查找的文件

添加目录项

intdirlink(structinode*dp,char*name,uintinum)
{
intoff;
structdirentde;
structinode*ip;

//Checkthatnameisnotpresent.
if((ip=dirlookup(dp,name,0))!=0){
iput(ip);//dirlookup调用iget使引用数加1,所以调用iput使引用数减1
return-1;//name目录项已存在,返回-1
}

//Lookforanemptydirent.
for(off=0;off< dp->size;off+=sizeof(de)){
if(readi(dp,(char*)&de,off,sizeof(de))!=sizeof(de))
panic("dirlinkread");
if(de.inum==0)//找到一个空闲目录项
break;
}

strncpy(de.name,name,DIRSIZ);//设置目录项的文件名字
de.inum=inum;//设置目录项的i结点编号
if(writei(dp,(char*)&de,off,sizeof(de))!=sizeof(de))//将该目录项写进dp指向的目录文件中
panic("dirlink");

return0;
}

此函数用来在 指向的目录文件中添加一个目录项,通常是创建了一个新文件,需要在该目录下添加这个新文件的信息

首先查找该目录项是否存在,如果不存在则找一个空闲目录项位置,将新文件的 和文件名写进去。

路径

路径,何为路径,比如常见的 ,仔细观察,会发现,路径实则是一个个文件名组成的,这个文件可能是目录文件,也可能是普通文件。一般最后一项是普通文件名,中间的都是目录文件名

另外像 这种路径以 '/' 开头表示绝对路径, 这种不以 '/' 开头的表示相对路径。这两种路径大家应该都很熟悉了,不再多做解释,或者可以参考文章捋一捋文件系统,其中详细的捋了捋文件系统的理论知识。

不论哪一种路径表示,都需要一个路径解析函数,将其中一个个文件名给提取出来:

staticchar*skipelem(char*path,char*name)
{
char*s;
intlen;

while(*path=='/')//跳过'/'
path++;
if(*path==0)//路径空
return0;
s=path;
while(*path!='/'&&*path!=0)//path继续向后移,剥出最前面的目录名
path++;
len=path-s;//记录该目录名的长度
if(len>=DIRSIZ)
memmove(name,s,DIRSIZ);//将该目录名复制给name
else{
memmove(name,s,len);
name[len]=0;
}
while(*path=='/')//继续跳过'/'
path++;
returnpath;//返回剩下的路径
}

调用一次解析一个头部的文件名放在 中,返回剩下的路径。

用源码注释中的例子来说明:

skipelem("a/bb/c",name)="bb/c",settingname="a"
skipelem("///a//bb",name)="bb",settingname="a"
skipelem("a",name)="",settingname="a"
skipelem("",name)=skipelem("////",name)=0
staticstructinode*
namex(char*path,intnameiparent,char*name)
{
structinode*ip,*next;

if(*path=='/')//绝对路径
ip=iget(ROOTDEV,ROOTINO);//读取根目录
else//相对路径
ip=idup(myproc()->cwd);//读取当前工作目录

while((path=skipelem(path,name))!=0){
ilock(ip);
if(ip->type!=T_DIR){
iunlockput(ip);
return0;
}
if(nameiparent&&*path==''){//如果是要返回父结点,并且剩下的路径已经为空,则当前结点就是i结点直接返回
//Stoponelevelearly.
iunlock(ip);
returnip;
}
if((next=dirlookup(ip,name,0))==0){//查询下一个目录
iunlockput(ip);
return0;
}
iunlockput(ip);
ip=next;//当前目录指向下一个,然后while循环,直到解析到最后
}
if(nameiparent){
iput(ip);
return0;
}
returnip;
}

这个函数根据路径返回 ,比如说路径 ,如果 有效,则返回文件 的 ,如果 无效,则返回文件 的 。

这个函数主要是对路径解析函数 和查找目录项函数 函数的运用,根据路径查找文件的步骤大致如下:

  • 获取当前目录的
  • 根据 获取目录文件
  • 在该目录文件下根据文件名查找文件/目录
  • 循环上述过程直到文件被找到

函数就是上述过程的实现,至于这个函数的具体怎么实现的,就不详细说明了,可以自己举个例子根据代码模拟一下过程就明白了。在模拟的过程中主要注意几个条件判断:

  • (path = skipelem(path, name)) != 0,当 为空字符串的才返回 0,也就是说 skipelem("", name) = 0。path 指向一个空字符串,并不是说 path 本身为空
  • if(nameiparent && *path == ''), 为空字符串的时候也就是 "" 的时候 *path = ''

另外如果是相对路径的话,当前目录需要从进程 中的 属性中获取,相关内容后面进程再详述。

本文主要介绍了 文件系统 ,目录,路径三个层次的设计与实现,应该对这三个概念会有个更深刻的认识。 是文件的代表,与文件一一对应,目录也是文件,只是其数据是其他文件的信息,也就是一个个目录项。目录项是其他文件的文件名和相应的 编号组合而成。而路径呢?就是一个个文件名组合在一起的字符串。可以使用路径解析函数将一个个文件名解析出来,然后根据一层层目录中的目录项从上至下地查找文件

好啦本文就到这里了,有什么错误还请批评指针,也欢迎大家来同我讨论交流一起学习进步。

责任编辑:haq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 文件
    +关注

    关注

    1

    文章

    540

    浏览量

    24385
  • 代码
    +关注

    关注

    30

    文章

    4548

    浏览量

    66609

原文标题:inode、目录、路径

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    全国大学生计算机系统能力大赛操作系统设计赛-LoongArch 赛道广东龙芯2K1000LA 平台资料分享

    LoongArch平台的参考实现。具体的实验设计参见xv6主页 的labs标签页。 uCore.实验指导书 rCore. 2022年全国大学生操作系统大赛-功能挑战赛二等奖。 MaQueOS.
    发表于 02-04 15:37

    服务器数据恢复—ocfs2文件系统被误格式化为Ext4文件系统的数据恢复案例

    由于工作人员的误操作,将Ext4文件系统误装入到存储中Ocfs2文件系统数据卷上,导致原Ocfs2文件系统被格式化为Ext4文件系统。 由于Ext4
    的头像 发表于 12-04 10:49 227次阅读
    服务器数据恢复—ocfs2<b class='flag-5'>文件系统</b>被误格式化为Ext4<b class='flag-5'>文件系统</b>的数据恢复案例

    Linux的文件系统特点

    Linux的文件系统特点 文件系统要有严格的组织形式,使得文件能够以块为单位进行存储。 文件系统中也要有索引区,用来方便查找一个文件分成的多
    的头像 发表于 11-09 14:48 447次阅读
    Linux的<b class='flag-5'>文件系统</b>特点

    如何把文件系统烧到EMMC并从EMMC加载

    如何下载并从SD卡加载文件系统,提到过可以从EMMC引导系统,本篇将为您介绍如何把文件系统烧到EMMC,并从EMMC加载。
    的头像 发表于 10-30 16:06 1346次阅读
    如何把<b class='flag-5'>文件系统</b>烧到EMMC并从EMMC加载

    基于ARM9和μC-OS-II的SD卡文件系统设计与实现

    电子发烧友网站提供《基于ARM9和μC-OS-II的SD卡文件系统设计与实现.pdf》资料免费下载
    发表于 10-24 09:56 0次下载
    基于ARM9和μC-OS-II的SD卡<b class='flag-5'>文件系统</b>设计与<b class='flag-5'>实现</b>

    分布式文件系统的设计原理是什么?

    什么是分布式文件系统?分布式文件系统(DFS)是一种计算机文件系统,使用户能够从多个分布式位置存储和访问数据。它是在分布式环境中的不同计算机之间共享信息的一种方式。通过使用分布式文件系统
    的头像 发表于 10-17 17:35 401次阅读

    事务性日志结构文件系统的设计及实现

    本文发表于FAST 2022,探讨日志结构文件系统层面的事务支持。本文主要对F2FS进行了支持事务的修改,实现了支持ACID事务特性的文件系统层面支持。本文通过实验测试了SQLite和ROCKSDB
    的头像 发表于 10-16 16:01 289次阅读
    事务性日志结构<b class='flag-5'>文件系统</b>的设计及<b class='flag-5'>实现</b>

    谈谈什么是文件系统 文件系统的功能与特点

    文件系统的应用非常广泛,同时种类也是特别的多,并且不同平台也会使用不同性能和特点的文件系统,比如查找效率、数据安全等级等等,如下面windows使用的NTFS:
    发表于 08-30 09:50 1336次阅读
    谈谈什么是<b class='flag-5'>文件系统</b> <b class='flag-5'>文件系统</b>的功能与特点

    适用于Linux的最佳通用文件系统 Linux文件系统的安装

    为您的计算机选择正确的文件系统可能是一个困难的过程。您可能会想知道:为什么文件系统很重要?有没有适用于安装 Linux 的特定文件系统? 事实证明,有两种文件系统在安装 Linux 时
    发表于 08-03 10:22 187次阅读
    适用于Linux的最佳通用<b class='flag-5'>文件系统</b> Linux<b class='flag-5'>文件系统</b>的安装

    FATFS文件系统原版文件下载

    FATFS文件系统原版文件下载
    发表于 06-25 09:02 0次下载

    Linux proc文件系统详解

    上一篇:《文件系统有很多,但这几个最为重要》介绍了procfs(进程文件系统的缩写),包含一个伪文件系统(启动时动态生成的文件系统),用于通过内核访问进程信息。这个
    发表于 06-15 11:42 764次阅读

    FAT文件系统(2)#操作系统

    文件系统
    学习硬声知识
    发布于 :2023年05月25日 17:02:20

    FAT文件系统(1)#操作系统

    文件系统
    学习硬声知识
    发布于 :2023年05月25日 17:01:49

    使用BusyBox构建根文件系统

    文件系统的构建,是 Linux移植三大组成部分的最后一步,根文件系统构建好后,就构成了一个基础的、可以运行的嵌入式 Linux最小系统
    的头像 发表于 04-19 11:20 822次阅读
    使用BusyBox构建根<b class='flag-5'>文件系统</b>

    如何完善根文件系统

    在用 BusyBox 构建根文件系统一文中介绍了根文件系统的制作,rootfs制作完成后测试时,提示无法运行“/etc/init.d/rcS”这个文件,说明rootfs仍然不够完善,下面将介绍如何完善根
    的头像 发表于 04-18 15:26 789次阅读
    如何完善根<b class='flag-5'>文件系统</b>