[原]UNIX内核(4):inode及其相关操作
来源:百度文库 编辑:神马文学网 时间:2024/04/28 05:21:27
早期的UNIX系统最重要的两大功能是:文件存储/访问,任务/进程调度(多任务)。由这两大功能衍生出了内存管理,设备管理,用户接口等功能。在这里就来说说其中第一个重要的功能:文件系统。 在UNIX系统上,所有一切都被当成文件来对待,包括设备。因此,就需要一个系统来管理这些文件,并提供一个统一的操作接口。inode就是用来管理文件的一个数据结构(或者说是模型、类)。每个文件在磁盘上都对应一个inode,在访问文件时,inode会被加载到内存中,使用结束后内存中的inode被释放。如果删除文件(unlink),磁盘上的inode及文件的数据都会被释放。 inode是一个文件在系统中的惟一标识。一个文件可以由多个名字(link),但是只占用一个inode。当文件处于active状态时(比如被打开),内存中保留一份inode的拷贝,与inode一起保留在内存中的对象成为inode的核内拷贝(in-core copy,以下用核内inode代替)。 另外,由于inode存放在磁盘上,因此加载到内存中,以及在内存中操作时需要用到Buffer cache(参考“UNIX内核(2):磁盘缓冲原理,缓冲分配、回收及用OO观点建模”),稍后加以讨论。好了,那就开始观察inode吧。 inode结构 如上所示,inode有磁盘inode和核内inode。其实大部分情况下说的都是存放在磁盘上的inode。一个inode对象包含如下成员:
- 文件属主。分成单个属主和组属主。
- 文件类型:普通文件、目录、设备文件、FIFO等
- 访问权限
- 文件访问时间:上次的文件修改/访问/inode修改时间
- 链接数:至少是1
- 文件数据块的地址表
- 文件大小
- 状态。指示是否锁定、有进程等待、核内inode是否与磁盘inode不同(包括对文件数据及inode本身的修改)、挂接点
- 逻辑设备号
- inode号
- 指向其他核内inode的指针。主要是用于将其放在hash Q与free list上进行操作。
- 引用计数
dev_t i_dev; // 设备号,用于核内inode
unsigned long i_ino; // inode号:因为在磁盘上连续存储,通过索引就可找到某个inode // 用于核内inode
umode_t i_mode;
nlink_t i_nlink; // 链接设置
uid_t i_uid; // 属主信息
gid_t i_gid;
dev_t i_rdev;
off_t i_size;
time_t i_atime; // 时间戳
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
struct inode_operations * i_op; // 对inode及文件的操作集合。该结构体包含对inode及文件 // 操作的函数指针,针对不同的inode, // 可以设置不同的操作集合。 // 这里有一点多态的影子。做过设备驱动的同学对此会有印象。
struct super_block * i_sb; // 超级块,日后再议
struct wait_queue * i_wait; // 等待队列。用于同步(睡眠/唤醒)
struct file_lock * i_flock;
struct vm_area_struct * i_mmap; // 映射的内存区
struct inode * i_next, * i_prev; // 用于核内inode
struct inode * i_hash_next, * i_hash_prev; // 用于核内inode // 剩余的信息貌似与本文主题无关,暂不讨论。日后再议
// …… unsigned short i_count; // 引用计数。用于核内inode // …… };
核内inode的分配与回收 分配: INode iget(int fs_inode_no) { while (未完成) { if (inode在cache中) { if (inode被锁住) { sleep (inode解锁事件); 重试; } // 对挂接点的特殊处理。日后再议 if (inode在free list上) 从free list移出; ++引用计数; 返回该inode; } // inode不在cache中 if (没有可用inode) 返回错误; // 因为inode占用时间较长,从文件打开到关闭通常要很久, // 此处进程选择等待将导致挂死。这里处理与getblk()不同 从free list移出; 重置inode号、文件系统号; 放到新的hash Q中; 从磁盘上读取bread(); 初始化其他成员如引用计数; 返回该inode; } } 由于inode的标识是fs NO和inode NO,因此从磁盘上读取时,需要做如下转换(因为一次读取一个磁盘块,而inode通常比磁盘块小): 块号 = ((inode号 -1) / 每块的inode数) + 磁盘inode首块块号 读到inode所在块后,就可以计算inode在块中偏移量: 偏移量 = ((inode号 - 1) % 每块的inode数) * 磁盘inode大小 还是来看看Linux 0.99.15的实现: struct inode * __iget(struct super_block * sb, int nr, int crossmntp)
{
static struct wait_queue * update_wait = NULL;
struct inode_hash_entry * h;
struct inode * inode;
struct inode * empty = NULL; if (!sb)
panic("VFS: iget with sb==NULL");
h = hash(sb->s_dev, nr); // 获取fs号
repeat:
for (inode = h->inode; inode ; inode = inode->i_hash_next)
if (inode->i_dev == sb->s_dev && inode->i_ino == nr)
goto found_it; // 在hash Q上找到 // 未在hash Q上找到
if (!empty) {
h->updating++;
empty = get_empty_inode();
if (!--h->updating)
wake_up(&update_wait);
if (empty)
goto repeat;
return (NULL); // free list上无可用inode
}
inode = empty; // 设置各成员值
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
inode->i_ino = nr;
inode->i_flags = sb->s_flags;
put_last_free(inode); // 放到新的hash Q上
insert_inode_hash(inode); // 从磁盘读取inode
read_inode(inode);
goto return_it; found_it:
if (!inode->i_count)
nr_free_inodes--;
inode->i_count++;
wait_on_inode(inode);
if (inode->i_dev != sb->s_dev || inode->i_ino != nr) {
printk("Whee.. inode changed from under us. Tell Linus\n");
iput(inode);
goto repeat;
} // 跨挂接点
if (crossmntp && inode->i_mount) {
struct inode * tmp = inode->i_mount;
tmp->i_count++;
iput(inode);
inode = tmp;
wait_on_inode(inode);
}
if (empty)
iput(empty); return_it:
while (h->updating)
sleep_on(&update_wait);
return inode;
} 回收: void iput (In_Core_Inode * iNode) { 加锁; --引用计数; if (引用计数为0) { if (链接计数为0) { 释放文件所占磁盘块; 设置文件类型为0; 释放磁盘inode,ifree(); } if (inode被更新) 将更新写到磁盘inode; 将该核内inode放到free list上; } 解锁; } Linux 0.99.15的实现: void iput(struct inode * inode)
{
if (!inode)
return;
wait_on_inode(inode); // 等待该inode被释放
// …… if (inode->i_pipe)
wake_up_interruptible(&PIPE_WAIT(*inode));
repeat:
if (inode->i_count>1) {
inode->i_count--; // 减少引用计数
return;
}
wake_up(&inode_wait); // 解锁
if (inode->i_pipe) {
unsigned long page = (unsigned long) PIPE_BASE(*inode);
PIPE_BASE(*inode) = NULL;
free_page(page);
}
if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
inode->i_sb->s_op->put_inode(inode);
if (!inode->i_nlink) // 链接数为0,此处并未释放磁盘inode
return;
}
if (inode->i_dirt) {
write_inode(inode); /* we can sleep - so do again */
wait_on_inode(inode);
goto repeat;
}
inode->i_count--;
nr_free_inodes++;
return;
} 由于Linux中不区分核内inode和磁盘inode,因此实现上与这里描述的算法有较大差别。 参考: The Design of The UNIX Operation System, by Maurice J. Bach Linux Kernel Source Code v0.99.15, by Linus Torvalds Linux Kernel Source Code v2.6.22, by Linus Torvalds and Linux community. Understanding The Linux Kernel, 3rd edition, by Daniel P. Bovet, Marco Cesati Copyleft (C) 2007 raof01. 本文可以用于除商业用途外的所有用途。若要用于商业用途,请与作者联系。
[原]UNIX内核(4):inode及其相关操作
[原]UNIX内核(5):inode与文件、目录
[原]UNIX内核(5):inode与文件、目录
[原]UNIX内核(6):super block管理inode和磁盘块 - 操作系统 - ...
[原]UNIX内核(12):进程的结构——上下文
[原]UNIX内核(3):磁盘读写以及磁盘缓冲的利弊 - 操作系统 - raof01-Ha...
[原]UNIX内核(10):FS系统调用——改变文件系统结构mount()和umount(...
[原]UNIX内核(12):进程的结构——上下文 - 操作系统 - raof01-Hast...
UNIX系统操作命令大全(一)
Windows & Unix 文件格式解密, 空格 与 tab 转换及其相关工具
[保留] 嵌入式Linux内核移植相关代码分析(zz)
类unix 系统 内核研究 各阶段学习导引
UNIX内核:加锁解锁——等待事件及唤醒
[保留] 嵌入式Linux内核移植相关代码分析(zz) - ChinaUnix.net
嵌入式Linux内核移植相关代码分析
Java繁体中文处理完全攻略(一) - JAVA - - 开源/Linux/Unix相关 ...
ChinaUnix.net - 写给Linux内核新手-关于Linux内核学习的误区 - 中国Unix技术社区
国学的原义、演变及内核
董氏掌诊及其相关应用
yaffs文件系统在2.6内核上的移植--操作步骤(转载)
位操作相关技术
电脑相关操作技巧
UNIX 高手的10 个习惯(提高 Shell 操作的习惯)
UNIX 高手的10 个习惯(提高 Shell 操作的习惯)