今天中午,在Redhat AS5 上解决一个数据库连接问题,在应用的日志中发现如下报错信息:

[error] [client 145.24.216.86] /lib/libc.so.6(__libc_start_main+0xdc)[0x4138cdec],


在系统中搜索了一下,发现三个位置有libc.so.6:

/home/ora10g/product/10.2.0/db_1/lib/stubs/libc.so.6
/lib/i686/nosegneg/libc.so.6
/lib/libc.so.6

看到Oracle也带有libc.so.6, 于是怀疑会不会是Oracle 10g与系统自己的libc.so.6版本不兼容而导致错误呢?

 

一时兴起,想出一个方案:
1、把系统自己的libc.so.6改名为: libc.so.6.bak
2、通过 
ln -s /home/ora10g/product/10.2.0/db_1/lib/stubs/libc.so.6 /lib/libc.so.6
用Oracle的libc.so.6来代替系统自带的libc.so.6

 

我为自己想出这么高明的办法暗自高兴一下,说干就干,动手。

 

第一步,先把/lib/libc.so.6改名为libc.so.6.bak。
执行:

[root@145 lib]#  cd /lib
[root@145 lib]#  mv libc.so.6 libc.so.6.bak

 

第二步,把Oracle的动态库连接到/lib/libc.so.6
执行:
[root@145 lib]# ln -s /home/ora10g/product/10.2.0/db_1/lib/stubs/libc.so.6 libc.so.6

一回车,系统提示:
ln: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory

我也没太在意这个错,以为仅是意外的出错而已,于是重新执行一下命令,没想到还是报同样的错!有点傻眼了……
我想既然不成功,那还是恢复原来状态吧,只要把改过的文件重新改回原来的名字即可。
再次执行:
[root@145 lib]# mv libc.so.6.bak  libc.so.6
系统还是报错:
mv: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory

这下可真傻眼!!汗!!!

于是开始一个个尝试命令: cp、 mv、rm 等等,发现都报这个错,我这才明白闯大祸了,把服务器整死了。
经过不断尝试,发现多数的shell命令已经不能执行了,最主要的是可以恢复原文件的cp、mv、ln命令全都失效了。发现只有象pwd、cd 等小部分命令还可以使用,而这些命令于事无补。

用SecureCRT尝试ssh登录这个服务器,已经无法连接到服务器,而原来连接在上面的两个进程却没被中断,服务器只是拒绝新连接而已。

 

看来只有到机房里,用安装盘进入急救模式,再把那个文件名改回来,才可能就能把机器救活了。

以前安装的光盘已经失踪了,但还好在一台机器上找到了同版本的一个ISO文件,于是赶紧刻盘,然后去机房。

 

到了机房,把光盘放入光驱,重启机器,进启动到 LILO: 界面时,输入
linux rescue
然后根据提示进入到急救模式,进入/lib目录下一看,libc.so.6文件还在啊,怎么回事?再想起在启动过程中,有句提示说原系统的目录mount在/mnt/sysp_w_picpath目录下,于是进入到/mnt/sysp_w_picpath,看到了与原服务器一致目录结构在那儿静静地躺着。再进入 /mnt/sysp_w_picpath/lib,看到了lib.so.6.bak,看来刚才操作的时候,文件改名是成功了。现在当务之急是把文件名改回来。执行:
 mv libc.so.6.bak libc.so.6

再次确认文件已改名成功,关掉机器,退出光盘,再重启机器,系统一路欢歌,进到登录界面。终于大功告成了。

 

 

事后再总结一下这次事故的出错原因:
libc.so.6是bash这个shell依赖的重要动态库之一,当我把这个动态库(链接)改名之后,shell找不到这个库了,所以就报找不到libc.so.6,并拒绝执行多数shell指令,也中断了ssh连接请求,而在整个过程,操作系统的kernel却还是活的,所以我原先连接的两个ssh进程还有反应(对回车、pwd等小数指令有响应),但却不能新建ssh连接。

 

这次事故也给我一个教训:
不要随便改Linux/Unix的系统动态库!

 


更不要在生产机上随便改!!

 


 

最不应该是还是在白天改!!!

 

 不久前,笔者一位在银行工作的网管朋友给笔者打电话,说他的一台安装有数据库的Linux服务器无法启动,这台Linux服务器存储着银行非常重要的数据,请帮助解决。他告诉笔者这台Linux服务器(安装的是Red Hat 5.1,内核为2.0.34)一直运行得很好,不巧UPS出了问题,电源断电,再启动服务器,当启动到init,出现如下错误提示: 

INIT:2.74 version booting 
can’t find libc.so.6 


然后就无法启动系统了。 

投石问路 

Linux下的共享链接库主要放在/lib目录下,以lib*.so.*为典型的文件名。Linux下的共享链接库对于Linux非常重要,几乎所有的程序都要调用共享链接库,类似于Windows下的*.dll文件。 

