OPPO最新ipad是哪款款是哪款

查看: 4710|回复: 13
记一次数据库核心对象(CON$表的I_CON2索引)损坏的修复过程
求职 : 论坛徽章:5
上一篇帖子已经说明了问题是如何产生的,这一篇主要是把整个解决过程详细记录下来(忽略失败的步骤),以供有同样问题的同学可以借鉴下。
一:分析问题:
最初呈现给我们的错误是这样的,如下图:
pri.jpg (13.81 KB, 下载次数: 32)
13:19 上传
拿这个ORA-08102去网上搜索,很快就知道了 这是一个索引建和表中的值 不符而导致的问题。
查看这个对象号为52的对象,发现是 SYS.CON$ 表中 I_CON2索引 ,这是 一个 BOOTSTRP$ 对象,而且 OBJ# 为51 ,所以这是一个 核心BOOTSTRP$ 对象,
是不能通过startup migrate 和 event 38003 重建的,所以最终只能通过BBED去修改这个块中有问题的地方。
先通过下面这个SQL,查找表和索引之间到底相差什么
sql&SELECT /*+ FULL(t1) */
owner#, NAME, con#
&&FROM CON$ t1
SELECT /*+ index(t I_CON2) */
owner#, NAME, con#
&&FROM CON$
OWNER#& && && && && && && & NAME& && && && && & CON#& &
& &0& && && && && && & _NEXT_CONSTRAINT& && &60009& &&&
第一次看到这个SQL时,真心佩服想到这个SQL的人,能利用这样的方法来查询表和索引之间的不一致 ,
通过查询结果 确实可以看到 表和索引存在不一样的,就是CON$表中有 CON#=60009的这一列,而索引的键值中却没有(要知道索引的键值就是保存该索引对应的字段值啊)
我们再去看看alter日志:
因为这个报错会有日志信息显示在alter日志中,所以现在去查看alter日志:
Errors in file /oracle/diag/rdbms/hollycrm/hollycrm/trace/hollycrm_ora_3795.trc:
Fri Feb 14 12:09:24 2014
Trace dumping is performing id=[cdmp_24]
在去查看这个trace文件,立刻能看到这个错误地方:
oer 8102.2 - obj# 52, rdba: 0x00419ff1(afn 1, blk# 106481)
kdk key 8102.2:
&&ncol: 1, len: 5
&&key: (5):&&04 c3 07 01 0a
&&mask: (2048):
该错误也明确,错误对象为52 , 在文件1 ,块106481 中,然后看 key的值,
第一个16进制数 04不知道代表什么意思,后面 4个字节c3 07 01 0a 正好是60009在数据库中存储的16进制表示,
sql&SELECT DUMP(6') FROM
Typ=2 Len=4: c3,7,1,a
通过上诉的 分析 ,现在我们基本可以确定,问题是这样的:
在文件1 块106481中有一个索引条目,它应该指向CON$表中 CON#=60009 这一条记录,
这个索引条目 的索引键 应该是 60009 (对应16进制为c3 07 01 0a) ,它的ROWID应该就是CON$表上 CON#=60009 这一条记录的实际ROWID;
但是现在 这个索引条目的索引键不是60009,所以才导致了我们的问题。
二:定位问题
知道了问题所在,那现在就是定位具体错误位置的时候了,根据上面的分析,
我们首先要得到CON$表中 CON#=60009 这一行的实际ROWID:
SELECT rawtohex(r.rowid) rh,
& && &&&dbms_rowid.rowid_relative_fno(ROWID) fno,
& && &&&dbms_rowid.rowid_block_number(ROWID) bno,
& && &&&dbms_rowid.rowid_row_number(ROWID) rno
& &FROM con$ r
&&WHERE NAME = '_NEXT_CONSTRAINT' --AND con#=60009;
& && && && && && &rh& && && && && && && &fno& && && &&&bno& && && && & rno
C& && &&&1& && && && & 289& && && && & 12
我把后面con#='60009'注释掉了,这是因为用CON#=60009直接查不出来,原因应该就是这个查询使用了I_CON2索引,而索引中没有个这个键值的条目,
ROWID的格式为:对象号(6个字符)& && && &文件号(3个字符)& && & 块号(6个字符)& && & 行号(3个字符) ,
通过rawtohex函数可以转换为16进制,现在得到了 这条记录的 文件号,块号,行号的16进制为:00 40 01 21 00 0C,因为索引条目上保存的ROWID也是文件号,块号,行号!
现在我们得到了这个记录的实际ROWID,下一步就是去索引块里去查找了ROWID了。
我们将这个索引块dump出来:
sql&alter system dump datafile 1 block 106481;
system alter
sql&oradebug setmypid
Statement processed.
sql&oradebug tracefile_name
/oracle/diag/rdbms/hollycrm/hollycrm/trace/hollycrm_ora_13537.trc
下面我们来查看这个dump文件:
直接看叶块部分:
Leaf block dump
===============
header address x7f968c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 1
kdxcosdc 1
kdxconro 375
kdxcofbo 786=0x312
kdxcoavs 2327
kdxlespl 0
kdxlende 4
kdxlenxt 0=0x0
kdxleprv x419ff0
kdxledsz 6
kdxlebksz 7984
row#0[7971] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 01
col 0; len 4; (4):&&c3 06 59 5f
row#1[7958] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 02
col 0; len 4; (4):&&c3 06 59 60
row#2[7945] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 03
col 0; len 4; (4):&&c3 06 59 61
row#3[7932] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 04
col 0; len 4; (4):&&c3 06 59 62
row#4[7919] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 05
col 0; len 4; (4):&&c3 06 59 63
row#5[7906] flag: ------, lock: 0, len=13, data6):&&00 42 71 f4 00 06
col 0; len 4; (4):&&c3 06 59 64
row#6[7894] flag: ------, lock: 0, len=12, data6):&&00 42 71 f4 00 07
col 0; len 3; (3):&&c3 06 5a
。。。。。。
row#372[2138] flag: ---D--, lock: 2, len=13, data6):&&00 42 71 f2 00 1c
col 0; len 4; (4):&&c3 06 64 40
row#373[2112] flag: ---D--, lock: 2, len=13, data6):&&00 42 71 f2 00 1d
col 0; len 4; (4):&&c3 06 64 41
row#374[2125] flag: ------, lock: 0, len=13, data6):&&00 40 01 21 00 0c
col 0; len 4; (4):&&c3 06 64 42
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 106481 maxblk 10648
这个索引块中一共有375行,我们随便看其中一行
row#374[2125] flag: ------, lock: 0, len=13, data:(6):&&00 40 01 21 00 0c
col 0; len 4; (4):&&c3 06 64 42
这里有两个地方比较重要,一个是 data:(6):&&00 40 01 21 00 0c ,另一个是col 0; len 4; (4):&&c3 06 64 42
可能在不同的版本中,这里格式不一样,我这里是11gR2的,
data:(6):&&00 40 01 21 00 0c 这里代表的是ROWID的信息,
col 0; len 4; (4):&&c3 06 64 42 这里代表的是索引键的值
通过查询,很快就找到了这一行:
row#374[2125] flag: ------, lock: 0, len=13, data:(6):
00 40 01 21 00 0c
col 0; len 4; (4):&&c3 06 64 42
而我们看到这个索引条目上的索引键为 c3 06 64 42 确实不是 我们表上的 c3 07 01 0a,
索引条目算是找到了,现在就要把这个索引条目在这个块上的偏移量找到就OK了,
偏移量offset的计算方式,网上有个帖子是这么说的:
偏移量=N+44+24*itl N是行数据的补偿 44是 kcbh(cache层)长度+ktbbh(事务层)长度 24是一个itl的长度 总共加起来其实就是行数据的offset
不过帖子里也没说是为什么,见
我也按照这个方法计算了,得到 *3=2268
现在地方也找到了,只需要把这里的c3 06 64 42改为c3 07 01 0a 就OK了。
三:解决问题
最后我们通过BBED来修改这个块,
BBED& set dba 1,106481 offset 2268&&
& && &&&DBA& && && && & 0x0481)
& && &&&OFFSET& && && & 2268
BBED& d /v
File: /oracle/oradata/hollycrm/system01.dbf (1)
Block: 106481&&Offsets: 2268 to 2779&&Dba:0x00419ff1
-------------------------------------------------------
c04c306 6442f2 l @.!...?.....Bq?
001c04c3 c04 l ...?d@...@.!...
c0d 04c30664 l ?dA...Bq?..?d
l ?...@.!...?d@..
04001 l .Bq? .?d&...@.
42 71f2001e l !...?d?...Bq?.
04c000 c04c306 l .?d=...@.!...?
......................................
42 71f20020 l !...?d/...Bq?
&16 bytes per line&
可以看到,这里就是我们要找的地方了,然后我们就是要修改红色部分,
BBED&modify /x c307 offset 2274
BBED&modify /x 0101 offset 2276
我们再看一下这个地方:
BBED& d /v
File: /oracle/oradata/hollycrm/system01.dbf (1)
Block: 106481&&Offsets: 2268 to 2779&&Dba:0x00419ff1
-------------------------------------------------------
c04c307 010af2 l @.!...?.....Bq?
001c04c3 c04 l ...?d@...@.!...
c0d 04c30664 l ?dA...Bq?..?d
l ?...@.!...?d@..
04001 l .Bq? .?d&...@.
42 71f2001e l !...?d?...Bq?.
04c000 c04c306 l .?d=...@.!...?
......................................
42 71f20020 l !...?d/...Bq?
&16 bytes per line&
BBED& sum apply
最后刷新下数据库的缓冲区
SQL&alter system flush buffer_
再次创建索引,约束时,不再报错了。
以下是老白大神(百鳝)的一篇有用的帖子
论坛徽章:0
认证徽章论坛徽章:764
论坛徽章:166
论坛徽章:166
本帖最后由 jieyancai 于
16:50 编辑
SQL& SELECT utl_raw.cast_to_number('c307010a') FROM
UTL_RAW.CAST_TO_NUMBER('C307010A')
----------------------------------
& && && && && && && && && &&&60009
SQL& SELECT utl_raw.cast_from_number(60009) FROM
UTL_RAW.CAST_FROM_NUMBER(60009)
----------------------------------------------------------
论坛徽章:16
求职 : 认证徽章论坛徽章:123
& && & 谢谢分享&&&&
求职 : 论坛徽章:5
jieyancai 发表于
SQL& SELECT utl_raw.cast_to_number('c307010a') FROM
非常感谢!这个函数确实很有用,可以反转dump函数的结果,
论坛徽章:7
论坛徽章:19
没看懂这个查询:
sql&SELECT /*+ FULL(t1) */
owner#, NAME, con#
&&FROM CON$ t1
SELECT /*+ index(t I_CON2) */
owner#, NAME, con#
&&FROM CON$
能否解析一下?
itpub.net All Right Reserved. 北京皓辰网域网络信息技术有限公司版权所有    
 北京市公安局海淀分局网监中心备案编号: 广播电视节目制作经营许可证:编号(京)字第1149号博客访问: 933191
