CentOS 6早已退休,没人维护了,但最近需要测试一个很老的服务器程序,要跑在CentOS 6上,那就在VirtualBox虚拟机上装一个吧。由于测试环境是不能上网的,因此我特意下载了一个CentOS-6.10-x86_64-bin-DVD1.iso镜像,这样不仅安装系统不需要连网,安装一些基本的软件也不需要连网了。

要测试的是服务器程序,因此是不需要桌面系统的,安装的时候选择了个Database Serve,系统安装过程很顺利,这里不再多说。测试这个程序,需要把win下的一个目录挂载到虚拟机的CentoS 6里,最合适的方式当然是使用VirtualBox的目录共享功能,这个功能需要安装VirtualBox的增强功能(VirtualBox Guest Additions)。

和win下不一样,VirtualBox的增强功能在Linux下是没有预编译二进制文件的,只能通过源码编译的方式安装,因此需要预先安装编译环境和内核依赖。由于是离线环境,需要使用CentOS-6.10-x86_64-bin-DVD1.iso镜像作为源来安装软件。

  1. CentOS-6.10-x86_64-bin-DVD1.iso插入虚拟机光驱作为安装源

  2. 加载光驱

[root@localhost ~]# mkdir /media/cdrom
[root@localhost ~]# mount /dev/cdrom /media/cdrom
mount: block device /dev/sr0 is write-protected, mounting read-only
  1. 关闭其他源,使用cdrom作为源安装编译软件
[root@localhost ~]# yum --disablerepo=\* --enablerepo=c6-media install gcc gcc-c++ cmake
  1. 安装编译VirtualBox Guest Additions所需要的内核源码
[root@localhost ~]# yum --disablerepo=\* --enablerepo=c6-media install "kernel-devel-uname-r == $(uname -r)"
  1. 卸载光驱
umount /media/cdrom

现在准备工作已经完毕,准备安装增强功能。点击菜单“设备/分配光驱/移除虚拟”盘来移除CentOS-6.10-x86_64-bin-DVD1.iso镜像,点击 “设备/安装增强功能”来加载VBoxGuestAdditions.iso镜像。

  1. 重新挂载光驱
[root@localhost ~]# mount /dev/cdrom /media/cdrom
mount: block device /dev/sr0 is write-protected, mounting read-only
  1. 执行VirtualBox Guest Additions安装程序(由于没有安装桌面,因此这里需要--nox11参数)
[root@localhost ~]# cd /media/cdrom
[root@localhost cdrom]# sh VBoxLinuxAdditions.run --nox11
Verifying archive integrity... All good.
Uncompressing VirtualBox 6.1.16 Guest Additions for Linux........
VirtualBox Guest Additions installer
Copying additional installer modules ...
Installing additional modules ...
VirtualBox Guest Additions: Starting.
VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel 
modules.  This may take a while.
VirtualBox Guest Additions: To build modules for other installed kernels, run
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup <version>
VirtualBox Guest Additions: or
VirtualBox Guest Additions:   /sbin/rcvboxadd quicksetup all
VirtualBox Guest Additions: Building the modules for kernel 
2.6.32-754.el6.x86_64.

VirtualBox Guest Additions: Look at /var/log/vboxadd-setup.log to find out what 
went wrong
VirtualBox Guest Additions: modprobe vboxsf failed
Building the VirtualBox Guest Additions kernel modules.  This may take a while.
To build modules for other installed kernels, run
  /sbin/rcvboxadd quicksetup <version>
or
  /sbin/rcvboxadd quicksetup all
Running kernel modules will not be replaced until the system is restarted
vboxadd-service.sh: Starting VirtualBox Guest Addition service.

出乎我的意料,居然没有安装成功。按提示查看/var/log/vboxadd-setup.log

[root@localhost cdrom]# cat /var/log/vboxadd-setup.log
Could not find the X.Org or XFree86 Window System, skipping.

