linux 0.11 文件系统总结
文件系统
详见:操作系统文件系统。
操作系统中的文件系统可以分为两部分:操作系统内核中或者在硬盘软盘虚拟盘中。一个物理设备可以分为多个逻辑设备,比如一个物理硬盘可以分为多个逻辑硬盘。而一个逻辑设备只有一个文件系统,一个文件系统只包含一个i结点的树结构。一个逻辑设备只能有一个根i结点。
image-20231203234247319
未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件。
生磁盘可以被分区,分区中可以安装文件系统,常见的文件系统有fat32、ext2、ext4等。
MINIX 文件系统与标准 UNIX 的文件系统基本相同。它由 6
个部分组成。分区内可以安装指定文件系统,同一磁盘多个分区文件系统不要求相同。MINIX文件系统布局如下:(下述部分是在磁盘上的)
MINIX文件系统布局
引导块:若作为引导分区,将存放操作系统的引导程序代码 ,否则空置。
超级块:用于存放磁盘设备上文件系统结构的信息 ,说明各部分的大小。
i节点位图:标记i节点数据元素是否被使用
逻辑块位图:标记磁盘数据块是否被使用
i节点区:用于存放inode节点数据,一个文件对应一个inode节点,inode节点存储文件属性数据。
数据区:以固定大小盘块(1k)为单位进行动态分配和回收,用于存储数据,类似内存分页。
位图:一个比特对应一个逻辑块,0,1代表是否被占用
删除文件:清理数据块关系清掉,对应逻辑块位图清0,清理i结点和i结点对应位图。
如果一个物理块有多个逻辑块,上述就罗列着摆放:
超级块结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct super_block { unsigned short s_ninodes; unsigned short s_nzones; unsigned short s_imap_blocks; unsigned short s_zmap_blocks; unsigned short s_firstdatazone; unsigned short s_log_zone_size; unsigned long s_max_size; unsigned short s_magic; struct buffer_head * s_imap [8]; struct buffer_head * s_zmap [8]; unsigned short s_dev; struct m_inode * s_isup ; struct m_inode * s_imount ; unsigned long s_time; struct task_struct * s_wait ; unsigned char s_lock; unsigned char s_rd_only; unsigned char s_dirt; };
超级块结构
inode结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct m_inode { unsigned short i_mode; unsigned short i_uid; unsigned long i_size; unsigned long i_mtime; unsigned char i_gid; unsigned char i_nlinks; unsigned short i_zone[9 ]; struct task_struct * i_wait ; unsigned long i_atime; unsigned long i_ctime; unsigned short i_dev; unsigned short i_num; unsigned short i_count; unsigned char i_lock; unsigned char i_dirt; unsigned char i_pipe; unsigned char i_mount; unsigned char i_seek; unsigned char i_update; };
inode结构
i_zone数组
unsigned short i_zone[9];
i_zone数组包含直接盘块号、一次间接盘块号和二次间接盘块号。一次盘块号可视为单级页表,一次间接盘块号可视为二级页表、二次间接盘块号可视为三级页表。
这种处理方式的好处在于,对于小文件,通过直接块号可快速定位数据块;对于中等类型的文件,一次间接块可以维护较多数据块的同时,具有较快的访问速度;对于大型文件,二次间接盘块号可以维护大量磁盘块,但访问速度较慢。
内存多级页表与i_zone直接区别:不同进程具有固定大小的虚地址空间,并且对其整个虚地址空间的内存,都有可能访问到,因此使用多级页表。文件系统内存在很多大小不一的文件,综合考虑对不同大小文件的特点,使用1-3级磁盘块表可以分别处理小、中、大文件。
izone
文件系统树形结构
所有文件的i结点最终会挂成一个树形结构,树根i结点就是文件系统的根i结点,
加载文件系统就是把一个文件系统的根i结点挂接在另一个文件系统的i结点上,按照这个设计,一个文件系统必须要挂在另一个文件系统上面,最后最根部那个文件系统就是根文件系统
加载根文件系统
根文件系统挂在super_block[8]上。超级块:有一个超级块数组super_block[8]里面每一个元素是一个超级块,只要一个文件系统加载到内核了这个文件系统的根i结点会依次加载到这个数组里面。最多加载8个文件系统
总体效果图
文件系统用i结点来管理,一个i结点管理一个文件,目录文件也是文件,也有i结点来管理。
文件系统与i结点
加载文件过程举例 :
通过文件inode节点,可以定位文件数据块,那如何通过文件路径定位到具体文件?
文件系统主要包含文件和目录两种文件,目录是一种特殊的文件,其文件内容存储其目录下文件名->inode节点号的映射信息。文件查找开始于根目录,根目录号固定为0,不需要查找即可直接打开。
举例说明文件查找过程,给定存在路径/name1/name2/name3查找具体文件过程:
1)通过根节点inode号,打开根目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name1目录inode节点号n1
2)通过name1的inode号n1,打开name1目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name1目录inode节点号n2
3)通过name2的inode号n2,打开name2目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name3目录inode节点号n3
4)通过name3的inode号n3,打开name3文件
怎么打开文件:
通过文件查找找到文件inode节点号,然后打开文件,即读取inode至内存。
定位数据块:通过文件inode节点,访问其i_zone数组,进一步可以定位具体的数据所在磁盘块号。
以c语言open和close返回的是什么解释文件系统
1 2 3 4 5 6 7 8 9 10 struct file file_table [NR_FILE ]; struct super_block super_block [NR_SUPER ]; struct file { unsigned short f_mode; unsigned short f_flags; unsigned short f_count; struct m_inode * f_inode ; off_t f_pos; };
操作系统只有一个super_blocks数组,每个数组元素是一个超级块,一个超级块管理一个逻辑设备,因此最多挂载8个逻辑设备,其中只有一个根设备。
inode_table[32]每一个元素就是一个i结点,是在操作系统中所有打开的i结点
file_table
里面装了file结构体,struct super_block super_block[NR_SUPER]
f_inode指针指向inode_table里面的元素
task struct里面的filp struct file * filp[NR_OPEN];/:
指针数组,每个元素都是file类型的指针
linux
0.11一个进程最多只能打开20个文件(文件是可以重复打开的)可以同一个文件占多个file_table的表项
filp归进程管。进程打开一个文件,首先在filp里面找空闲项,
将这个空闲的位置指向file_table其中的一项,这一项里面的f_inode指针指向inode_table。
c语言里面打开文件返回的句柄就是这个指向的inode_table位置对应的下标索引,例如下图就是0.打开文件就是建立这个指针链接的过程,对应的close文件就是把这个关系链断掉。
file对应的是用户的需求,inode对应的是内核管理
文件系统
打开同一个文件,指向的inode_table是一个,file_table新开了一个
==file_table【64】是整个kernel只有一个,file_table[32]也是整个操作系统只有一个,每个元素是一个file对象==
打开同一个文件
目录跟结点也要放到inode——table,当路径找完了就把结点pop了
image-20231205200445043
i结点是如何管理文件的
1 2 3 4 5 struct m_inode { ... unsigned short i_zone[9 ]; .... };
izone
izone
izone数组是unsigned
short类型,两个字节。前7个是直接的指向7个块。一级间接块大小1k个字节,包含了1k/2
个unsigned short索引
可以指向512个块,二级索引同理。有512*512个数据块。
因此一个inode管理的极限是:(7+512+512*512)KB
根文件系统
更换根设备(进程1格式化虚拟盘并更换跟设备为虚拟盘)
之前第二章设置了虚拟盘并初始化,但是当时没有进行格式化还不能作为块设备使用。格式化的信息存在boot操作系统的软盘上
进程1调用rd_load();函数格式化虚拟盘调用
rd_load()是虚拟盘根文件加载函数。在系统初始化阶段,该函数被用于尝试从启动引导盘
上指定的磁盘块位置开始处把一个根文件系统加载到虚拟盘中。在函数中,这个起始磁盘块位置被定为256。当然你也可以根据自己的具体要求修改这个值,只要保证这个值所规定的磁盘容量能容纳内核映象
文件即可。这样一个由内核引导映象文件(Bootimage)加上根文件系统映象文件(Rootiamge)组合而
成的“二合一”磁盘,就可以象启动 DOS 系统盘那样来启动 Linux
系统。在进行正常的根文件系统加载之前,系统会首先执行
rd_load()函数,试图从磁盘的第 257 块中读取
根文件系统超级块。若成功,就把该根文件映象文件读到内存虚拟盘中,并把根文件系统设备标志
ROOT_DEV 设置为虚拟盘设备(0x0101),否则退出
rd_load(),系统继续从别的设备上执行根文件加载 操作。
之前根设备是软盘:bootsect.s里面指定的
==把虚拟盘指定为根设备,读硬盘是有中断的。软盘因为比较快就在内存里。所以读软盘不用中断读软盘要用do_rd_request==
==rd虚拟盘,虚拟的是软盘==,相当于把软盘的内容映射过来,然后把虚拟盘替软盘成为根设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void rd_load (void ) { struct buffer_head *bh ; struct super_block s ; int block = 256 ; int i = 1 ; int nblocks; char *cp; if (!rd_length) return ; printk("Ram disk: %d bytes, starting at 0x%x\n" , rd_length, (int ) rd_start); if (MAJOR(ROOT_DEV) != 2 ) return ; bh = breada(ROOT_DEV,block+1 ,block,block+2 ,-1 ); if (!bh) { printk("Disk error while looking for ramdisk!\n" ); return ; } *((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data); brelse(bh); if (s.s_magic != SUPER_MAGIC) return ; nblocks = s.s_nzones << s.s_log_zone_size; if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) { printk("Ram disk image too big! (%d blocks, %d avail)\n" , nblocks, rd_length >> BLOCK_SIZE_BITS); return ; } printk("Loading %d bytes into ram disk... 0000k" , nblocks << BLOCK_SIZE_BITS); cp = rd_start; while (nblocks) { if (nblocks > 2 ) bh = breada(ROOT_DEV, block, block+1 , block+2 , -1 ); else bh = bread(ROOT_DEV, block); if (!bh) { printk("I/O error on block %d, aborting load\n" , block); return ; } (void ) memcpy (cp, bh->b_data, BLOCK_SIZE); brelse(bh); printk("\010\010\010\010\010%4dk" ,i); cp += BLOCK_SIZE; block++; nblocks--; i++; } printk("\010\010\010\010\010done \n" ); ROOT_DEV=0x0101 ; }
加载根文件系统
进程1调用mount_root在根设备虚拟盘上加载根文件系统
1 2 3 4 5 6 struct super_block {... struct m_inode * s_isup ; struct m_inode * s_imount ; ... };
文件系统加载结点
首先挂载super_block数组和file_table数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void mount_root (void ) { int i,free ; struct super_block * p ; struct m_inode * mi ; if (32 != sizeof (struct d_inode)) panic("bad i-node size" ); for (i=0 ;i<NR_FILE;i++) file_table[i].f_count=0 ; if (MAJOR(ROOT_DEV) == 2 ) { printk("Insert root floppy and press ENTER" ); wait_for_keypress(); } for (p = &super_block[0 ] ; p < &super_block[NR_SUPER] ; p++) { p->s_dev = 0 ; p->s_lock = 0 ; p->s_wait = NULL ; } ... }
image-20231211092132558
read_super加载文件系统超级块
整体流程图:
readsuper
1 2 3 4 5 6 7 void mount_root (void ) { ... if (!(p=read_super(ROOT_DEV))) panic("Unable to mount root" ); ... }
read_super()用于把指定设备的文件系统的==超级块==读入到==缓冲区==中,并登记到超级块数组中,同时也
把文件系统的 i
节点位图和逻辑块位图读入内存超级块结构的相应数组中。最后并返回该超级块结构的
指针。
首先检查这个要读的超级块是不是已经在super_block[8]中,如果有直接使用不用在加载一次了(和缓冲区看有没有现成的一个意思)
1 2 3 4 5 6 7 8 9 10 11 12 13 static struct super_block * read_super (int dev) { struct super_block * s ; struct buffer_head * bh ; int i,block; if (!dev) return NULL ; check_disk_change(dev); if (s = get_super(dev)) return s; ... }
get_super
查这个要读的超级块是不是已经在super_block[8]中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct super_block * get_super (int dev) { struct super_block * s ; if (!dev) return NULL ; s = 0 +super_block; while (s < NR_SUPER+super_block) if (s->s_dev == dev) { wait_on_super(s); if (s->s_dev == dev) return s; s = 0 +super_block; } else s++; return NULL ; }
超级块上锁等待(别的进程加载了这个文件系统)
1 2 3 4 5 6 7 8 static void wait_on_super (struct super_block * sb) { cli(); while (sb->s_lock) sleep_on(&(sb->s_wait)); sti(); }
在super_block里面找到空项
在super_block中找到一项空的并加锁。这里加载根文件系统,第一项就是空的所以是选了第一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static struct super_block * read_super (int dev) { ... for (s = 0 +super_block ;; s++) { if (s >= NR_SUPER+super_block) return NULL ; if (!s->s_dev) break ; } s->s_dev = dev; s->s_isup = NULL ; s->s_imount = NULL ; s->s_time = 0 ; s->s_rd_only = 0 ; s->s_dirt = 0 ; lock_super(s); ... }
image-20231211095203241
把超级块加载到缓冲区,再加载到super
block
调用bread读取超级块,这里的设备是rd虚拟盘。块号是1.因此在do
request的时候是do_rd_request.虚拟盘虽然是内存模拟的盘,但是读取的操作完全模仿了外设,但是他毕竟是内存不是外设,因此和读硬盘不同的是:不会发生类似硬盘中断的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static struct super_block * read_super (int dev) { ... if (!(bh = bread(dev,1 ))) { s->s_dev=0 ; free_super(s); return NULL ; } *((struct d_super_block *) s) = *((struct d_super_block *) bh->b_data); brelse(bh); if (s->s_magic != SUPER_MAGIC) { s->s_dev = 0 ; free_super(s); return NULL ; } ... }
image-20231211100257070
完善super
blok中i结点位图逻辑位图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 static struct super_block * read_super (int dev) { ... for (i=0 ;i<I_MAP_SLOTS;i++) s->s_imap[i] = NULL ; for (i=0 ;i<Z_MAP_SLOTS;i++) s->s_zmap[i] = NULL ; block=2 ; for (i=0 ; i < s->s_imap_blocks ; i++) if (s->s_imap[i]=bread(dev,block)) block++; else break ; for (i=0 ; i < s->s_zmap_blocks ; i++) if (s->s_zmap[i]=bread(dev,block)) block++; else break ; if (block != 2 +s->s_imap_blocks+s->s_zmap_blocks) { for (i=0 ;i<I_MAP_SLOTS;i++) brelse(s->s_imap[i]); for (i=0 ;i<Z_MAP_SLOTS;i++) brelse(s->s_zmap[i]); s->s_dev=0 ; free_super(s); return NULL ; } s->s_imap[0 ]->b_data[0 ] |= 1 ; s->s_zmap[0 ]->b_data[0 ] |= 1 ; free_super(s); return s; }
1 2 3 4 5 6 7 static void free_super (struct super_block * sb) { cli(); sb->s_lock = 0 ; wake_up(&(sb->s_wait)); sti(); }
image-20231211102021214
将根设备的根i结点挂载super
block上
调用iget从虚拟盘上读取i结点。有了i结点,可以通过根i结点找到文件系统中的任意指定i结点
1 2 3 4 5 6 7 8 9 void mount_root (void ) { ... if (!(mi=iget(ROOT_DEV,ROOT_INO))) panic("Unable to read root i-node" ); ...
iget:get_empty_inode
首先在记载所有打开的i结点的数组中申请一个空闲的
1 2 3 4 5 6 7 8 9 struct m_inode * iget (int dev,int nr) { struct m_inode * inode , * empty ; if (!dev) panic("iget with dev==0" ); empty = get_empty_inode(); ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct m_inode * get_empty_inode (void ) { struct m_inode * inode ; static struct m_inode * last_inode = inode_table; int i; do { inode = NULL ; for (i = NR_INODE; i ; i--) { if (++last_inode >= inode_table + NR_INODE) last_inode = inode_table; if (!last_inode->i_count) { inode = last_inode; if (!inode->i_dirt && !inode->i_lock) break ; } } if (!inode) { for (i=0 ; i<NR_INODE ; i++) printk("%04x: %6d\t" ,inode_table[i].i_dev, inode_table[i].i_num); panic("No free inodes in mem" ); } wait_on_inode(inode); while (inode->i_dirt) { write_inode(inode); wait_on_inode(inode); } } while (inode->i_count); memset (inode,0 ,sizeof (*inode)); inode->i_count = 1 ; return inode; }
iget
inode_table 初始化的时候:
1 struct m_inode inode_table [NR_INODE ]= {{0 ,},};
这是对数组进行初始化的语法。它使用了一个嵌套的大括号,将数组中的每个元素初始化为
{0,},这将初始化结构体中的所有成员为零(或NULL,具体取决于结构体的定义)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 struct m_inode * iget (int dev,int nr) { ... inode = inode_table; while (inode < NR_INODE+inode_table) { if (inode->i_dev != dev || inode->i_num != nr) { inode++; continue ; } wait_on_inode(inode); if (inode->i_dev != dev || inode->i_num != nr) { inode = inode_table; continue ; } inode->i_count++; if (inode->i_mount) { int i; for (i = 0 ; i<NR_SUPER ; i++) if (super_block[i].s_imount==inode) break ; if (i >= NR_SUPER) { printk("Mounted inode hasn't got sb\n" ); if (empty) iput(empty); return inode; } iput(inode); dev = super_block[i].s_dev; nr = ROOT_INO; inode = inode_table; continue ; } if (empty) iput(empty); return inode; } if (!empty) return (NULL ); inode=empty; inode->i_dev = dev; inode->i_num = nr; read_inode(inode); return inode; }
read_inode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static void read_inode (struct m_inode * inode) { struct super_block * sb ; struct buffer_head * bh ; int block; lock_inode(inode); if (!(sb=get_super(inode->i_dev))) panic("trying to read inode without dev" ); block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1 )/INODES_PER_BLOCK; if (!(bh=bread(inode->i_dev,block))) panic("unable to read i-node block" ); *(struct d_inode *)inode = ((struct d_inode *)bh->b_data) [(inode->i_num-1 )%INODES_PER_BLOCK]; brelse(bh); unlock_inode(inode); }
image-20231211131847124
将根文件系统与进程1关联,设置root和pwd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void mount_root (void ) { ... if (!(mi=iget(ROOT_DEV,ROOT_INO))) panic("Unable to read root i-node" ); mi->i_count += 3 ; p->s_isup = p->s_imount = mi; current->pwd = mi; current->root = mi; ... }
计算虚拟盘空闲块信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void mount_root (void ) { ... free =0 ; i=p->s_nzones; while (-- i >= 0 ) if (!set_bit(i&8191 ,p->s_zmap[i>>13 ]->b_data)) free ++; printk("%d/%d free blocks\n\r" ,free ,p->s_nzones); free =0 ; i=p->s_ninodes+1 ; while (-- i >= 0 ) if (!set_bit(i&8191 ,p->s_imap[i>>13 ]->b_data)) free ++; printk("%d/%d free inodes\n\r" ,free ,p->s_ninodes); }
至此mount_root执行完,同时返回后sys_setip函数也执行完了:
1 2 3 4 5 6 7 int sys_setup (void * BIOS) { ... rd_load(); mount_root(); return (0 ); }
返回到之前调用system_call的地方,一路返回到一开始的init函数。
更通用的安装文件系统
安装文件系统分为三步:
把超级块读出来挂载到super block[8]上
将作为挂载结点的虚拟盘上的i结点挂在inode_table上
将硬盘上的需要被挂载的超级块挂在inode table上
在shell下输入 mount /dev/hd1 /mnt
的命令可以安装文件系统:将设备hd1的文件系统挂载到mnt目录文件下。此时shell会创建一个新的进程调用mount函数最终映射到sys_mount系统调用函数执行加载文件系统的过程
sys_mount
挂载流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 int sys_mount (char * dev_name, char * dir_name, int rw_flag) { struct m_inode * dev_i , * dir_i ; struct super_block * sb ; int dev; if (!(dev_i=namei(dev_name))) return -ENOENT; dev = dev_i->i_zone[0 ]; if (!S_ISBLK(dev_i->i_mode)) { iput(dev_i); return -EPERM; } iput(dev_i); if (!(dir_i=namei(dir_name))) return -ENOENT; if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) { iput(dir_i); return -EBUSY; } if (!S_ISDIR(dir_i->i_mode)) { iput(dir_i); return -EPERM; } if (!(sb=read_super(dev))) { iput(dir_i); return -EBUSY; } if (sb->s_imount) { iput(dir_i); return -EBUSY; } if (dir_i->i_mount) { iput(dir_i); return -EPERM; } sb->s_imount=dir_i; dir_i->i_mount=1 ; dir_i->i_dirt=1 ; return 0 ; }
文件系统加载结点
//看imode区分inode类型,判断是不是目录文件。设备文件i结点找到设备号,第一个就是根i结点
//read super :先找有没有现成的
//simap 8个缓冲块,每个缓冲块1k,一字节8bit:64kbit,有64ki结点
//64M文件
//超级块里面有两个8个的指针数组。每个指向一个块,
1 2 3 4 5 6 7 struct super_block { ... struct buffer_head * s_imap [8]; struct buffer_head * s_zmap [8]; ... };
超级块在内存的信息里面用于存放i结点位图用的buffer head
*类型的数组。一个buffer head 1k
8个8k字节。一字节8位,所以对应了64K个inode。一个inode对应一个文件一个文件系统最多可以有64k个文件。同理一共64k个逻辑块,注意逻辑块也是块大小是1k,所以一共逻辑系统最多有64M大小的文件。
打开文件
打开文件的本质就是建立*filep【20】,file_table【64】
和inode_table【32】之间的联系
taskstruct 里面有:struct file * filp[NR_OPEN];
每个进程都有一个filep,而file_table和inode_table是内核持有的,只有一个。
image-20231211225204563
打开文件建立连接的过程:使用open函数打开文件,最终映射到系统调用sys_open
sys_open
总导图:
open文件流程
将进程的*filp和file_table挂接
分别找filp和file_table里面的空闲项,并建立他俩的连接关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int sys_open (const char * filename,int flag,int mode) { struct m_inode * inode ; struct file * f ; int i,fd; mode &= 0777 & ~current->umask; for (fd=0 ; fd<NR_OPEN ; fd++) if (!current->filp[fd]) break ; if (fd>=NR_OPEN) return -EINVAL; current->close_on_exec &= ~(1 <<fd); f=0 +file_table; for (i=0 ; i<NR_FILE ; i++,f++) if (!f->f_count) break ; if (i>=NR_FILE) return -EINVAL; (current->filp[fd]=f)->f_count++; ... }
获取文件的i结点:opennamei
例如要打开的文件路径是:/mnt/user/user1/user2/hellow.txt如何找到hellow.txt文件的i结点?
寻找过程
主要有目录文件结点和最终的文件结点。通过目录文件结点可以找到目录文件,里面的目录项指向了下一级的结点
1 2 3 4 5 6 7 8 9 10 11 12 int sys_open (const char * filename,int flag,int mode) { ... if ((i=open_namei(filename,flag,mode,&inode))<0 ) { current->filp[fd]=NULL ; f->f_count=0 ; return i; } ... }
这里调用了open_namei实现上述的寻找过程:
流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int open_namei (const char * pathname, int flag, int mode, struct m_inode ** res_inode) { const char * basename; int inr,dev,namelen; struct m_inode * dir , *inode ; struct buffer_head * bh ; struct dir_entry * de ; if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) flag |= O_WRONLY; mode &= 0777 & ~current->umask; mode |= I_REGULAR; if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; ... }
首先找到枝梢结点,也就是例子里面的user2目录文件inode结点
dir_namei
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static struct m_inode * dir_namei (const char * pathname, int * namelen, const char ** name) { char c; const char * basename; struct m_inode * dir ; if (!(dir = get_dir(pathname))) return NULL ; basename = pathname; while (c=get_fs_byte(pathname++)) if (c=='/' ) basename=pathname; *namelen = pathname-basename-1 ; *name = basename; return dir; }
get_dir
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 static struct m_inode * get_dir (const char * pathname) { char c; const char * thisname; struct m_inode * inode ; struct buffer_head * bh ; int namelen,inr,idev; struct dir_entry * de ; if (!current->root || !current->root->i_count) panic("No root inode" ); if (!current->pwd || !current->pwd->i_count) panic("No cwd inode" ); if ((c=get_fs_byte(pathname))=='/' ) { inode = current->root; pathname++; } else if (c) inode = current->pwd; else return NULL ; inode->i_count++; while (1 ) { thisname = pathname; if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { iput(inode); return NULL ; } for (namelen=0 ;(c=get_fs_byte(pathname++))&&(c!='/' );namelen++) ; if (!c) return inode; if (!(bh = find_entry(&inode,thisname,namelen,&de))) { iput(inode); return NULL ; } inr = de->inode; idev = inode->i_dev; brelse(bh); iput(inode); if (!(inode = iget(idev,inr))) return NULL ; } }
find_entry
findentry
1 2 3 4 struct dir_entry { unsigned short inode; char name[NAME_LEN]; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 static struct buffer_head * find_entry (struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir) { int entries; int block,i; struct buffer_head * bh ; struct dir_entry * de ; struct super_block * sb ; #ifdef NO_TRUNCATE if (namelen > NAME_LEN) return NULL ; #else if (namelen > NAME_LEN) namelen = NAME_LEN; #endif entries = (*dir)->i_size / (sizeof (struct dir_entry)); *res_dir = NULL ; if (!namelen) return NULL ; if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1 )=='.' ) { if ((*dir) == current->root) namelen=1 ; else if ((*dir)->i_num == ROOT_INO) { sb=get_super((*dir)->i_dev); if (sb->s_imount) { iput(*dir); (*dir)=sb->s_imount; (*dir)->i_count++; } } } if (!(block = (*dir)->i_zone[0 ])) return NULL ; if (!(bh = bread((*dir)->i_dev,block))) return NULL ; i = 0 ; de = (struct dir_entry *) bh->b_data; while (i < entries) { if ((char *)de >= BLOCK_SIZE+bh->b_data) { brelse(bh); bh = NULL ; if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) || !(bh = bread((*dir)->i_dev,block))) { i += DIR_ENTRIES_PER_BLOCK; continue ; } de = (struct dir_entry *) bh->b_data; } if (match(namelen,name,de)) { *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL ; }
iget
iget根据目录项中提供的设备号i结点号获取i结点。
首先在inode_table里面看有没有现成的,如果找不到再加载
整体流程图:
iget
返回dir_namei
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static struct m_inode * dir_namei (const char * pathname, int * namelen, const char ** name) { char c; const char * basename; struct m_inode * dir ; if (!(dir = get_dir(pathname))) return NULL ; basename = pathname; while (c=get_fs_byte(pathname++)) if (c=='/' ) basename=pathname; *namelen = pathname-basename-1 ; *name = basename; return dir; }
opennamei:获取目标文件i节点
上文已经讲述了获得枝梢结点的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 int open_namei (const char * pathname, int flag, int mode, struct m_inode ** res_inode) { ... if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; if (!namelen) { if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { *res_inode=dir; return 0 ; } iput(dir); return -EISDIR; } bh = find_entry(&dir,basename,namelen,&de); if (!bh) { if (!(flag & O_CREAT)) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; } inode = new_inode(dir->i_dev); if (!inode) { iput(dir); return -ENOSPC; } inode->i_uid = current->euid; inode->i_mode = mode; inode->i_dirt = 1 ; bh = add_entry(dir,basename,namelen,&de); if (!bh) { inode->i_nlinks--; iput(inode); iput(dir); return -ENOSPC; } de->inode = inode->i_num; bh->b_dirt = 1 ; brelse(bh); iput(dir); *res_inode = inode; return 0 ; } inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); if (flag & O_EXCL) return -EEXIST; if (!(inode=iget(dev,inr))) return -EACCES; if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || !permission(inode,ACC_MODE(flag))) { iput(inode); return -EPERM; } inode->i_atime = CURRENT_TIME; if (flag & O_TRUNC) truncate(inode); *res_inode = inode; return 0 ; }
将文件i结点与file_table[64]挂接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int sys_open (const char * filename,int flag,int mode) { ... if ((i=open_namei(filename,flag,mode,&inode))<0 ) { current->filp[fd]=NULL ; f->f_count=0 ; return i; } if (S_ISCHR(inode->i_mode)) if (MAJOR(inode->i_zone[0 ])==4 ) { if (current->leader && current->tty<0 ) { current->tty = MINOR(inode->i_zone[0 ]); tty_table[current->tty].pgrp = current->pgrp; } } else if (MAJOR(inode->i_zone[0 ])==5 ) if (current->tty<0 ) { iput(inode); current->filp[fd]=NULL ; f->f_count=0 ; return -EPERM; } if (S_ISBLK(inode->i_mode)) check_disk_change(inode->i_zone[0 ]); f->f_mode = inode->i_mode; f->f_flags = flag; f->f_count = 1 ; f->f_inode = inode; f->f_pos = 0 ; return (fd); }
namei
将给定的文件路径名映射 到其 i 节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 struct m_inode * namei (const char * pathname) { const char * basename; int inr,dev,namelen; struct m_inode * dir ; struct buffer_head * bh ; struct dir_entry * de ; if (!(dir = dir_namei(pathname,&namelen,&basename))) return NULL ; if (!namelen) return dir; bh = find_entry(&dir,basename,namelen,&de); if (!bh) { iput(dir); return NULL ; } inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); dir=iget(dev,inr); if (dir) { dir->i_atime=CURRENT_TIME; dir->i_dirt=1 ; } return dir; }
izone :;一个块1k。一个号unsigned short
2字节,一个块:512个号-》512k
inode_table里面的inode只有一个,但是允许别人重复打开,因此有conunt引用计数
读文件
sys_read
读文件由用户调用read完成
read函数最终映射到sys_read()系统调用执行
读文件的offset在file结构体里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int sys_read (unsigned int fd,char * buf,int count) { struct file * file ; struct m_inode * inode ; if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd])) return -EINVAL; if (!count) return 0 ; verify_area(buf,count); inode = file->f_inode; if (inode->i_pipe) return (file->f_mode&1 )?read_pipe(inode,buf,count):-EIO; if (S_ISCHR(inode->i_mode)) return rw_char(READ,inode->i_zone[0 ],buf,count,&file->f_pos); if (S_ISBLK(inode->i_mode)) return block_read(inode->i_zone[0 ],&file->f_pos,buf,count); if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) { if (count+file->f_pos > inode->i_size) count = inode->i_size - file->f_pos; if (count<=0 ) return 0 ; return file_read(inode,file,buf,count); } printk("(Read)inode->i_mode=%06o\n\r" ,inode->i_mode); return -EINVAL; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void verify_area (void * addr,int size) { unsigned long start; start = (unsigned long ) addr; size += start & 0xfff ; start &= 0xfffff000 ; start += get_base(current->ldt[2 ]); while (size>0 ) { size -= 4096 ; write_verify(start); start += 4096 ; } } void write_verify (unsigned long address) { unsigned long page; if (!( (page = *((unsigned long *) ((address>>20 ) & 0xffc )) )&1 )) return ; page &= 0xfffff000 ; page += ((address>>10 ) & 0xffc ); if ((3 & *(unsigned long *) page) == 1 ) un_wp_page((unsigned long *) page); return ; }
file_read
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int file_read (struct m_inode * inode, struct file * filp, char * buf, int count) { int left,chars,nr; struct buffer_head * bh ; if ((left=count)<=0 ) return 0 ; while (left) { if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) { if (!(bh=bread(inode->i_dev,nr))) break ; } else bh = NULL ; nr = filp->f_pos % BLOCK_SIZE; chars = MIN( BLOCK_SIZE-nr , left ); filp->f_pos += chars; left -= chars; if (bh) { char * p = nr + bh->b_data; while (chars-->0 ) put_fs_byte(*(p++),buf++); brelse(bh); } else { while (chars-->0 ) put_fs_byte(0 ,buf++); } } inode->i_atime = CURRENT_TIME; return (count-left)?(count-left):-ERROR; }
_bmap
1 2 3 4 int bmap (struct m_inode * inode,int block) { return _bmap(inode,block,0 ); }
新建文件
示意图
creat函数,最终映射到sys_create函数,调用sys_open新建文件,和之前打开文件调用open不一样,这回opennamei没有这个i结点会返回空
1 2 3 4 5 6 7 8 9 10 int sys_open (const char * filename,int flag,int mode) { ... if ((i=open_namei(filename,flag,mode,&inode))<0 ) { current->filp[fd]=NULL ; f->f_count=0 ; return i; } ... }
1 2 3 4 5 6 7 8 int open_namei (const char * pathname, int flag, int mode, struct m_inode ** res_inode) { ... if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static struct buffer_head * find_entry (struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir) { ... while (i < entries) { ... if (match(namelen,name,de)) { *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL ; }
最终find_entry会返回null
因此opennamei执行:if (!bh) 里面的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int open_namei (const char * pathname, int flag, int mode, struct m_inode ** res_inode) { ... bh = find_entry(&dir,basename,namelen,&de); if (!bh) { if (!(flag & O_CREAT)) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; } inode = new_inode(dir->i_dev); if (!inode) { iput(dir); return -ENOSPC; } inode->i_uid = current->euid; inode->i_mode = mode; inode->i_dirt = 1 ; bh = add_entry(dir,basename,namelen,&de); if (!bh) { inode->i_nlinks--; iput(inode); iput(dir); return -ENOSPC; } de->inode = inode->i_num; bh->b_dirt = 1 ; brelse(bh); iput(dir); *res_inode = inode; return 0 ; } ... }
new_inode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct m_inode * new_inode (int dev) { struct m_inode * inode ; struct super_block * sb ; struct buffer_head * bh ; int i,j; if (!(inode=get_empty_inode())) return NULL ; if (!(sb = get_super(dev))) panic("new_inode with unknown device" ); j = 8192 ; for (i=0 ; i<8 ; i++) if (bh=sb->s_imap[i]) if ((j=find_first_zero(bh->b_data))<8192 ) break ; if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) { iput(inode); return NULL ; } if (set_bit(j,bh->b_data)) panic("new_inode: bit already set" ); bh->b_dirt = 1 ; inode->i_count=1 ; inode->i_nlinks=1 ; inode->i_dev=dev; inode->i_uid=current->euid; inode->i_gid=current->egid; inode->i_dirt=1 ; inode->i_num = j + i*8192 ; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; return inode; }
add_entry
如果在目录文件中找到空闲项,则在此处加载
目录文件示意图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 static struct buffer_head * add_entry (struct m_inode * dir, const char * name, int namelen, struct dir_entry ** res_dir) { int block,i; struct buffer_head * bh ; struct dir_entry * de ; *res_dir = NULL ; #ifdef NO_TRUNCATE if (namelen > NAME_LEN) return NULL ; #else if (namelen > NAME_LEN) namelen = NAME_LEN; #endif if (!namelen) return NULL ; if (!(block = dir->i_zone[0 ])) return NULL ; if (!(bh = bread(dir->i_dev,block))) return NULL ; i = 0 ; de = (struct dir_entry *) bh->b_data; while (1 ) { if ((char *)de >= BLOCK_SIZE+bh->b_data) { brelse(bh); bh = NULL ; block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); if (!block) return NULL ; if (!(bh = bread(dir->i_dev,block))) { i += DIR_ENTRIES_PER_BLOCK; continue ; } de = (struct dir_entry *) bh->b_data; } 时 if (i*sizeof (struct dir_entry) >= dir->i_size) { de->inode=0 ; dir->i_size = (i+1 )*sizeof (struct dir_entry); dir->i_dirt = 1 ; dir->i_ctime = CURRENT_TIME; } if (!de->inode) { dir->i_mtime = CURRENT_TIME; for (i=0 ; i < NAME_LEN ; i++) de->name[i]=(i<namelen)?get_fs_byte(name+i):0 ; bh->b_dirt = 1 ; *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL ; } int create_block (struct m_inode * inode, int block) { return _bmap(inode,block,1 ); }
_bmap
直接索引部分 (block<7)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static int _bmap(struct m_inode * inode,int block,int create){ struct buffer_head * bh ; int i; if (block<0 ) panic("_bmap: block<0" ); if (block >= 7 +512 +512 *512 ) panic("_bmap: block>big" ); if (block<7 ) { if (create && !inode->i_zone[block]) if (inode->i_zone[block]=new_block(inode->i_dev)) { inode->i_ctime=CURRENT_TIME; inode->i_dirt=1 ; } return inode->i_zone[block]; } block -= 7 ; ... }
bmap
一级索引部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static int _bmap(struct m_inode * inode,int block,int create){ ... if (block<512 ) { if (create && !inode->i_zone[7 ]) if (inode->i_zone[7 ]=new_block(inode->i_dev)) { inode->i_dirt=1 ; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[7 ]) return 0 ; if (!(bh = bread(inode->i_dev,inode->i_zone[7 ]))) return 0 ; i = ((unsigned short *) (bh->b_data))[block]; if (create && !i) if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block]=i; bh->b_dirt=1 ; } brelse(bh); return i; } block -= 512 ; ... }
bmap1
二级索引部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static int _bmap(struct m_inode * inode,int block,int create){ ... if (create && !inode->i_zone[8 ]) if (inode->i_zone[8 ]=new_block(inode->i_dev)) { inode->i_dirt=1 ; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[8 ]) return 0 ; if (!(bh=bread(inode->i_dev,inode->i_zone[8 ]))) return 0 ; i = ((unsigned short *)bh->b_data)[block>>9 ]; if (create && !i) if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block>>9 ]=i; bh->b_dirt=1 ; } brelse(bh); if (!i) return 0 ; if (!(bh=bread(inode->i_dev,i))) return 0 ; i = ((unsigned short *)bh->b_data)[block&511 ]; if (create && !i) if (i=new_block(inode->i_dev)) { ((unsigned short *) (bh->b_data))[block&511 ]=i; bh->b_dirt=1 ; } brelse(bh); return i; }
new block
new block
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 int new_block (int dev) { struct buffer_head * bh ; struct super_block * sb ; int i,j; if (!(sb = get_super(dev))) panic("trying to get new block from nonexistant device" ); j = 8192 ; for (i=0 ; i<8 ; i++) if (bh=sb->s_zmap[i]) if ((j=find_first_zero(bh->b_data))<8192 ) break ; if (i>=8 || !bh || j>=8192 ) return 0 ; if (set_bit(j,bh->b_data)) panic("new_block: bit already set" ); bh->b_dirt = 1 ; j += i*8192 + sb->s_firstdatazone-1 ; if (j >= sb->s_nzones) return 0 ; if (!(bh=getblk(dev,j))) panic("new_block: cannot get block" ); if (bh->b_count != 1 ) panic("new block: count is != 1" ); clear_block(bh->b_data); bh->b_uptodate = 1 ; bh->b_dirt = 1 ; brelse(bh); return j; }
写文件
写入文件最终映射到sys_write,sys_write判断要写的文件的类型,对于目录文件调用file_write
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 int file_write (struct m_inode * inode, struct file * filp, char * buf, int count) { off_t pos; int block,c; struct buffer_head * bh ; char * p; int i=0 ; if (filp->f_flags & O_APPEND) pos = inode->i_size; else pos = filp->f_pos; while (i<count) { if (!(block = create_block(inode,pos/BLOCK_SIZE))) break ; if (!(bh=bread(inode->i_dev,block))) break ; c = pos % BLOCK_SIZE; p = c + bh->b_data; bh->b_dirt = 1 ; c = BLOCK_SIZE-c; if (c > count-i) c = count-i; pos += c; if (pos > inode->i_size) { inode->i_size = pos; inode->i_dirt = 1 ; } i += c; while (c-->0 ) *(p++) = get_fs_byte(buf++); brelse(bh); } inode->i_mtime = CURRENT_TIME; if (!(filp->f_flags & O_APPEND)) { filp->f_pos = pos; inode->i_ctime = CURRENT_TIME; } return (i?i:-1 ); }
关闭文件
关闭文件
注意file_table[64]是所有进程共享的,所以这里是减小引用计数而不是清零。当引用计数为0的时候空闲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int sys_close (unsigned int fd) { struct file * filp ; if (fd >= NR_OPEN) return -EINVAL; current->close_on_exec &= ~(1 <<fd); if (!(filp = current->filp[fd])) return -EINVAL; current->filp[fd] = NULL ; if (filp->f_count == 0 ) panic("Close: file count is 0" ); if (--filp->f_count) return (0 ); iput(filp->f_inode); return (0 ); }
删除文件
删除文件
i_nlinks
linux文件可以建立链接。文件没多被链接一次文件的的inode的i_nlinks会加1。也就是多个目录项链接到一个文件inode
i_nlinks
sys_unlink
检查文件是否能被删除。函数整个流程和打开文件很类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 int sys_unlink (const char * name) { const char * basename; int namelen; struct m_inode * dir , * inode ; struct buffer_head * bh ; struct dir_entry * de ; if (!(dir = dir_namei(name,&namelen,&basename))) return -ENOENT; if (!namelen) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EPERM; } bh = find_entry(&dir,basename,namelen,&de); if (!bh) { iput(dir); return -ENOENT; } if (!(inode = iget(dir->i_dev, de->inode))) { iput(dir); brelse(bh); return -ENOENT; } if ((dir->i_mode & S_ISVTX) && !suser() && current->euid != inode->i_uid && current->euid != dir->i_uid) { iput(dir); iput(inode); brelse(bh); return -EPERM; } if (S_ISDIR(inode->i_mode)) { iput(inode); iput(dir); brelse(bh); return -EPERM; } if (!inode->i_nlinks) { printk("Deleting nonexistent file (%04x:%d), %d\n" , inode->i_dev,inode->i_num,inode->i_nlinks); inode->i_nlinks=1 ; } de->inode = 0 ; bh->b_dirt = 1 ; brelse(bh); inode->i_nlinks--; inode->i_dirt = 1 ; inode->i_ctime = CURRENT_TIME; iput(inode); iput(dir); return 0 ; }
### iput
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 void iput (struct m_inode * inode) { if (!inode) return ; wait_on_inode(inode); if (!inode->i_count) panic("iput: trying to free free inode" ); if (inode->i_pipe) { wake_up(&inode->i_wait); if (--inode->i_count) return ; free_page(inode->i_size); inode->i_count=0 ; inode->i_dirt=0 ; inode->i_pipe=0 ; return ; } if (!inode->i_dev) { inode->i_count--; return ; } if (S_ISBLK(inode->i_mode)) { sync_dev(inode->i_zone[0 ]); wait_on_inode(inode); } repeat: if (inode->i_count>1 ) { inode->i_count--; return ; } if (!inode->i_nlinks) { truncate(inode); free_inode(inode); return ; } if (inode->i_dirt) { write_inode(inode); wait_on_inode(inode); goto repeat; } inode->i_count--; return ; }
truncate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void truncate (struct m_inode * inode) { int i; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) return ; for (i=0 ;i<7 ;i++) if (inode->i_zone[i]) { free_block(inode->i_dev,inode->i_zone[i]); inode->i_zone[i]=0 ; } free_ind(inode->i_dev,inode->i_zone[7 ]); free_dind(inode->i_dev,inode->i_zone[8 ]); inode->i_zone[7 ] = inode->i_zone[8 ] = 0 ; inode->i_size = 0 ; inode->i_dirt = 1 ; inode->i_mtime = inode->i_ctime = CURRENT_TIME; }
free_inode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void free_inode (struct m_inode * inode) { struct super_block * sb ; struct buffer_head * bh ; if (!inode) return ; if (!inode->i_dev) { memset (inode,0 ,sizeof (*inode)); return ; } if (inode->i_count>1 ) { printk("trying to free inode with count=%d\n" ,inode->i_count); panic("free_inode" ); } if (inode->i_nlinks) panic("trying to free inode with links" ); if (!(sb = get_super(inode->i_dev))) panic("trying to free inode on nonexistent device" ); if (inode->i_num < 1 || inode->i_num > sb->s_ninodes) panic("trying to free inode 0 or nonexistant inode" ); if (!(bh=sb->s_imap[inode->i_num>>13 ])) panic("nonexistent imap in superblock" ); if (clear_bit(inode->i_num&8191 ,bh->b_data)) printk("free_inode: bit already cleared.\n\r" ); bh->b_dirt = 1 ; memset (inode,0 ,sizeof (*inode)); }