咱們知道,linux文件系統,inode和dentry是有對應關係的,dentry是文件名或者目錄的一個管理結構,2.6內核中:node
struct dentry { atomic_t d_count; unsigned int d_flags; spinlock_t d_lock; int d_mounted; struct inode *d_inode; struct hlist_node d_hash; struct dentry *d_parent; struct qstr d_name; struct list_head d_lru; union { struct list_head d_child; struct rcu_head d_rcu; } d_u; struct list_head d_subdirs; struct list_head d_alias; unsigned long d_time; const struct dentry_operations *d_op; struct super_block *d_sb; void *d_fsdata; unsigned char d_iname[32]; } SIZE: 192
3.10的內核中以下:linux
struct dentry { unsigned int d_flags; seqcount_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr d_name; struct inode *d_inode; unsigned char d_iname[32]; struct lockref d_lockref; const struct dentry_operations *d_op; struct super_block *d_sb; unsigned long d_time; void *d_fsdata; struct list_head d_lru; union { struct list_head d_child; struct callback_head d_rcu; } d_u; struct list_head d_subdirs; struct hlist_node d_alias; }
對比兩個結構,其實主要成員變化不大,好比d_parent,d_name,d_iname,下面以2.6爲例來描述proc文件系統的父子關係,而父子關係中,主要就是由d_parent,d_u中的ios
d_child,d_subdirs這三個成員來維護的。
給定一個dentry:ide
crash> struct dentry 0xffff8818014b1540 struct dentry { d_count = { counter = 1 }, d_flags = 0, d_lock = { raw_lock = { slock = 196611 } }, d_mounted = 0, d_inode = 0xffff88100a8117f8,-----------指向這個dentry對應的inode d_hash = { next = 0x0, pprev = 0xffffc90000b13890 }, d_parent = 0xffff8818118002c0,----------指向父節點,也是一個dentry d_name = { hash = 3255717505, len = 8, name = 0xffff8818014b15e0 "slabinfo"------------------------文件名 }, d_lru = { next = 0xffff8818014b1580, prev = 0xffff8818014b1580 }, d_u = { d_child = { next = 0xffff880c117eb710, prev = 0xffff881811800320 }, d_rcu = { next = 0xffff880c117eb710, func = 0xffff881811800320 } }, d_subdirs = { next = 0xffff8818014b15a0, prev = 0xffff8818014b15a0 }, d_alias = { next = 0xffff88100a811828, prev = 0xffff88100a811828 }, d_time = 0, d_op = 0xffffffff81622b00 <proc_file_inode_operations+160>, d_sb = 0xffff880c1188cc00, d_fsdata = 0x0, d_iname = "slabinfo\000_offset\000ndler.py\000m\000\000\000\000"
能夠看出:當前節點的文件名是slabinfo, 0xffff8818118002c0就是這個slabinfo的父節點,
crash> dentry 0xffff8818118002c0 struct dentry { d_count = { counter = 725 }, d_flags = 16, d_lock = { raw_lock = { slock = 797912975 } }, d_mounted = 0, d_inode = 0xffff880c11402cf8, d_hash = { next = 0x0, pprev = 0x0 }, d_parent = 0xffff8818118002c0,--------------對應的parent是自身,也就是當前是頂級節點。 d_name = { hash = 0, len = 1, name = 0xffff881811800360 "/"--------------當前節點名字,已是根目錄名字了 }, d_lru = { next = 0xffff881811800300, prev = 0xffff881811800300 }, d_u = { d_child = { next = 0xffff881811800310,-------d_u到dentry的偏移是0x50,也就是d_u的自己地址是310,而後這個地址的prev和next是本身自己,說明是根節點 prev = 0xffff881811800310 }, d_rcu = { next = 0xffff881811800310, func = 0xffff881811800310 } }, d_subdirs = {--------------------------這個鏈表中,存放的就是子節點的d_u的地址 next = 0xffff8818014b1590, prev = 0xffff880c0dd74590 }, d_alias = { next = 0xffff880c11402d28, prev = 0xffff880c11402d28 }, d_time = 0, d_op = 0x0, d_sb = 0xffff880c1188cc00, d_fsdata = 0x0, d_iname = "/\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" }
雖然咱們知道slabinfo的目錄是/proc/slabinfo,那爲何slabinfo的父目錄就已是「/」,而不是proc呢?由於進入/proc,則意味着切換了文件系統,/proc是掛載點。函數
咱們來看他父目錄的子目錄/文件的目錄項鍊表,也就是d_suddir 成員。咱們知道,dentry能夠經過d_parent來尋找本身的父dentry,比較有意思的是,dentry的d_u,會造成一條雙向循環鏈表,按道理,雙向循環鏈表是沒有頭的,但因爲他們的父dentry的d_suddir 也嵌入在這個列表中,全部就能夠從父dentry的d_suddirs 的地址來做爲list的頭來遍歷:atom
crash> struct -xo dentry.d_subdirs ffff8818118002c0 struct dentry { [ffff881811800320] struct list_head d_subdirs; } crash> list ffff881811800320 ffff881811800320 ffff8818014b1590-----------------------這個就是slabinfo這個文件的d_u的地址 ffff880c117eb710 ffff8804ee7510d0 ffff880c0df82f50 ffff880db397a850 ffff88180c6359d0 ffff880c0de13110 ffff880a28539910 ffff880c0dc589d0 ffff880bfeee9e50 ffff8805cbccde90 ffff880bfed53410 ffff8812ac422650 ffff8810794cecd0 ffff881811812c10 ffff8812ac661190 ffff88181180cf10 ffff881801543c10 ffff880797d60550 ffff88100aa00890 ffff88100aa004d0 ffff88180140da90 ffff880a28569590 ffff880c11648b90 ffff880430752d90 ffff8804305dddd0 ffff8804b1f3ef50 ffff88126055f310 ffff88126055fcd0 ffff88126055f6d0 ffff881801561610 ffff881052e7fe90 ffff88126055f550 ffff880c1167ead0 ffff880c0a046850 ffff880c0dfd5710 ffff880c0dd40310 ffff880c0dd40610 ffff8804b1f2c550 ffff8810ead120d0 ffff880ffc7633d0 ffff8808af56ef10 ffff880bfed2d550 ffff880a284baf50 ffff880c0ddccf50
在這個list出來的地址中,都是dentry的d_u成員的地址,也就是均可以獲取到對應的dentry,咱們找到slabinfo這個文件的d_u的地址爲:spa
crash> struct -xo dentry.d_u 0xffff8818014b1540 struct dentry { union { [ffff8818014b1590] } d_u; }
也就是說,dentry能夠經過d_parent成員直接找到父節點的dentry的地址,而父節點的dentry,能夠經過遍歷d_subdirs 找到全部子文件或者子目錄的地址,這個地址偏移以後,就能夠獲取到設計
對應的dentry,也就是父子關係造成。固然,並非全部的子文件或者子目錄都能遍歷到,由於若是沒人打開,則不會有dentry,畢竟內存有限。3d
有個地方須要注意,該文件系統的最高根目錄,那麼它的child按道理要嵌入到它父dentry的d_subdirs 造成鏈表,但因爲他的父dentry就是自己,因此它的child指針仍是指向本身,也就是:指針
crash> struct -xo dentry.d_u ffff8818118002c0 struct dentry { union { [ffff881811800310] } d_u; } crash> crash> crash> struct -x dentry.d_u.d_child ffff8818118002c0 d_u.d_child = { next = 0xffff881811800310, prev = 0xffff881811800310 },
內核中一樣用兩個成員來表示分級關係的還有:task_struct 中的children,sibling成員,父進程的child成員和子進程的sibling成員傳成一個雙向循環鏈表。咱們本身設計到層級的結構的時候,能夠參照這種設計方法。
注:用一個3.10的例子描述下:常常須要用到的一個結構是name,好比我須要知道某個inode對應的文件名,因爲一個inode能夠對應多個文件名,因此在inode結構中並無文件名的直接對應,這個文件名是放在dentry中的,
crash> struct dentry.d_parent,d_name,d_iname ffff885221936780 d_parent = 0xffff88579984b140----------------對應的父dentry d_name = { { { hash = 1776607972, len = 7 }, hash_len = 31841379044 }, name = 0xffff8852219367b8 "vmlinux" } d_iname = "vmlinux\000nity_list\000.wants\000\064\000\000\000\000\000" crash> struct dentry.d_parent,d_name,d_iname 0xffff88579984b140------查看其父dentry的文件名 d_parent = 0xffff88579984ac00 d_name = { { { hash = 4190344536, len = 26 }, hash_len = 115859494232 }, name = 0xffff88579984b178 "3.10.0-693.21.1.el7.x86_64" } d_iname = "3.10.0-693.21.1.el7.x86_64\000\000\000\000\000"
經過獲取d_name結構中的name,能夠獲取文件名,依次往上遍歷d_parent,就能夠獲取整個路徑名。
一個比較特殊的是根目錄,這個根目錄是指掛載的根目錄,它的d_parent是自身,並且其名字是"/"
因此要獲取完整的路徑,能夠在內核模塊中參照以下的寫法:
void get_inode_filename(struct dentry *dentry, char *filename) { struct dentry *temp; const char *item[128]; int i = 0; temp = dentry; strcpy(filename, ""); do { item[i++] = temp->d_name.name; if (temp == temp->d_parent || !temp->d_parent) break; temp = temp->d_parent; }while (1); while ( i > 0 ) { strcat(filename, item[i - 1]); i --; if ( i > 0 && strcmp(item[i], "/") ) strcat(filename, "/"); } }
很明顯的,如上的方法有一種硬傷,就是它遍歷到的根目錄,就是這個文件歸屬的掛載點,而咱們知道,一個文件的多級目錄,可能屬於多個掛載點,那怎麼獲取進一步的全路徑呢?
咱們來看下面的例子:
crash> files 317933 PID: 317933 TASK: ffff8853a6c10fd0 CPU: 3 COMMAND: "ZMSSdu" ROOT: / CWD: /tmp FD FILE DENTRY INODE TYPE PATH 0 ffff882536c4c000 ffff88268e6bc480 ffff8827d88d73a0 CHR /dev/pts/32 1 ffff8853d5859500 ffff8828b700a240 ffff8857bf568850 CHR /dev/null 2 ffff8853d5859500 ffff8828b700a240 ffff8857bf568850 CHR /dev/null 3 ffff8816b12a8100 ffff8834653c26c0 ffff88518d4ef9c0 REG /var/log/zmsscmd/ZMSSdu.log 4 ffff88268f550200 ffff8811cadccd80 ffff880505b992f0 REG /mnt/ZMSS/ZMSSDu.log--------------------做爲例子的文件 5 ffff8829a0cff000 ffff882759336fc0 ffff8817d73f9ae8 REG /mnt/ZMSS/ZMSSMultiPath_Du.log 6 ffff8829a0cfc800 ffff884e79e68780 ffff885412896da8 REG /ZMSS/etc/ZMSSDu/etc/SemLock.sys 7 ffff8829a0cfe900 ffff8852e44d1b00 ffff884962453130 REG /var/log/ZMSS/backtrace/ZMSSDu.log 9 ffff885353dd8c00 ffff8823a3c61740 ffff88135486ad28 REG /ZMSS/etc/ZMSSRRIProcessd/etc/du_info 10 ffff885353ddae00 ffff8823a3c600c0 ffff88135486b130 REG /ZMSS/etc/ZMSSRRIProcessd/etc/df_info 11 ffff885353dda800 ffff8823a3c60900 ffff88135486b538 REG /ZMSS/etc/ZMSSRRIProcessd/etc/iostat_info
根據打印,能夠知道文件名爲/mnt/ZMSS/ZMSSDu.log,那麼假設根據給出的inode:ffff880505b992f0 ,怎麼獲取到它的全路徑呢?
crash> inode.i_dentry ffff880505b992f0 i_dentry = { first = 0xffff8811cadcce30 } crash> struct -xo dentry.d_alias struct dentry { [0xb0] struct hlist_node d_alias; } crash> px 0xffff8811cadcce30-0xb0 $1 = 0xffff8811cadccd80-----------------這個就是inode對應的其中一個dentry的地址,和直接列出來的 0xffff8811cadccd80 是對得上的。
crash> dentry.d_inode ffff8811cadccd80--------能夠再驗證一下:
d_inode = 0xffff880505b992f0
開始往parent遍歷啦:
crash> dentry.d_name,d_iname,d_parent ffff8811cadccd80 d_name = { { { hash = 3146792703, len = 10 }, hash_len = 46096465663 }, name = 0xffff8811cadccdb8 "ZMSSDu.log" } d_iname = "ZMSSDu.log\000e\000ion.confrr3FQB\000\000\000\000" d_parent = 0xffff8857b90d3ec0
遍歷到的最後一層文件名是:ZMSSDu.log,而後根據d_parent 來遍歷:
crash> dentry.d_name,d_iname,d_parent 0xffff8857b90d3ec0 d_name = { { { hash = 0, len = 1 }, hash_len = 4294967296 }, name = 0xffff8857b90d3ef8 "/" } d_iname = "/\000lockevents:clockevent61\000\000\000\000\000\000"------------這裏面後面都是亂碼,前面的/是對的,說明到了該掛載點的根目錄 d_parent = 0xffff8857b90d3ec0----------d_parent 就是自己,說明到了掛載點的根目錄
往下就不能遍歷了,這個時候,咱們須要回到inode去,找到他的掛載路徑:
crash> inode.i_sb ffff880505b992f0 i_sb = 0xffff8857a1faa800 crash> super_block.s_mounts 0xffff8857a1faa800 s_mounts = { next = 0xffff8857aefbc070, prev = 0xffff8857aefbc370 } crash> struct -xo mount struct mount { [0x0] struct hlist_node mnt_hash; [0x10] struct mount *mnt_parent; [0x18] struct dentry *mnt_mountpoint;------------掛載點,其實就是一個dentry,又能夠遍歷啦 crash> struct mount 0xffff8857aefbc000 struct mount { mnt_hash = { next = 0x0, pprev = 0xffffc900305fc858 }, mnt_parent = 0xffff88587fe82b80, mnt_mountpoint = 0xffff8800354d8c00,--------對應掛載點的dentry crash> dentry.d_iname,d_parent 0xffff8800354d8c00------------繼續遍歷 d_iname = "ZMSS\000\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314" d_parent = 0xffff8800354d8cc0
咱們找到了它的掛載點,文件名是ZMSS,絕對路徑須要進一步往上遍歷:
crash> dentry.d_name,d_iname,d_parent 0xffff8800354d8cc0 d_name = { { { hash = 7630445, len = 3 }, hash_len = 12892532333 }, name = 0xffff8800354d8cf8 "mnt" } d_iname = "mnt\000\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314\314" d_parent = 0xffff8857b08de9c0------------------------------繼續遍歷 crash> dentry.d_name,d_iname,d_parent 0xffff8857b08de9c0 d_name = { { { hash = 0, len = 1 }, hash_len = 4294967296 }, name = 0xffff8857b08de9f8 "/" } d_iname = "/\000h13\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" d_parent = 0xffff8857b08de9c0---------------d_parent和自身相等,說明又遇到了掛載的根目錄了
至此,咱們反向遍歷完了/mnt/zxdfs/ZMSSDu.log,咱們也無法肯定咱們找到的/就是最終的系統的根目錄,因此還須要再肯定一下:
crash> dentry.d_sb 0xffff8857b08de9c0 d_sb = 0xffff8857b8cfd800 crash> super_block.s_mounts 0xffff8857b8cfd800 s_mounts = { next = 0xffff88587fe82bf0, prev = 0xffff8857bb960f70 } crash> struct mount 0xffff8857bb960f00 struct mount { mnt_hash = { next = 0x0, pprev = 0xffffc900301bf318 }, mnt_parent = 0xffff8857bb960480, crash> struct mount.mnt_devname,mnt_parent 0xffff8857bb960480 mnt_devname = 0xffff8857bed630c0 "rootfs" mnt_parent = 0xffff8857bb960480
發現mnt_parent指向本身,說明確定是根節點了。能夠看出整個遍歷過程很長很無趣。
若是對文件系統熟悉的人來講,內核中提供了一個函數來實現相似的過程,就是d_path:
char *d_path(const struct path *path, char *buf, int buflen) { char *res = buf + buflen; struct path root; int error; /* * We have various synthetic filesystems that never get mounted. On * these filesystems dentries are never used for lookup purposes, and * thus don't need to be hashed. They also don't need a name until a * user wants to identify the object in /proc/pid/fd/. The little hack * below allows us to generate a name for these objects on demand: * * Some pseudo inodes are mountable. When they are mounted * path->dentry == path->mnt->mnt_root. In that case don't call d_dname * and instead have d_path return the mounted path. */ if (path->dentry->d_op && path->dentry->d_op->d_dname && (!IS_ROOT(path->dentry) || path->dentry != path->mnt->mnt_root)) return path->dentry->d_op->d_dname(path->dentry, buf, buflen); rcu_read_lock(); get_fs_root_rcu(current->fs, &root); error = path_with_deleted(path, &root, &res, &buflen); rcu_read_unlock(); if (error < 0) res = ERR_PTR(error); return res; }
給定一個dentry,給定對應的mnt,就能惟一肯定一個路徑。