由于没装桌面,当然没有X.Org,这是一个正常的提示,不会导致编译失败。但是在/var/log目录下,我还注意到还有一个vboxadd-setup.log.1,程序员的直觉告诉我要看下里面有啥。

[root@localhost cdrom]# cat /var/log/vboxadd-setup.log.1 
Building the main Guest Additions 6.1.16 module for kernel 2.6.32-754.el6.x86_64.
Building the shared folder support module.
Error building the module.  Build output follows.
make V=1 CONFIG_MODULE_SIG= CONFIG_MODULE_SIG_ALL= -C /lib/modules/2.6.32-754.el6.x86_64/build M=/tmp/vbox.0 SRCROOT=/tmp/vbox.0 -j1 modules
test -e include/linux/autoconf.h -a -e include/config/auto.conf || (            \
        echo;                                                           \
        echo "  ERROR: Kernel configuration is invalid.";               \
        echo "         include/linux/autoconf.h or include/config/auto.conf are missing.";      \
        echo "         Run 'make oldconfig && make prepare' on kernel src to fix it.";  \
        echo;                                                           \
        /bin/false)
mkdir -p /tmp/vbox.0/.tmp_versions ; rm -f /tmp/vbox.0/.tmp_versions/*
make -f scripts/Makefile.build obj=/tmp/vbox.0
  gcc -Wp,-MD,/tmp/vbox.0/.vfsmod.o.d  -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.4.7/include -Iinclude  -I/usr/src/kernels/2.6.32-754.el6.x86_64/include/uapi -I/usr/src/kernels/2.6.32-754.el6.x86_64/arch/x86/include -Iarch/include/generated -Iinclude -include /usr/src/kernels/2.6.32-754.el6.x86_64/include/linux/kconfig.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks -O2 -m64 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -maccumulate-outgoing-args -fstack-protector -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_AVX=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mindirect-branch=thunk-extern -mindirect-branch-register -DRETPOLINE -Wframe-larger-than=2048 -Wno-unused-but-set-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -pg -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fno-dwarf2-cfi-asm -fconserve-stack -Wno-declaration-after-statement -fno-pie -include /tmp/vbox.0//include/VBox/VBoxGuestMangling.h -fshort-wchar -I/usr/src/kernels/2.6.32-754.el6.x86_64/include -I/tmp/vbox.0/ -I/tmp/vbox.0/include -I/tmp/vbox.0/r0drv/linux -D__KERNEL__ -DMODULE -DRT_WITHOUT_PRAGMA_ONCE -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DVBOX_WITH_HGCM -DIN_MODULE -DIN_GUEST -DIN_GUEST_R0 -DRT_NO_EXPORT_SYMBOL -DVBOX_WITH_64_BITS_GUESTS -DRT_ARCH_AMD64  -DMODULE -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(vfsmod)"  -D"KBUILD_MODNAME=KBUILD_STR(vboxsf)" -D"DEBUG_HASH=41" -D"DEBUG_HASH2=24" -c -o /tmp/vbox.0/.tmp_vfsmod.o /tmp/vbox.0/vfsmod.c
In file included from /tmp/vbox.0/vfsmod.c:44:
/tmp/vbox.0/vfsmod.h:102: error: redefinition of ‘set_nlink’
include/linux/fs.h:1892: note: previous definition of ‘set_nlink’ was here
make[2]: *** [/tmp/vbox.0/vfsmod.o] Error 1
make[1]: *** [_module_/tmp/vbox.0] Error 2
make: *** [vboxsf] Error 2
Could not find the X.Org or XFree86 Window System, skipping.

对于写C++的我,这个提示很熟悉了,就是VirtualBox的源代码和CentOS 6.10的内核源代码版本没对上,不兼容,set_nlink这个符号重复定义。除非是改内核源代码或者VirtualBox的源代码,不然基本上没救了。我用VirtualBox好多年了,之前也遇到过这种情况,多半是旧版本的VirtualBox安装新版本的Linux,由于新的内核源代码做了一些改动,与旧的VirtualBox不兼容,只能等VirtualBox更新。但CentOS 6已经停止支持,应该是没有更新了,想要自己改的话,要对Linux内核和VirtualBox的源代码比较熟悉才行,我基本是放弃了,这事就搁下了。

过了好几天,刚好有点空闲时间,又想起来这事。我定了几个方案,1是使用CentOS 7来测试这个程序,不过这个最终测出来的结果可能差比较远;2是使用其他方式挂载目录或者不挂载,使用sftp来同步目录,这样整个测试过程比较难受;3是删掉当前的虚拟机和VirtualBox,再安装一个低版本的VirtualBox试试,这个比较费时。这有一个令我非常好奇的点,那就是CentOS 6.10虽然是一个比较老的系统,但距离停止支持也才几年,这个系统的使用这么广泛,VirtualBox没有理由不再支持这个系统的。我的NAS上还用VirtualBox跑着一个更老的xp,也没见VirtualBox放弃支持,我决定还是查一下原因,看看能不能抢救一下。

先是google半天“VirtualBox set_nlink”关键字,啥有用的东西都没找着,然后查“CentOS modprobe vboxsf failed”,这个倒是很多,但翻了半天没有和我这个相同的。反正我也是写C++的,于是想看一下源码。由于VBoxLinuxAdditions.run是一个自解压脚本,报错的文件/tmp/vbox.0/vfsmod.h只是一个临时文件,程序结束后就没了,看不了,需要手动解压VBoxLinuxAdditions.run

sh VBoxLinuxAdditions.run --target /tmp/VBoxLinuxAdditions
[root@localhost cdrom]# ls /tmp/VBoxLinuxAdditions/
deffiles   install.sh   VBoxGuestAdditions-amd64.tar.bz2
installer  routines.sh  VBoxGuestAdditions-x86.tar.bz2

这个网上查的解压指令好像不太对,会执行一次安装操作(后来意识到好像还要加一个extra-only之类的参数,记不太清了,sh VBoxLinuxAdditions.run --help应该会列出所有参数),不过文件确实是解压到了对应的目录。从目录文件名来看,解压后应该是执行了install.sh,64位的源码应该是在VBoxGuestAdditions-amd64.tar.bz2里。

[root@localhost cdrom]# cd /tmp/VBoxLinuxAdditions/
[root@localhost cdrom]# mkdir VBoxGuestAdditions-amd64
[root@localhost VBoxLinuxAdditions]# tar -xf VBoxGuestAdditions-amd64.tar.bz2 -C VBoxGuestAdditions-amd64

搜索一下,就能定位到出错的文件VBoxGuestAdditions-amd64/src/vboxguest-6.1.16/vboxsf/vfsmod.h,查看102行

#if RTLNX_VER_MAX(3,2,0) && !RTLNX_RHEL_MIN(6, 10)
DECLINLINE(void) set_nlink(struct inode *pInode, unsigned int cLinks)
{
    pInode->i_nlink = cLinks;
}
#endif

从代码来看,这明显是有做版本兼容的,而且是以6.10为分界。我尝试直接去掉这个定义,然后重新打包一个新的VBoxGuestAdditions-amd64.tar.bz2到当前目录,再执行sh install.sh,这次报错不一样了,但没安装成功,怀疑是参数没传对,但install.sh这个脚本很大,一步步跟下去太花时间,想重新再打一个run脚本,这个又不会弄,放弃了。决定还是再看看源码的问题,感觉像是哪个环境变量设置不对导致的。再找一下RTLNX_VER_MAX``RTLNX_RHEL_MIN的定义,发现它们在VBoxGuestAdditions-amd64/src/vboxguest-6.1.16/vboxsf/include/iprt/linux/version.h

/** @def RTLNX_VER_MAX
 * Evaluates to true if the linux kernel version is less to the one specfied
 * (exclusive). */
