文件系统损坏通常导致进程(或系统)挂起也就不足为奇了。
但是有时,看起来干净的文件系统仍可能导致进程挂起。
在本文中,我们介绍了创建具有空扩展属性(EA)条目的,启用访问控制列表(ACL)的增强型日记文件系统(JFS2)索引节点的方法,并研究了它如何导致进程挂起。 本文还概述了解决此类JFS2索引节点导致的进程挂起的方法。
AIXC ACL包括基本权限和扩展权限。 基本权限是分配给文件所有者,文件组和其他用户的传统文件访问模式。 扩展权限通过允许,拒绝或指定特定个人,组或用户和组组合的访问模式来修改(所有者,组或其他人的)基本文件权限。
要为/ testfs / foo文件启用ACL并向用户添加特定权限,我们可以使用acledit命令:
# export EDITOR=/usr/bin/vi; acledit /testfs/foo然后,我们可以使用vi命令将扩展权限更改为enabled并添加特定权限:
* * ACL_type AIXC * attributes: base permissions owner(root): rw- group(system): r-- others: r-- extended permissions enabled permit rwx u:bin Should the modified ACL be applied? (yes) or (no) yes我们可以运行aclget 检查文件的ACL。
许多应用程序要求具有将可变长度控制信息与文件系统对象相关联的能力。 这些对象称为扩展属性或EA。 di_ea节点di_ea的EA描述符描述了di_ea节点是否为其存储了任何EA。 如果索引节点确实具有与其关联的EA,则EA描述符还将描述与索引节点关联的索引节点扩展。 EA描述符在j2_types.h和ead_t定义:
typedef struct { uint8 flag; /* 1: flags */ uint8 nEntry; /* 1: */ uint8 len; /* 1: length in unit of fsblksize */ uint8 addr1; /* 1: address in unit of fsblksize */ uint32 addr2; /* 4: address in unit of fsblksize */ uint16 type; /* 2: ea type */ int16 nblocks; /* 2: nBlocks for outline pages */ int32 rsrvd; /* 4: */ } ead_t; /* 16 */启用ACL后,inode的di_ea.nEntry通常不会为零。 为了进行测试,我们需要为启用了ACL的inode故意将di_ea.nEntry设置为0。 我们有几种方法可以完成它。
首先,我们创建一个测试文件:
# echo "a test file" > /testfs/foo # sync然后,我们获得索引节点编号:
# ls -i /testfs/foo 3 /testfs/foo在卸载文件系统后,可以使用fsdb修改磁盘上的inode:
# umount /testfs # fsdb /testfs File System: /testfs File System Size: 2620952 (512 byte blocks) Aggregate Block Size: 4096 Allocation Group Size: 8192 (aggregate blocks) > i 3 Inode 3 at block 33, offset 0x600: …… [5] di_nlink: 1 [22] di_ixpxd.addr2: 0x00000021 [6] di_mode: 0x000081a4 di_ixpxd.address: 33 0100644 -rw-r--r-- [24] di_uid: 0 …… [13] di_ea.flag: 0x00 [30] di_ea.len: 0 EAv1 [31] di_ea.addr1: 0x00 [15] di_ea.nEntry: 0x00 [32] di_ea.addr2: 0x00000000 [16] di_ea.type: 0x0000 di_ea.address: 0 [34] di_ea.nblocks: 0 change_inode: [m]odify, [e]a, [t]ree, or e[x]it > m 6 0x020081a4 change_inode: [m]odify, [e]a, [t]ree, or e[x]it > m 16 2 Inode 3 at block 33, offset 0x600: …… Inode 3 at block 33, offset 0x600: …… [5] di_nlink: 1 [22] di_ixpxd.addr2: 0x00000021 [6] di_mode: 0x020081a4 di_ixpxd.address: 33 0100644 -rw-r--r-- [24] di_uid: 0 …… [13] di_ea.flag: 0x00 [30] di_ea.len: 0 EAv1 [31] di_ea.addr1: 0x00 [15] di_ea.nEntry: 0x00 [32] di_ea.addr2: 0x00000000 [16] di_ea.type: 0x0002 di_ea.address: 0 [34] di_ea.nblocks: 0 change_inode: [m]odify, [e]a, [t]ree, or e[x]it > x > q现在,我们运行fscck命令来检查/ testfs:
# fsck -yvv /testfs The current volume is: /dev/fslv00 Primary superblock is valid. Superblock s_state = 0x0 mode = 0x3 *** Phase 1 - Initial inode scan *** Phase 2 - Process remaining directories *** Phase 3 - Process remaining files *** Phase 4 - Check and repair inode allocation map *** Phase 5 - Check and repair block allocation map File system is clean.输出显示fsck命令无法检测到具有空EA条目的启用ACL的JFS2索引节点。
在IBM®AIX®6.1和AIX 7.1中,可以在安装文件系统时使用kdb命令修改内存中的inode。
(0)> i2 -i 3 ADDRESS DEVICE I_NUM/FS COUNT TYPE FLAG F1000A00588F6880 8000000A0000000C 3/16 00000 VREG …… On-disk Persistent Inode @ 0xF1000A00588F6A20: uid..........0x00000000 gid..........0x00000000 mode......0x000081A4 mode.........-rw-r--r--- mode.........0100644 …… ea.flag......0x00000000 ea.address...0x0000000000000000 ea.addr1.....0x00000000 ea.addr2.....0x00000000 ea.nEntry....0x00000000 ea.len.........0x00000000 ea.type.......0x00000000 (0)> mw 0xF1000A00588F6A20+0x3C F1000A00588F6A5C: 000081A4 = 020081a4 F1000A00588F6A60: 00000000 = . (0)> mw0xF1000A00588F6A20+0x88 F1000A00588F6AA8: 00000000 = 00020000 F1000A00588F6AAC: 00000000 = .再次检查inode以确认更改:
(0)> i2 F1000A00588F6880 …… On-disk Persistent Inode @ 0xF1000A00588F6A20: uid..........0x00000000 gid..........0x00000000 mode......0x020081A4 mode.........-rw-r--r--- mode.........0100644 …… ea.flag......0x00000000 ea.address...0x0000000000000000 ea.addr1.....0x00000000 ea.addr2.....0x00000000 ea.nEntry....0x00000000 ea.len.........0x00000000 ea.type.......0x00000002 typeNames.... AIXACL当进程打开使用上述方法创建的索引节点时,该进程可能会挂起:
# more foo # ps ax|grep more 25100524 pts/3 A 0:25 more foo当更多进程尝试打开该文件并且挂起进程无法终止时,处理器系统时间将急剧增加:
# kill -9 25100524 # ps ax|grep more 25100524 pts/3 A 0:57 more foo仅当线程从内核模式返回时才传递信号。 因此,我们需要检查线程当前在内核模式下运行的功能以及为什么它无法返回用户模式。 在这种情况下, Trace和kdb是非常有用的命令。
kdb命令显示了挂起线程的内核堆栈在不同的时间是不同的,但是一直在调用openpath()和openpnp()函数。
这意味着挂起线程没有在Hibernate某些资源,而是进入了openpath()和openpnp()内核函数之间的无限循环。 这也解释了为什么挂起过程会消耗大量的处理器系统时间。
跟踪可用于获取有关挂起过程的更多详细信息。
# trace -anl -C all -T20M -L40M -o trace.raw; sleep 5; trcstop # ps ax|grep more 286908 pts/5 A 13:07 more foo 323740 pts/8 A 12:26 more foo # trcrpt -C all -O 'exec=on,pid=on,tid=on,cpu=on' -o trace.log -p 323740 trace.rawtrace.log重复以下块:
107 more 3 323740 1401077 4.450242983 0 lookuppn: foo 4DF more 3 323740 1401077 4.450244140 1 JFS2 iget: vp = F100010038159FE8, count = 0017, ino = 0002, dev = 000000A0000000B 4DB more 3 323740 1401077 4.450244521 0 vnop_hold(vp = F100010038159FE8, getcaller = 3DB1B8) = 0000 4DF more 3 323740 1401077 4.450246527 2 JFS2 iget: vp = F100010038169FE8, count = 0006, ino = 0003, dev = 000000A0000000B 107 more 3 323740 1401077 4.450247078 0 vnop_lookup(dvp = F100010038159FE8, flag = 000A) = 0000, *vpp = 100010038169FE8 107 more 3 323740 1401077 4.450247558 0 lookuppn exit: 'foo' = vnode F100010038169FE8 15B more 3 323740 1401077 4.450248828 1 vnop_open(vp = F100010038169FE8, flags = 4000001, ext = 0000) = 0002显然,线程首先调用了lookuppn()函数并找到了foo文件出口,但是当vnop_open()尝试打开foo时,它返回了02 (ENOENT)错误代码。 然后,它不断重复调用lookuppn()和vnop_open()函数,从而导致进程挂起。
当挂起进程无法终止时,我们通常必须重新启动系统。 但是对于重要的生产系统,重启不是首选。 当我们知道挂起的根本原因时,有时可以无需重新启动即可解决它。 在此特定示例中,我们已经知道挂起是由空白EA条目引起的。 因此,我们可以通过禁用ACL退出无限循环。
在AIX 6.1和7.1中,可以使用kdb修改内存中的inode以禁用ACL。
(0)> i2 F1000A0034496880 On-disk Persistent Inode @ 0xF1000A0034496A20: uid..........0x00000000 gid..........0x00000000 mode...... 0x020081A4 mode.........-rw-r--r--- mode.........0100644 …… ea.flag....... 0x00000000 ea.address...0x0000000000000000 ea.addr1.....0x00000000 ea.addr2.....0x00000000 ea.nEntry....0x00000000 ea.len.........0x00000000 ea.type........0x00000002 typeNames.... AIXACL (0)> mw 0xF1000A0034496A20+0x3c F1000A0034496A5C: 020081A4 = 000081A4 F1000A0034496A60: 00000000 = . (0)> mw 0xF1000A0034496A20+0x88 F1000A0034496AA8: 00020000 = 0 F1000A0034496AAC: 00000000 = . (0)> q现在,所有挂起进程将继续运行,或者如果之前挂起了kill信号,则将挂起。
在AIX 5.3中,我们编写了一个修复程序,因为kdb无法修改内存。 (请参见下载部分。)
该修订程序也适用于AIX 6.1和AIX 7.1。
首先,我们使用kdb获取on-disk persistent inode的内存地址。 然后,我们通过/dev/kmem接口读取,修改和写回ea.type和di_mode :
/* on-disk inode */ dinode_t dip; /* on-disk inode address got from kdb */ unsigned long long ip=0xF1000A005D84F620; open("/dev/kmem", O_RDWR, 0); kread(ip, (char *)&dip, sizeof(dinode_t)); /* clear S_IXACL */ if(dip.di_mode == 0x20081a4) dip.di_mode = 0x81a4; /* clear ea.type */ if(dip.di_ea.type == 0x02) dip.di_ea.type = 0; kwrite(ip, (char *)&dip, sizeof(dinode_t));运行修订程序后,所有挂起进程将继续运行,或者如果之前已挂起kill信号,则将挂起所有挂起进程。
当进程打开带有空EA条目的启用ACL的文件时,进程将挂起。 IBM最近提供了可以避免此问题的授权程序分析师报告(APAR)IV34969。 如果不幸的是,您的AIX系统在申请IV34969之前遇到了此问题,并且您不想重新启动系统,则可以应用本文介绍的方法作为解决方法。
翻译自: https://www.ibm.com/developerworks/aix/library/au-aix-jfs2-inode/index.html
相关资源:入门学习Linux常用必会60个命令实例详解doc/txt