博文数量: 126
博客积分: 0
博客等级: 民兵
技术积分: 4587
注册时间:
将晦涩难懂的技术讲的通俗易懂
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
声明:本Linux文件系统博客,共分四节,是根据网上多个相关博客,以及自己的理解加上相关资料总结而成。(作者:lvyilong316)
VFS采用的是面向对象的设计思想,使用一簇数据结构来代表通用文件对象。所有内核中的数据结构都使用C结构体实现。
1.superblock(超级块)对象
保存一个挂在的文件系统的相关信息(Stores&information&concerning&a&mounted&filesystem.&For&disk-based&filesystems,&this&object&usually&corresponds&to&a&filesystem&control&block&stored&on&disk.)
(1)超级块用来描述特定文件系统的信息。它存放在磁盘特定的扇区中&,它在使用的时候将信息存在于内存中。
(2)当内核对一个文件系统进行初始化和注册时在内存为其分配一个超级块,这就是VFS超级块。即,VFS超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时被自动删除&。
1.1&数据结构
(3)超级块对象由结构体&super_block来体现。VFS超级块的数据结构为&super_block在include/linux/fs.h中可以查看
struct&super_block&{
我们先来看一个图,再来具体解释:
其中主要的数据成员和解释如下:
(1)&&s_list&:所有的超级块形成一个双联表,s_list.prev和s_list.next分别指向与当前超级块相邻的前一个元素和后一个元素。
(2)&&s_lock&:保护链表免受多处理器系统上的同时访问。
(3)&&s_fs_info:&字段指向具体文件系统的超级块。
例如:超级块对象指的是Ext2文件系统,该字段就指向ext2_sb_info数据结构。
(4)&&s_dirt&:来表示该超级块是否是脏的,也就是说,磁盘上的数据是否必须要更新。
(5)&&超级块对象是通过函数alloc_super()创建并初始化的。在文件系统安装时,内核会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中&。
1.2操作定义
超级对象中最重要的就是s_op,每一种文件系统都应该有自己的super_operations操作实例。它指向超级块的操作函数表,&它由struct&super_operations结构体来表示。
现在来看一下它的定义:它的定义在&include/linux/fs.h头文件中可以看到
1560struct&super_operations&{
1561&&&&&&&&struct&inode&*(*alloc_inode)(struct&super_block&*sb);&
1562&&&&&&&&void&(*destroy_inode)(struct&inode&*);&
1564&&&&&&&&void&(*dirty_inode)&(struct&inode&*);&
1565&&&&&&&&int&(*write_inode)&(struct&inode&*,&struct&writeback_control&*wbc);&
1566&&&&&&&&int&(*drop_inode)&(struct&inode&*);&
1567&&&&&&&&void&(*evict_inode)&(struct&inode&*);
1568&&&&&&&&void&(*put_super)&(struct&super_block&*);&
1569&&&&&&&&void&(*write_super)&(struct&super_block&*);&
1570&&&&&&&&int&(*sync_fs)(struct&super_block&*sb,&int&wait);&
1571&&&&&&&&int&(*freeze_fs)&(struct&super_block&*);
1572&&&&&&&&int&(*unfreeze_fs)&(struct&super_block&*);
1573&&&&&&&&int&(*statfs)&(struct&dentry&*,&struct&kstatfs&*);&
1574&&&&&&&&int&(*remount_fs)&(struct&super_block&*,&int&*,&char&*);
1575&&&&&&&&void&(*umount_begin)&(struct&super_block&*);
1577&&&&&&&&int&(*show_options)(struct&seq_file&*,&struct&vfsmount&*);
1578&&&&&&&&int&(*show_stats)(struct&seq_file&*,&struct&vfsmount&*);
1579#ifdef&CONFIG_QUOTA
1580&&&&&&&&ssize_t&(*quota_read)(struct&super_block&*,&int,&char&*,&size_t,&loff_t);
1581&&&&&&&&ssize_t&(*quota_write)(struct&super_block&*,&int,&const&char&*,&size_t,&loff_t);
1582#endif
1583&&&&&&&&int&(*bdev_try_to_free_page)(struct&super_block*,&struct&page*,&gfp_t);
(1)&可以看到该结构体中的每一项都是一个指向超级块操作函数的指针,超级块操作函数执行文件系统和索引节点的底层操作。
(2)当文件系统需要对超级块执行操作时,要在超级块对象中寻找需要的操作方法。
例如:一个文件系统要写自己的超级块,需要调用:
sturct&super_block&*&
sb->s_op->write_super(sb);
sb是指向文件系统超级块的指针,沿着该指针进入超级块操作函数表,并从表中取得writ_super()函数,该函数执行写入超级块的实际操作。
尽管writ_super()方法来自超级块,但是在调用时,还是要把超级块作为参数传递给它。因为没有C++的this指针。
(3)具体操作说明
struct&inode&*&alloc_inode(struct&super_block&*&sb)&:创建和初始化一个新的索引结点。
void&destroy_inode(struct&super_block&*sb)&:释放指定的索引结点&。
void&dirty_inode(struct&inode&*inode)&:VFS在索引节点被修改时会调用此函数。
void&&write_inode(struct&inode&*inode,&struct&writeback_control&*wbc)&将指定的inode写回磁盘。
void&&drop_inode(&struct&inode&*&inode):删除索引节点。
void&&&put_super(struct&super_block&*sb)&:用来释放超级块。
void&&write_super(struct&super_block&*sb):更新磁盘上的超级块。
void&&&sync_fs(struct&super_block&*sb,in&wait):使文件系统的数据元素与磁盘上的文件系统同步,wait参数指定操作是否同步。
int&statfs(struct&super_block&*sb,struct&statfs&*statfs):获取文件系统状态。把文件系统相关的统计信息放在statfs中。
2.VFS的索引节点(ls查看的信息)
(文件或目录的静态描述信息,不随进程不同而变化)
(1)&保存一个文件的通用信息,每个inode有一个inode&number,在文件系统中,一个inode&number能够唯一地标识一个文件(Stores&general&information&about&a&specific&file.&For&disk-based&filesystems,&this&object&usually&corresponds&to&a&file&control&block&stored&on&disk.&Each&inode&object&is&associated&with&an&inode&number,&which&uniquely&identifies&the&file&within&the&filesystem.)
(2)&文件系统处理文件或目录时的所有信息都存放在称为索引节点的数据结构中。文件名可以随时改,但是索引节点对文件是唯一的(它是随文件的存在而存在)。
(3)&具体文件系统的索引节点是存放在磁盘上的,是一种静态结构,要使用它,必须将其调入内存,填写&VFS的索引节点。VFS索引节点也称为动态节点。(即索引节点仅当文件被访问时才在内存中创建)
2.1数据结构
它的定义在&/include/linux/fs.h中有这个结构体的定义
&struct&inode&{
还记的我们在终端下输入命令:ls&命令后可以看到文件的信息,这些信息就是记录在这里的。这是为什么呢?
我们知道,文件是由FCB(文件控制块控制的),而具体到Linux下,文件是有索引节点结构控制的。所以在struct&inode&里存放了文件的基本信息。大家有没有发现在怎么在索引节点里面会包含超级块的对象呢,有些人可能不明白了,先看看下面的图,再来解释把。
从上面对的图我们可以看出索引节点&对象靠i_sb指回到了超级块对象。&
成员说明:&&&&&&&
i_hash&:为了提高查找inode的效率,每一个inode都会有一个hash值。该字段指向hash值相同的inode所形成的双链表该字段包含prev和next两个指针,分别指向上述链表的前一个元素和后一个元素;
i_list&:所有索引结点形成的双联表,(从图上可以看出,索引节点对象是靠它来链接的)
i_dentry&:所有引用该inode的目录项将形成一个双联表,该字段即为这个双联表的头结点
i_ino&:索引结点号。通过ls&-i命令可以查看文件的索引节点号;
i_count&:引用计数;
i_nlink&:硬链接数。当该inode描述一个目录时,这个值至少为2,代表.和..的数目;
(注:索引节点没有软连接数,软连接会对应单独的索引节点)
i_uid&:inode所属文件的拥有者的id,通过ls&-n可查看拥有者id;
i_gid&:inode所属文件所在组的id,通过ls&-n可查看组id;
i_rdev&:如果该inode描述的是一个设备文件,此值为设备号;
i_blkbits&:以位为单位的块大小;
i_atime&:文件最近一次被访问的时间。通过ls&-lu可查看该时间;
i_mtime&:文件最近一次被修改的时间,这里的修改只文件内容被修改。通过ls&-l可查看该时间;
i_ctime&:文件最近一次被修改的时间,这里的修改除了指文件内容被修改外,更强调的是文件的属性被修改。通过ls&-lc可查看该时间;
i_blocks&:文件使用块的个数,通过ls&-s可以查看该某个文件的块使用数目;
i_mode&:文件的访问权限;
i_op&:&&&&指向索引结点操作结构体的指针;
i_fop&:&&指向文件操作街头体的指针;
i_sb&:&&&&指向inode所属文件系统的超级块的指针;
i_pipe&:如果inode所代表的文件是一个管道,则使用该字段;
i_bdev&:如果inode所代表的文件是一个块设备,则使用该字段;
i_cdev&:如果inode所代表的文件是一个字符设备,则使用该字段;
i_state&&:&&索引节点的状态信息。
(1)&在同一个文件系统中,每个索引节点号都是唯一的,内核可以根据索引节点号的散列值来查找其inode结构。
(2)&inode中有两个设备号i_dev和i_rdev。
a.&除特别文件外,每个节点都存储在某个设备上,这就是i_dev。
b.&如果索引节点所代表的并不是常规文件,而是某个设备,则需要另一个设备号,这就是i_rdev。
(3)&对i_state的说明:
每个VFS索引节点都会复制磁盘索引节点包含的一些数据,比如文件占有的磁盘数。如果i_state&的值等于I_DIR,该索引节点就是“脏“的。也就是说,对应的磁盘索引节点必须被更新。
(4)&三个重要的双向链表:
a.未用索引节点链表,正在使用索引节点链表和脏索引节点链表。每个索引节点对象总是出现在上面三种的一个。
b.这3个链表都是通过索引节点的i_list&域链接在一起的。
c.属于“正在使用“或“脏“链表的索引节点对象也同时存放在一个散列表中。
(4)&一个索引节点代表文件系统中的一个文件,它也可以是设备或管道这样的特殊文件。所以在索引节点结构体中有一些和特殊文件相关的项。
(6)&有时候某些文件系统并不能完整地包含索引节点结构体要求的所有信息。那么此时刚怎么办呢?
此时,可以给它赋一些其它的值。例如:一个文件系统可能并不记录文件的访问时间,这时就可以在i_atime中存储0。
(7)&i_list和i_sb_list的区别
a.i_list:VFS中使用四个链表来管理不同状态的inode结点。inode_unused将当前未使用的inode链接起来,inode_in_use将当前正在被使用的inode链接起来,超级块中的s_dirty将所有脏inode链接起来,i_hash将所有hash值相同的inode链接起来。i_list中包含prev和next两个指针,分别指向与当前inode处于同一个状态链表的前后两个元素
b.i_sb_list:每个文件系统中的inode都会形成一个双联表,这个双链表的头结点存放在超级块的s_inodes中。而该字段中的prev和next指针分别指向在双链表中与其相邻的前后两个元素
c.索引结点中i_sb_list链表是链接一个文件系统中所有inode的链表,因此相邻的inode之间均会由此链表链接;而i_list链接的是处于同一个状态的所有inode。所以,相邻inode之间并不一定链接在一起。
2.2操作说明
与索引节点关联的方法叫索引节点操作表,它是在&struct&inode_operations这个结构体中具体描述的。它的定义在&include/linux/fs.h头文件中定义。
1516struct&inode_operations&{
1517&&&&&&&&int&(*create)&(struct&inode&*,struct&dentry&*,int,&struct&nameidata&*);&
1518&&&&&&&&struct&dentry&*&(*lookup)&(struct&inode&*,struct&dentry&*,&struct&nameidata&*);&
1519&&&&&&&&int&(*link)&(struct&dentry&*,struct&inode&*,struct&dentry&*);&
1520&&&&&&&&int&(*unlink)&(struct&inode&*,struct&dentry&*);&
1521&&&&&&&&int&(*symlink)&(struct&inode&*,struct&dentry&*,const&char&*);&
1522&&&&&&&&int&(*mkdir)&(struct&inode&*,struct&dentry&*,int);&
1523&&&&&&&&int&(*rmdir)&(struct&inode&*,struct&dentry&*);&
1524&&&&&&&&int&(*mknod)&(struct&inode&*,struct&dentry&*,int,dev_t);&
1525&&&&&&&&int&(*rename)&(struct&inode&*,&struct&dentry&*,
1526&&&&&&&&&&&&&&&&&&&&&&&&struct&inode&*,&struct&dentry&*);&
1527&&&&&&&&int&(*readlink)&(struct&dentry&*,&char&__user&*,int);&
1528&&&&&&&&void&*&(*follow_link)&(struct&dentry&*,&struct&nameidata&*);&
1529&&&&&&&&void&(*put_link)&(struct&dentry&*,&struct&nameidata&*,&void&*);&
1530&&&&&&&&void&(*truncate)&(struct&inode&*);&
1531&&&&&&&&int&(*permission)&(struct&inode&*,&int);&
1532&&&&&&&&int&(*check_acl)(struct&inode&*,&int);
1533&&&&&&&&int&(*setattr)&(struct&dentry&*,&struct&iattr&*);&
1534&&&&&&&&int&(*getattr)&(struct&vfsmount&*mnt,&struct&dentry&*,&struct&kstat&*);&
1535&&&&&&&&int&(*setxattr)&(struct&dentry&*,&const&char&*,const&void&*,size_t,int);&
1536&&&&&&&&ssize_t&(*getxattr)&(struct&dentry&*,&const&char&*,&void&*,&size_t);&
1537&&&&&&&&ssize_t&(*listxattr)&(struct&dentry&*,&char&*,&size_t);&
1538&&&&&&&&int&(*removexattr)&(struct&dentry&*,&const&char&*);&
1539&&&&&&&&void&(*truncate_range)(struct&inode&*,&loff_t,&loff_t);
1540&&&&&&&&long&(*fallocate)(struct&inode&*inode,&int&mode,&loff_t&offset,
1541&&&&&&&&&&&&&&&&&&&&&&&&&&loff_t&len);
1542&&&&&&&&int&(*fiemap)(struct&inode&*,&struct&fiemap_extent_info&*,&u64&start,
1543&&&&&&&&&&&&&&&&&&&&&&u64&len);
现在我们对其中一些重要的结果进行分析:
create()&:如果该inode描述一个目录文件,那么当在该目录下创建或打开一个文件时,内核必须为这个文件创建一个inode。VFS通过调用该inode的i_op->create()函数来完成上述新inode的创建。该函数的第一个参数为该目录的&inode,第二个参数为要打开新文件的dentry,第三个参数是对该文件的访问权限。如果该inode描述的是一个普通文件,那么该inode永远都不会调用这个create函数;
lookup()&:查找指定文件的dentry;
link&:用于在指定目录下创建一个硬链接。这个link函数最终会被系统调用link()调用。该函数的第一个参数是原始文件的dentry,第二个参数即为上述指定目录的inode,第三个参数是链接文件的dentry。
symlink&():在某个目录下新建软连接(符号链接),第一个参数是原始文件所在目录的inode,第二个参数是原始文件的dentry,第三个参数是符号链接的名字(const&char&*)。
(关于硬链接,软连接与dentry和inode的关系以及的具体创建参考后面)
unlink&():在某个目录下删除dentry指定的索引节点对象。这个unlink函数最终会被系统调用unlink()调用。&第一个参数即为上述硬链接所在目录的inode,第二个参数为要删除文件的dentry。
(可以看到硬链接的创建就是dentry的创建,删除就是删除一个dentry)
mkdir:在指定的目录下创建一个子目录,当前目录的inode会调用i_op->mkdir()。该函数会被系统调用mkdir()调用。第一个参数即为指定目录的inode,第二个参数为子目录的dentry,第三个参数为子目录权限;(目录与子目录是通过目录inode中的dentry链相连的,而子目录的dentry又指向子目录自身的inode)
rmdir&():从inode所描述的目录中删除一个指定的子目录时,该函数会被系统调用rmdir()最终调用;
mknod()&:在指定的目录下创建一个特殊文件,比如管道、设备文件或套接字等。
1.&对于不同的文件系统,上面的每个函数的具体实现是不同的,也不是每个函数都必须实现,没有实现的函数对应的域应当设置为NULL&。
2.&上面我们说了两个主要的操作对像:superblock和inode。它们两个对象中都包含一个操作对象。super_operations和inode_opetations它们有什么区别呢
a.super_operations对象:其中包括内核针对特定文件系统所有调用的方法。
b.inode_operations对象:&其中包括内核对特定文件的所有调用的方法。
所以它们一个是针对文件系统,一个是针对文件&。
3.本来inode中应该包括“目录节点”的名称,但由于硬链接的存在,导致一个物理文件可能有多个文件名,因此把和“目录节点”名称相关的部分从&inode&中分开,放在一个专门的&dentry&结构(目录项)中。
3.VFS&中的目录项对象
为了方便查找,VFS引入了目录项,每个dentry代表路径中的一个特定部分。目录项也可包括安装点。&可能还是不明白,没关系,看看老外怎么说的——保存一个目录的链接信息(Stores&information&about&the&linking&of&a&directory&entry&(that&is,&a&particular&name&of&the&file)&with&the&corresponding&file.&Each&disk-based&filesystem&stores&this&information&in&its&own&particular&way&on&disk.)个人理解:描述一个文件和一个名字的对应关系,或者说dentry就是一个“文件名”。
3.1数据结构
目录项对象由dentry结构体表示&,定义在文件linux/dcache.h&头文件中。&
&struct&dentry&{
1.索引节点中的i_dentry指向了它目录项,目录项中的d_alias,d_inode又指会了索引节点对象,目录项中的d_sb又指回了超级块对象。
2.我们可以看到不同于VFS&中的索引节点对象和超级块对象,目录项对象中没有对应磁盘的数据结构,所以说明目录项对象并没有真正标存在磁盘上,那么它也就没有脏标志位。
3.目录项的状态(被使用,未被使用和负状态)
它们是靠d_count的值来进行区分的,当d_count为正值说明目录项处于被使用状态。当d_count=0时表示该目录项是一个未被使用的目录项,&但其d_inode指针仍然指向相关的的索引节点。该目录项仍然包含有效的信息,只是当前没有人引用他。d_count=NULL表示负(negative)状态,与目录项相关的inode对象不复存在(相应的磁盘索引节点可能已经被删除),dentry对象的d_inode&指针为NULL。但这种dentry对象仍然保存在dcache中,以便后续对同一文件名的查找能够快速完成。这种dentry对象在回收内存时将首先被释放。
4.&d_subdirs:如果当前目录项是一个目录,那么该目录下所有的子目录(一级子目录)形成一个链表。该字段是这个链表的表头;
5.&d_child:如果当前目录项是一个目录,那么该目录项通过这个字段加入到父目录的d_subdirs链表当中。这个字段中的next和prev指针分别指向父目录中的另外两个子目录;
6.&d_alias:一个inode可能对应多个目录项,所有的目录项形成一个链表。inode结构中的i_dentry即为这个链表的头结点。当前目录项以这个字段处于i_dentry链表中。该字段中的prev和next指针分别指向与该目录项同inode的其他两个(如果有的话)目录项。
3.2dentry和inode的区别
 inode(可理解为ext2&inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,其中的d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量。所谓"文件",&就是按一定的形式存储在介质上的信息,所以一个文件其实包含了两方面的信息,一是存储的数据本身,二是有关该文件的组织和管理的信息。在内存中,&每个文件都至少有一个dentry(目录项)和inode(索引节点)结构,dentry记录着文件名,上级目录等信息,正是它形成了我们所看到的树状结构;而有关该文件的组织和管理的信息主要存放inode里面,它记录着文件在存储介质上的位置与分布。同时dentry->d_inode指向相应的inode结构。dentry与inode是多对一的关系,因为有可能一个文件有好几个文件名(硬链接)。
3.3&操作定义
对目录项进行操作的一组函数叫目录项操作表,由dentry_operation结构描述。它可以在&include/linux/dcache.h&中查到
&134struct&dentry_operations&{
&135&&&&&&&&int&(*d_revalidate)(struct&dentry&*,&struct&nameidata&*);
&136&&&&&&&&int&(*d_hash)&(struct&dentry&*,&struct&qstr&*);
&137&&&&&&&&int&(*d_compare)&(struct&dentry&*,&struct&qstr&*,&struct&qstr&*);
&138&&&&&&&&int&(*d_delete)(struct&dentry&*);
&139&&&&&&&&void&(*d_release)(struct&dentry&*);
&140&&&&&&&&void&(*d_iput)(struct&dentry&*,&struct&inode&*);
&141&&&&&&&&char&*(*d_dname)(struct&dentry&*,&char&*,&int);
(1)&int&d_reavlidate(struct&dentry&*dentry&,int&flags)&该函数判断目录对象是否有效。VFS准备从dcache中使用一个目录项时,会调用该函数.
(2)&int&d_hash(struct&dentry&*dentry&,struct&qstr&*name):该目录生成散列值,当目录项要加入到散列表时,VFS要调用此函数。
(3)&int&d_compare(&struct&dentry&*dentry,&struct&qstr&*name1,&struct&qstr&*name2)&该函数来比较name1和name2这两个文件名。使用该函数要加dcache_lock锁。
(4)&int&d_delete(struct&dentry&*dentry):当d_count=0时,VFS调用次函数。使用该函数要叫&dcache_lock锁。
(5)&void&d_release(struct&dentry&*dentry):当该目录对象将要被释放时,VFS调用该函数。
(6)&void&d_iput(struct&dentry&*dentry,struct&inode&*inode)当一个目录项丢失了其索引节点时,VFS就掉用该函数。
4.VFS中的文件对象&(与进程有关)
保存一个打开的文件与一个进程的关系(Stores&information&about&the&interaction&between&an&open&file&and&a&process.&This&information&exists&only&in&kernel&memory&during&the&period&when&a&process&has&the&file&open.)
1.文件对象表示进程已经打开的文件&在内存中的表示,该对象不是物理上的文件。它是由相应的open()系统调用创建,由close()系统调用销毁。多个进程可以打开和操作同一个文件,所以同一个文件也可能存在多个对应的文件对象。&
2一个文件对应的文件对象不是唯一的,但对应的索引节点和超级块对象是唯一的。&
3.file结构中保存了文件位置,此外,还把指向该文件索引节点的指针也放在其中。
4.1数据结构
file结构形成一个双链表,称为系统打开文件表&。它的定义在&include/linux/fs.h&中可以看到&
struct&file&{
1.文件对象实际上没有对应的磁盘数据,所以在结构体中没有代表其对象是否为脏,是否需要写回磁盘的标志。文件对象通过f_path.dentry指针指向相关的目录项对象。目录项会指向相关的索引节点,索引节点会记录文件是否是脏的。
2.fu_list:每个文件系统中以被打开的文件都会形成一个双联表,这个双联表的头结点存放在超级块的s_files字段中。该字段的prev和next指针分别指向在链表中与当前文件结构体相邻的前后两个元素.
file结构中主要保存了文件当前的偏移量,此外还把指向该文件索引节点的指针也放在其中。有人就问了,问什么不直接把文件位置存放在索引节点中呢?
因为:Linux中的文件是能够共享的,假如把文件位置存放在索引节点中,当有两个或更多个进程同时打开一个文件时,它们将去访问同一个索引节点,那么一个进程的lseek操作将影响到另一个进程的读操作,这显然是致命的错误。
4.2&操作定义
对文件进行操作的一组函数叫文件操作表,由file_operations结构定义:可以在include/linux/fs.h&中查看&
1488struct&file_operations&{
1489&&&&&&&&struct&module&*
1490&&&&&&&&loff_t&(*llseek)&(struct&file&*,&loff_t,&int);
1491&&&&&&&&ssize_t&(*read)&(struct&file&*,&char&__user&*,&size_t,&loff_t&*);
1492&&&&&&&&ssize_t&(*write)&(struct&file&*,&const&char&__user&*,&size_t,&loff_t&*);
1493&&&&&&&&ssize_t&(*aio_read)&(struct&kiocb&*,&const&struct&iovec&*,&unsigned&long,&loff_t);
1494&&&&&&&&ssize_t&(*aio_write)&(struct&kiocb&*,&const&struct&iovec&*,&unsigned&long,&loff_t);
1495&&&&&&&&int&(*readdir)&(struct&file&*,&void&*,&filldir_t);
1496&&&&&&&&unsigned&int&(*poll)&(struct&file&*,&struct&poll_table_struct&*);
1497&&&&&&&&long&(*unlocked_ioctl)&(struct&file&*,&unsigned&int,&unsigned&long);
1498&&&&&&&&long&(*compat_ioctl)&(struct&file&*,&unsigned&int,&unsigned&long);
1499&&&&&&&&int&(*mmap)&(struct&file&*,&struct&vm_area_struct&*);
1500&&&&&&&&int&(*open)&(struct&inode&*,&struct&file&*);
1501&&&&&&&&int&(*flush)&(struct&file&*,&fl_owner_t&id);
1502&&&&&&&&int&(*release)&(struct&inode&*,&struct&file&*);
1503&&&&&&&&int&(*fsync)&(struct&file&*,&int&datasync);
1504&&&&&&&&int&(*aio_fsync)&(struct&kiocb&*,&int&datasync);
1505&&&&&&&&int&(*fasync)&(int,&struct&file&*,&int);
1506&&&&&&&&int&(*lock)&(struct&file&*,&int,&struct&file_lock&*);
1507&&&&&&&&ssize_t&(*sendpage)&(struct&file&*,&struct&page&*,&int,&size_t,&loff_t&*,&int);
1508&&&&&&&&unsigned&long&(*get_unmapped_area)(struct&file&*,&unsigned&long,&unsigned&long,&unsigned&long,&unsigned&long);
1509&&&&&&&&int&(*check_flags)(int);
1510&&&&&&&&int&(*flock)&(struct&file&*,&int,&struct&file_lock&*);
1511&&&&&&&&ssize_t&(*splice_write)(struct&pipe_inode_info&*,&struct&file&*,&loff_t&*,&size_t,&unsigned&int);
1512&&&&&&&&ssize_t&(*splice_read)(struct&file&*,&loff_t&*,&struct&pipe_inode_info&*,&size_t,&unsigned&int);
1513&&&&&&&&int&(*setlease)(struct&file&*,&long,&struct&file_lock&**);
(1)&owner:用于指定拥有这个文件操作结构体的模块,通常取THIS_MODULE;
(2)&llseek:用于设置文件的偏移量。第一个参数指明要操作的文件,第二个参数为偏移量,第三个参数为开始偏移的位置(可取SEEK_SET,SEEK_CUR和SEEK_END之一)。
(3)&read:从文件中读数据。第一个参数为源文件,第二个参数为目的字符串,第三个参数指明欲读数据的总字节数,第四个参数指明从源文件的某个偏移量处开始读数据。由系统调用read()调用;
(4)&write:往文件里写数据。第一个参数为目的文件,第二个参数源字符串,第三个参数指明欲写数据的总字节数,第四个参数指明从目的文件的某个偏移量出开始写数据。由系统调用write()调用;
(5)&mmap:将指定文件映射到指定的地址空间上。由系统调用mmap()调用;
(6)&open:打开指定文件,并且将这个文件和指定的索引结点关联起来。由系统调用open()调用;
(7)&release:释放以打开的文件,当打开文件的引用计数(f_count)为0时,该函数被调用;
(8)&fsync():文件在缓冲的数据写回磁盘
5.四大VFS对象总结
1、超级块对象和inode对象分别对应有物理数据,在磁盘上有静态信息。而目录项对象和文件对象描述的是一种关系,前者描述的文件与文件名的关系,后者描述的是进程与文件的关系,所以没有对应物理数据。
eg:有三个不同的进程打开同一个文件,其中有两个进程使用了相同的硬链接。三个进程拥有各自的file&object,而只有两个dentry(同一个硬链接对应一个dentry,dentry不随进程打开文件而增加或改变)。两个dentry都指向同一个inode。
2、进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述被打开文件的属性,如文件的当前偏移量等信息。
3、两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。
4、在存储介质中,每个文件对应唯一的inode结点,但是每个文件又可以有多个文件名。即可以通过不同的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。
4)Inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。
5)不同的dentry则是同个文件链接(ln命令)来实现的
阅读(2374) | 评论(0) | 转发(3) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。

我要回帖

更多关于 oppo最新手机报价 的文章

 

随机推荐