笔者先进入单用户模式,在“LILO:”提示符下输入:Linux single,结果同样提示libc.so.6文件找不到!看来Linux调用共享链接库是在读取/etc/inittab文件之前。在这里笔者简单地介绍一下Linux的启动过程: Linux的启动首先要引导内核,然后进行设备检测,紧接着调用一个称为init的进程,该进程按照一定的规则,读取/etc/inittab文件的内容并且执行文件中的相关进程,指引系统进入某一特定的运行规则进程,也就是大家众所周知的6种模式:0为待机,1为单用户,2为多用户本机模式,3为多用户网络模式,4为系统保留,5为XWindows,6为重启。init进程首先调用共享链接库,由于共享链接库发生错误, 所以现在单用户模式也进不去,看来只有用启动盘和修复盘进入Linux的急救模式去试一试。于是笔者在另一台机器的DOS下,利用Linux光盘dosutils目录下的rawrite.exe程序制作了一张启动盘和一张修复盘,笔者先用启动盘引导系统,在“LILO: ”提示符下输入:rescue,直至系统提示笔者插入修复盘,进入急救模式。由于处于急救模式状况下,许多常用的命令不能用,而且由于只是将软盘中的内核映射到内存中,连根分区也没有挂上,而/lib目录正是在根分区上。笔者先挂上了根分区: mount -ext2 /dev/hda1 /mnt/hda1,进入/lib目录用ls命令查看,libc.so.6存在,于是怀疑是否是超级块或者节点出了问题,于是便用fsck命令(在急救模式状况下常规的ext2文件系统的检查命令e2fsck不可用):fsck -b 8193 /dev/hda1,然后退出重启,结果故障依旧,反复用fsck命令检查也无法解决。 

柳暗花明 

反复了几次之后笔者耐下性子,又到/lib目录下去仔细看了一下libc.so.6: ls -l libc.so.6,注意到: 

lrwxrwxrwx 1 root root 13 Mar 10 03:32 libc.so.6 ->; libc-2.?.7.so 

原来libc.so.6文件只是libc-2.?.7.so文件的一个链接,看来此前笔者大意了。指向的链接名有一个“?”号,问题可能就出在这儿,init进程运行首先要调用libc-2.?.7.so所指向链接文件libc.so.6,init进程真正调用的是libc-2.?.7.so,而libc-2.?.7.so文件肯定是不存在的,那么到底应该是那个文件呢?再用ls查看,lib目录下有一个libc-2.0.7.so文件,这个文件才是真正指向libc.so.6的文件。笔者执行“rm -f libc.so.6, ln -s libc-2.0.7.so libc.so.6”,重新做了指向libc.so.6的正确链接,然后退出重启,结果故障仍然存在。 

水落石出 

可笔者总觉得判断是对的,又到lib目录下去仔细看了一下libc.so.6这个文件:ls -l libc.so.6,结果如下所示: 

lrwxrwxrwx 1 root root 13 Mar 10 03:32 libc.so.6 ->; libc-2.?.7.so 

看来刚才笔者所做的操作并没有写进磁盘,于是笔者又重新做了刚才的操作。可这一次笔者老老实实地“umount /dev/hda1”,然后退出重启,系统又重新正常启动了,再到lib目录下去看了一下libc.so.6这个文件:ls -l libc.so.6,结果如下: 

lrwxrwxrwx 1 root root 13 Mar 10 03:32 libc.so.6 ->; libc-2.0.7.so 

这回所做的修改写进了磁盘。看来问题归根结底是出在libc.so.6文件的链接问题上,问题总算解决了! 

后 记 

为什么非要做umount?在正常模式下没做umount,所做的操作也能写进磁盘的。笔者查了一下资料才明白: Linux文件系统更新是一个复杂的过程,当用户程序对文件系统进行修改以后,例如进行了写操作,文件数据将修改记录在内核缓冲中,在数据没有写到磁盘的时候,依然能够执行用户进程,所有数据的改变都在inode的内容中得到反映。磁盘的数据更新实际上是异步进行的,很有可能在写操作已经完成很长时间以后才真正对磁盘的数据进行更新。sync命令强制将磁盘缓冲的所有数据写入磁盘,如果在没有将磁盘缓冲区的信息写入磁盘之前终止系统,则磁盘的文件系统就会处在一个不稳定的状态。而在正常模式下即使没有对分区进行umount的操作,在重启之前系统会调用sync命令强制将磁盘缓冲的所有数据写入磁盘,而在急救模式下必须对所挂的分区进行umount的操作,系统才会调用sync命令强制将磁盘缓冲的所有数据写入磁盘,请在急救模式下的朋友注意这个问题。其实“reboot -n(Don’t sync before reboot or halt)”在重启之前不用sync命令强制将磁盘缓冲的所有数据写入磁盘,就很能说明问题。