(可能会有遗漏。请参考文末的参考资料)
一、 实验目的
学习Linux内核的配置和编译;
深入理解Linux系统调用;
理解ARM和x86的CPU模式(系统模式、用户模式等)的不同。
掌握内核模块的编写方法。
二、 实验器材
硬件:
PC一台(windows7旗舰版,ubuntu14.04虚拟机)
树莓派1代一块
USB-TTL串口线一根
SD卡一张(树莓派自带)
SD读卡器(笔记本自带)
键鼠套装(USB接口)
显示器一台(带dvi接口)
Hdmi转dvi转接线一根
杜邦线若干
软件:
Putty
Win32DiskImager
Virtual box5.0.14
Ubuntu 14.04
三、 器材连接
1. 连接树莓派与USB-TTL串口线
树莓派
USB-TTL
2
VCC
6
GND
8
RX
10
TX
四、 实验步骤及结果
以下实验步骤针对树莓派1代
1. 搭建环境
搭建环境主要包括:
a) 安装virtual box 5.0.14 ,安装ubuntu 14.04 lts
b) Windosws:下载Win32DiskImager,用以烧录树莓派官方系统
c) 将sd卡挂入linux虚拟机
d) Ubuntu:下载内核源码,编译工具链
e) Ubuntu:下载相关依赖库、工具等
想到一种曲线救国方案,可以不把SD卡挂入linux虚拟机。在虚拟机内编译好kernel.img和Modules后,分步导入SD卡。
以下过程我没有实践过,讲道理是可以的。
a) 从虚拟机拖出kernel.img和modules(modules内有软链接,可能要删除后才能拖出来)
b) 将modules放入boot分区。启动树莓派,在树莓派内将modules目录放入正确的位置(启动后,boot分区位置:/boot)
c) SD卡插回pc,将新的kernel.img放入boot分区。再启动树莓派。
1) 安装virtual box 5.0.14 ,安装ubuntu 14.04 lts
a) linux环境:ubuntu 14.04 lts
b) 实验使用virtual box作为虚拟机。截止本实验报告,官网最新版virtual box安装ubuntu 14.04,再安装增强功能,拖曳文件、共享剪贴板有问题(不排除是我个人问题)。因此,安装稍旧的 virtual box 5.0.14
c) 安装ubuntu后,需要安装增加功能(启动系统后,虚拟机菜单栏“设备”->安装增强功能)
2) 下载Win32DiskImager,刷机
a) 下载Wind32DiskImage
b) 下载树莓派镜像:(内核版本 3.18。稍后下载内核源码需要对应)
https://downloads.raspberrypi.org/raspbian/images/raspbian-2015-02-17/
c) SD卡放入读卡器,使用Wind32DiskImage将镜像刷入。
Windows下只看得见boot一个分区,不影响,直接刷就好。
3) 将sd卡挂入linux虚拟机
a) SD卡插入笔记本读卡器后,虚拟机不能直接像u盘一样挂入sd卡。
b) VirtualBox挂入SD卡参考:
http://superuser.com/questions/373463/how-to-access-an-sd-card-from-a-virtual-machine
c) Windows大致步骤:
i. 查看SD卡设备号
ii. 建立SD卡的文件链接
iii. Virtual box添加SD卡设备
iv. (如果以上步骤不成功,到virtualbox官网下载虚拟机增强工具,貌似叫extension****。在下载virtual box的页面,会有相应增强工具的链接。我之前下过,不知道是否必须)
d) 注意:
i. 添加SD卡后,如果取出了SD卡,linux就不能启动了。弹出SD卡也会导致linux崩溃。没有SD卡时,需要将之前添加的SD卡设备删除,才能正常启动linux
ii. 添加SD卡后,ubuntu左侧导航会出现两个硬盘图标。点击后查看具体的挂载位置。如过启动linux后,终端里cd这个位置,说找不到路径。点开一次硬盘图标,再cd,就能找到路径了。
iii. 我在linux里,对boot分区操作,会导致linux崩溃,猜测是windows和linux共享了boot分区的原因。所以,对boot分区的操作,我是直接在windows下进行的,另一个分区在linux里操作。
4) 下载内核源码,编译工具链
a) 下载内核3.18的源码:
wget https://github.com/raspberrypi/linux/archive/rpi-3.18.y.tar.gz
b) 解压:
tar-xzf rpi-3.18.y.tar.gz
c) 建软链接,方便使用:
ln-s linux-rpi-3.18.y/ linux
d) 下载编译工具链(主要是交叉编译所用的gcc):
注意:最好不要把tools放在linux目录里,tools和linux放在同一个目录下就好
gitclone git://github.com/raspberrypi/tools.git
e) 下载相关依赖库、工具等
以下是我安装了,但我也不知道是不是必须安装的:
apt-getinstall lib32z1 lib32ncurses5
apt-getinstall libncurses5-dev
apt-getinstall bc
2. 复制配置文件config
刷机后,启动一次树莓派,在树莓派上
1) 查看内核版本
Uname –a
内核为3.18,与下载的源代码相同
2) 复制配置文件config.gz,稍后用于编译内核
cp /proc/config.gz /home/pi
关机,取下SD卡,插入pc,打开虚拟机,在ubuntu内:
1) 复制config.gz
cp /media/my/fe/home/pi/config.gz /home/my/linux (fe是SD卡根目录分区路径)
2) 解压
zcat config.gz > .config
3. 修改系统调用
1)在linux/arch/arm/kernel中新建sys_mysyscall.c文件,包含新的系统调用处理函数
该系统调用处理函数向系统日志打印”This is the new system call.”
2) 修改linux/arch/arm/kernel中Makefile文件,将sys_mysyscall.o纳入编译进程
3) 修改linux/arch/arm/kernel中calls.S文件,修改系统的中断向量表
修改223号中断
4) 修改linux/include/uapi/asm-generic/unistd.h头文件,将223号调用与某个宏进行关联
5) 修改linux/中的Makefile,修改内核源码的ARCH类型和编译器路径
CROSS_COMPILE为tools目录中编译工具的路径
4. 编译内核
不做特别说明,都在linux/目录下
1) 查看、修改配置选项
a) 安装字符菜单支持软件包
apt-getinstall libncurses5-dev
b) 查看、修改配置选项
makemenuconfig
不做修改,直接选中exit退出即可
2) 编译内核
编译过程中,如果出现了一堆可选项,一直按enter即可
a) 编译,使用4个线程
make –j4
编译成功后,linux/arch/arm/boot目录下会生成zImage文件,这就是新的内核映像。如果zImage的大小和SD卡boot分区内kernel.img差距过大,编译肯定出了问题,最有可能是配置步骤有问题(比如没有正确导入.config文件)。
b) 使用tools中的工具,处理zImage文件
i. 树莓派需要的是另一种格式的镜像文件,不能直接使用zImage(也许可以,我看有些教程是直接将zImage改为kernel.img,我没有试过)
ii. cd/home/my/tools/mkimage
iii. ./imagetool-uncompressed.py../../linux/arch/arm/boot/zImage (这一步如果没有root权限,会提示某某bin文件写入/读取啥啥错误)
iv. 成功后会在当前目录下生成一个kernel.img文件,就是新内核。
3) 提取modules
在编译内核的过程中,模块文件也编译出来了,这里把它们提取出来
a) 在linux上层目录,新建目录modules
mkdirmodules
b) 提取modules
cdlinux
makemodules_install INSTALL_MOD_PATH=../modules
5. 替换内核
1) 替换kernel.img
boot分区下,(虚拟机下操作boot分区会导致崩溃,建议对boot分区的操作在windows下进行以下操作)
备份kernel.img:kernel.img -> old_kernel.img
将新的kernel.img (tools/mkimage目录下) 放入boot分区
2) 更新内核modules
将提取出的modules/lib目录拷入树莓派根目录下。
cp–r /home/my/modules/lib /media/my/fe
我在这里采用的是复制的方式,建议不要删除原树莓派lib目录下的东西
3) 验证内核
插回SD卡,启动树莓派
uname–a
根据内核版本号和编译时间可知,内核替换成功
6. 测试新的系统调用
1) syscall函数
a) test.c
#include #include int main()
{
syscall(223);
return 0;
}
b) 编译
直接在树莓派上编译
gcc–o t test.c
c) 测试
./t运行
dmesg| tail 查看日志
日志最后一行由新系统调用打印。系统调用成功
2) 汇编代码
#include #define sys_call() {__asm__ __volatile__ ("swi 0x900000+223\n\t");} while(0)
int main(void)
{
sys_call();
return 0;
}
(网上都是这样写的,但我就是不能成功,表示无奈……)
7. 编写内核模块
1) 内核模块代码
加载和卸下模块时,向系统日志打印信息
#include #include #include #include static int __init modl_init(void)
{
printk("Module starts.\n");
return 0;
}
static void __exit modl_exit(void)
{
printk("Module ends.\n");
}
module_init (modl_init)
module_exit (modl_exit)
2) 交叉编译
a) 无法在树莓派上直接编译,原因是,编译内核模块需要用到内核源码。树莓派上,新内核的/lib/modules/3.18.16目录下有编译所需的build和source目录。但是这两个目录是软链接,指向ubuntu内的linux目录。尝试过将linux目录拷入树莓派内,因为种种原因失败了(如果努力,应该还是可以成功的)。最后选择在ubuntu上交叉编译
b) Makefile文件
CROSS_COMPILE是编译工具路径
obj-m:=modl.o
KDIR=/home/my/linux
PWD=$(shell pwd)
ARGS:=ARCH=arm CROSS_COMPILE=/home/my/tools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi-
default:
make -C $(KDIR) M=$(PWD) $(ARGS) modules
clean:
rm *.o *.ko *.mod.c
.PHONY:clean
c) 编译
生成modl.ko
8. 测试内核模块
1) 将编译出的modl.ko拷入树莓派
2) 挂载内核模块
insmodmodl.ko
3) 验证
dmesg| tail
lsmod
内核模块挂载成功
4) 卸下内核模块
rmmodmodl
dmesg| tail
参考资料:
1. http://blog.csdn.net/xdw1985829/article/details/39077611
2. http://tieba.baidu.com/p/3987137224?pid=74172808781#74172808781
3. http://www.jianshu.com/p/802e7f6ff9ce