前言
本文是搭建基于qemu的linux开发环境的后续,之前搭建的环境使用时还有一些不便利的地方,以及内核配置上的些许遗漏。因此对之前搭建的环境做一些完善,本文则是记录这个完善过程。
1 完善根文件系统
前文文末指出通过busybox构建的最小文件系统缺少一些配置文件,主要是这么几个文件:/etc/inittab
、/etc/fstab
、/etc/profile
、/etc/init.d/rcS
(rcS已经创建,不过这里会做一些修改)。接下来,给出这些文件的内容:
① /etc/inittab
# /etc/inittab
::sysinit:/etc/init.d/rcS
ttyAMA0::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
② /etc/fstab:
#device mount-point type options dump fsck order
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
③ /etc/profile:
# 配置shell环境的命令提示符:用户名@主机名:当前路径 #
PS1='lenny@vexpress:\w # '
export PS1
④ /etc/init.d/rcS:
#!bin/sh
# mount fs according to fstab
mount -a
# mdev相当于嵌入式版本的udev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
以上几个文件的格式以及含义网上有很多资料讲解,很容易查到,因此这里只给出内容,其它不再多说。
⑤ 拷贝动态库
除此上述的几个配置文件外,还需要将工具链所带的动态库拷贝到根文件系统的lib
目录下。这是为什么呢?当我们编写了一个程序如下:
/* a.c */
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
一个很简单的程序,使用交叉编译工具链编译(默认动态链接):
arm-linux-gnueabi-gcc a.c
然后再qemu运行的linux系统上执行:
./a.out
会提示如下信息:
-/bin/sh: ./a.out: not found
这是因为当前根文件系统的lib
目录下还没有动态链接器以及各自动态库,linux找不到动态链接器自然也就无法加载动态链接的可执行文件了。反过来,如果我们静态链接:
arm-linux-gnueabi-gcc -static a.c -o a_static
则可以在qemu上运行可执行文件a_static。这又是为什么呢?更多关于程序链接的知识,可以参考我的另外一篇博客:计算机系统基础摘记——程序的链接。
至此,我们搞清楚了为什么需要动态库。现在我们将交叉编译工具链自带的动态库拷贝过来即可:
cp /usr/arm-linux-gnueabi/lib/*.so* ./ -d
值得注意的是,拷贝的具体路径需要根据自己的开发环境来确定;拷贝指令中 的-d
选项的作用是拷贝软链接时,保持软链接,而不是去拷贝软链接指向的文件(避免重复拷贝真正的动态库文件)。
2 配置静态IP
我的Ubuntu当前使用的是动态IP,重新开机后IP可能会变化,因此每次使用u-boot启动kernel时可能需要修改环境变量(服务器IP变了),这样非常麻烦,因此决定为Ubuntu设置静态IP。
设置静态IP主要是修改配置文件/etc/network/interfaces
。这个文件在配置qemu的网络时已经修改过,当前内容如下:
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto ens33
auto br0
iface br0 inet dhcp
bridge_ports ens33
配置静态IP后内容如下:
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto br0
iface br0 inet static
address 192.168.0.117
netmask 255.255.255.0
broadcast 192.168.0.255
gateway 192.168.0.1
bridge_ports ens33
auto ens33
配置完后需要重启网卡或重启计算机,以使配置生效。
3 配置DNS服务器
之前我们使用动态IP时,IP地址由DHCP(动态主机配置协议)服务器分配。除了IP地址外,DHCP还会给主机分配DNS服务器(告诉主机域名服务器的IP),而我们设置了静态IP后,主机就不会向DHCP服务器请求服务了,换句话说现在没人告诉主机域名服务器的IP了。实际测试也发现,设置完静态IP后,执行ping www.baidu.com,会提示unkown host。
这个问题的由来很清楚,解决方法就是自己设置DNS的IP。不过,值得一提的是,网上流传的一些设置DNS的方法在Ubuntu18.04.2已经失效了。比如:
- 修改/etc/network/interfaces
在/etc/network/interfaces文件中添加一行:dns-nameservers IP。实测,该方法无效。 - 修改/etc/resolvconf/resolv.conf.d/base
在/etc/resolvconf/resolv.conf.d/base文件中添加一行nameserver IP。在修改这个文件后进行保存时提示如下错误:
上述方法在更老版本的Ubuntu中是有效的,至于上述配置方式从哪个版本开始被放弃的,我没有查过。那么,在Ubuntu18.04.2中如何配置DNS的IP呢?我在参考文献[1]中找到了答案:修改配置文件/etc/systemd/resolved.conf
,将DNS
配置项设置为:
# 国内移动、电信和联通通用的DNS(国内用速度快、稳定)
DNS=114.114.114.114
# Google提供的DNS(全球通用)
DNS=8.8.8.8
修改完配置文件后,重启相关服务:
systemctl restart systemd-resolved.service
执行systemd-resolve --status
查看配置结果,可以发现,此时DNS的IP已经变为我们刚才设置的。再次执行ping www.baidu.com,可以ping通。
4 kernel的热插拔相关配置
修改完静态IP后,需要修改u-boot中相关环境变量,将serverip
改为上文设置的静态IP,同时给内核传递的参数bootargs
中相关IP也要改变。然后,从qemu启动u-boot,进而启动内核,内核能够顺利启动,不过执行rcS
中的echo /sbin/mdev > /proc/sys/kernel/hotplug
时出错,错误信息如下:
can't create /proc/sys/kernel/hotplug: nonexistent directory
一番搜索后,在参考文献[2]中发现了答案:配置CONFIG_UEVENT_HELPER
。这个配置项具体位于menuconfig的:Device Drivers ⇒ Generic Driver Options
下。此外,我们可以在选中该选项的同时,设置配置项CONFIG_UEVENT_HELPER_PATH为/sbin/mdev
,这样就可以不用执行rcS中echo语句了。
至于为什么要将/sbin/mdev
的位置告知给kernel,根源在kernel实现热插拔的方式。热插拔有两种实现方式,一种是内核通过netlink
向用户空间广播uevent消息,用户空间的进程udev
根据收到的消息执行一些动作;另一种是内核空间启动一个用户空间进程,通过给进程传递环境变量的方式传递uevent消息,然后被启动的进程根据消息执行一些动作,以处理热插拔事件(busybox的mdev
适用于此方式)。既然需要内核启动mdev,那么我们自然要将mdev的pathname告诉内核。
另外,对于/proc/sys/kernel/hotplug不存在的问题,网上有说要配置CONFIG_HOTPLUG
,这个解决方法可能适合版本较旧的内核,在我使用的5.4.26
版本的内核中,这个配置项已经被移除了,且原先这个配置项影响的代码总是会被编译(相当于这个配置项总是被选中)。
参考文献
[1] Ubuntu 18.04 永久修改DNS的方法
[2] genkernel init: Can’t create /proc/sys/kernel/hotplug