#define RTLNX_VER_MAX(a_Major, a_Minor, a_Patch) \
    (LINUX_VERSION_CODE < KERNEL_VERSION(a_Major, a_Minor, a_Patch))

/** @def RTLNX_RHEL_MIN
 * Require a minium RedHat release.
 * @param a_iMajor      The major release number (RHEL_MAJOR).
 * @param a_iMinor      The minor release number (RHEL_MINOR).
 * @sa RTLNX_RHEL_MAX, RTLNX_RHEL_RANGE, RTLNX_RHEL_MAJ_PREREQ
 */
#if defined(RHEL_MAJOR) && defined(RHEL_MINOR)
# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) \
     ((RHEL_MAJOR) > (a_iMajor) || ((RHEL_MAJOR) == (a_iMajor) && (RHEL_MINOR) >= (a_iMinor)))
#else
# define RTLNX_RHEL_MIN(a_iMajor, a_iMinor) (0)
#endif

根据编译时的日志,当前内核版本为2.6.32-754.el6.x86_64,猜测一下,CentOS 6.10这个版本RHEL_MAJOR为6,RHEL_MINOR为10,那就不应该启用这个set_nlink的结构体定义才对。但是既然启用了,那就是引用错误的头文件,或者是我装错了内核开发库版本。但是这是从DVD装的,不是从网络装的,版本错了就会找不到安装包,也不太可能装错啊。
使用yum再次检查

[root@localhost VBoxLinuxAdditions]# yum list installed | grep kernel
abrt-addon-kerneloops.x86_64
dracut-kernel.noarch    004-411.el6    @anaconda-CentOS-201806291108.x86_64/6.10
kernel.x86_64           2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
kernel-devel.x86_64     2.6.32-754.el6 @c6-media                                
kernel-firmware.noarch  2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
kernel-headers.x86_64   2.6.32-754.el6 @anaconda-CentOS-201806291108.x86_64/6.10
libreport-plugin-kerneloops.x86_64

kernel-devel.x86_64 2.6.32-754.el6 @c6-media这个应该是我安装的,从版本号上来看是没错的,那再查下是不是引用了错误的头文件。这个文件里引用了#include <linux/version.h>,于是找到/usr/include/linux/version.h,发现有点不太对

[root@localhost VBoxLinuxAdditions]# cat /usr/include/linux/version.h
#define LINUX_VERSION_CODE 132640
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#define RHEL_MAJOR 6
#define RHEL_MINOR 9
#define RHEL_RELEASE_VERSION(a,b) (((a) << 8) + (b))
#define RHEL_RELEASE_CODE 1545
#define RHEL_RELEASE 753
#define RHEL_16KSTACK_BUILD 520

这里的RHEL_MINOR居然是9而不是10,我怀疑我下错了镜像,但看了下release,好像没错。

[root@localhost VBoxLinuxAdditions]# cat /etc/centos-release 
CentOS release 6.10 (Final)

现在怀疑我下载了被修改的镜像,到官网核对一下checksum。官网(https://vault.centos.org/6.10/)给出的镜像列表中有mirror.nsc.liu,进去后有checksum。

可以看到,sha1的值是对得上的。不管那么多了,我直接手动修改/usr/include/linux/version.h中的RHEL_MINOR为10,重新编译,结果发现还是同样的错误。再仔细查,它引用的其实是/usr/src/kernels/2.6.32-754.el6.x86_64/include/linux/version.h。把这里也改掉后,编译成功,VirtualBox的增强功能也没问题。

由于CentOS 6已停止支持,这个我也不打算到CentOS社区那边去问了,或者已经有人提了issue我没搜索到,或者是这个问题已修复,但没更新DVD,需要联网更新,这个就不再深究。这里只是做个笔记。

posted on 2022-03-16 00:46  coding my life  阅读(875)  评论(0编辑  收藏  举报