注,部分内容参照http://wiki.ubuntu.org.cn/index.php?title=UbuntuHelp:Kernel/Compile/zh&variant=zh-cn#.E7.BC.96.E8.AF.91.E5.86.85.E6.A0.B8.EF.BC.88.E4.BB.85.E9.92.88.E5.AF.B9.E5.86.85.E6.A0.B8.E6.BA.90.E7.A0.81.E6.9D.A5.E8.87.AA.E4.BA.8Egit_.E4.BB.93.E5.BA.93.EF.BC.8C.E6.88.96.E6.98.AFapt-get.E6.BA.90.EF.BC.89
系统安装完毕后,各软件的版本情况
wlan_ac@wlan:/boot$ llsudo make mrproper 清除代码(如果不需要重新编译整个内核,仅更新了部分代码,则不需要执行词句。)
执行sudo make dep生成依赖关系
执行AUTOBUILD=1 fakeroot debian/rules binary-debs进行编译,系统提示rule目录下缺少文件。
执行sudo make bzImage,编译内核映像。
执行sudo make modules 编译模块。
内核装载
执行sudo make install 加载内核模块。
结果,加载期间机房断电!!启动后,现象非常奇怪,系统没有提示老版本linux的加载通道,只有新版本3.11.0.4的加载选项。选择进入后报错。
重新执行sudo make install,执行成功后再次启动系统。
界面给出了当前新内核的加载选项以及前版本加载选项。选择前版本,可以看到3.11.0.4和3.10.5.0两个版本。也就是说包括第一次加载了一半的系统也给出了选项。
目前的问题是:
启动新内核后,usb的键盘鼠标都不能用了。
用老系统安全模式重新登录,查阅资料,看到文章http://bbs.chinaunix.net/thread-1922275-1-1.html,联想到自己的情况确实也有modules加载失败的提示。
重新执行sudo make modules_install然后sudo make install
发现系统自动后,进入不稳定状态,经常出现标准版不能正常登录,修复版本可以normal reboot的状态。而且这个现象不稳定,重启后存在恢复可能。
担心是grub问题,按照网上介绍修改了分辨率问题,结果只是改善了显示效果,不解决实际问题。遂决定重新make mrproper。
结果发现一切照旧。
后来偶然发现原来系统本身没有问题,只是linux 默认启动等待10秒.在这10秒里,键盘鼠标无响应。
修改方法参照http://blog.csdn.net/binbinxyz/article/details/8498452为、
$ sudo cp /boot/grub/grub.cfg /boot/grub/grub.cfg.bak
$ sudo vi /boot/grub/grub.cfg
输入以下内容以快速查找定位相关配置信息
:/timeout
其中
set timeout=10
表示默认等待时间是10秒(注意:单位是秒)。
但是发现没有用。用秒表统计,修改后,选择内核版本后,界面超过一分钟无任何信息。按esc后,正常启动。
后仔细观察看到提示ata_id[6864]: HDIO_GET_IDENTITY failed for '/dev/sdb': Invalid argument.
根据http://ubuntuforums.org/showthread.php?t=2146901上的提示,问题的原因在于新增的硬盘没有分区表。
可是奇怪的是,系统提示出错的硬盘是/dev/sdb,而新增的硬盘其实是/dev/sda。问题是?
关于HDIO的详细解释参见http://ww2.cs.fsu.edu/~rosentha/linux/2.6.26.5/docs/ioctl/hdio.txt
其中描述
HDIO_GET_IDENTITY get IDE identification info
usage:
unsigned char identity[512];
ioctl(fd, HDIO_GET_IDENTITY, identity);
inputs: none
outputs:
ATA drive identity information. For full description, see
the IDENTIFY DEVICE and IDENTIFY PACKET DEVICE commands in
the ATA specification.
error returns:
EINVAL (bdev != bdev->bd_contains) (not sure what this means)
ENOMSG IDENTIFY DEVICE information not available
notes:
Returns information that was obtained when the drive was
probed. Some of this information is subject to change, and
this ioctl does not re-probe the drive to update the
information.
This information is also available from /proc/ide/hdX/identify
进入命令行,执行sudo hdparm -i /dev/sda 和 sudo hdparm -i /dev/sdb果然在/dev/sdb下提示获取硬件规格信息异常。
具体内容如下
wlan_ac@wlan:/proc$ sudo hdparm -i /dev/sdb
/dev/sdb:
SG_IO: bad/missing sense data, sb[]: 70 00 05 00 00 00 00 0a 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
HDIO_GET_IDENTITY failed: Invalid argument
wlan_ac@wlan:/proc$ sudo hdparm -i /dev/sda
/dev/sda:
Model=Hitachi HTS547575A9E384, FwRev=JE4OA60A, SerialNo=J2541054F8K52E
Config={ HardSect NotMFM HdSw>15uSec Fixed DTR>10Mbs }
RawCHS=16383/16/63, TrkSize=0, SectSize=0, ECCbytes=4
BuffType=DualPortCache, BuffSize=8192kB, MaxMultSect=16, MultSect=16
CurCHS=16383/16/63, CurSects=16514064, LBA=yes, LBAsects=1465149168
IORDY=on/off, tPIO={min:120,w/IORDY:120}, tDMA={min:120,rec:120}
PIO modes: pio0 pio1 pio2 pio3 pio4
DMA modes: mdma0 mdma1 mdma2
UDMA modes: udma0 udma1 udma2 udma3 udma4 udma5 *udma6
AdvancedPM=yes: mode=0x80 (128) WriteCache=enabled
Drive conforms to: unknown: ATA/ATAPI-2,3,4,5,6,7
* signifies the current active mode
在ubuntu论坛发帖请教问题原因,结果被人建议不要用再用menuconfig了,理由是grub2现在已经默认安装好了。恩,mark一下,以后注意。
http://apexu.com/apexu/tw/modules/publisher/item.php?itemid=1
考虑到项目进度,内核编译阶段到此结束。遗留问题是登录界面需要esc的问题。,此后进入调试篇。
++++++++++++++++++++++++++++++++++++我是调试篇的分割线++++++++++++++++++++++++++++++++++++++++++++++++++++++++
编写了内核模块的代码,标准格式模板如下:
#include
#include
#include
static int __init init_module(void)
{
printk("<1>Hello world 1.\n");
return 0;
}
static void __exit cleanup_module(void)
{
printk(KERN_ALERT "Goodbye world1.\n");
}
module_init(init_module);
module_exit(cleanup_module);
编译出错。
发现是内核源代码路径问题,/usr/src下只有头文件
将源代码放入/ usr/src并且在makefile中指定路径。
结果依然出错,提示找不到Makefile。原来linux系统默认必须用这个文件名。
修改makefile后,makefile如下
ifneq ($(KERNELRELEASE),)
obj-m =test_module.o
else
KERNEL_DIR ?= /usr/src/linux-lts-saucy-3.11.0
PWD := $(shell pwd)
default:test_modules
test_modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
endif
clean:
rm -rf *.tmp*.o *.o *.ko* *.mod* *.cmd *odule* *~
此时代码提示编译错误。
make -C /usr/src/linux-lts-saucy-3.11.0 M=/home/wlan_ac/workspace/kernelPrj modules
make[1]: 正在进入目录 `/usr/src/linux-lts-saucy-3.11.0'
CC [M] /home/wlan_ac/workspace/kernelPrj/test_module.o
/home/wlan_ac/workspace/kernelPrj/test_module.c:16:1: 错误: 对‘init_module’的静态声明出现在非静态声明之后
include/linux/module.h:70:12: 附注: ‘init_module’的上一个声明在此
/home/wlan_ac/workspace/kernelPrj/test_module.c:22:1: 错误: 对‘cleanup_module’的静态声明出现在非静态声明之后
include/linux/module.h:71:13: 附注: ‘cleanup_module’的上一个声明在此
/home/wlan_ac/workspace/kernelPrj/test_module.c:26:1: 错误: ‘init_module’重定义
/home/wlan_ac/workspace/kernelPrj/test_module.c:15:20: 附注: ‘init_module’的上一个定义在此
/home/wlan_ac/workspace/kernelPrj/test_module.c:27:1: 错误: ‘cleanup_module’重定义
/home/wlan_ac/workspace/kernelPrj/test_module.c:21:20: 附注: ‘cleanup_module’的上一个定义在此
make[2]: *** [/home/wlan_ac/workspace/kernelPrj/test_module.o] 错误 1
make[1]: *** [_module_/home/wlan_ac/workspace/kernelPrj] 错误 2
make[1]:正在离开目录 `/usr/src/linux-lts-saucy-3.11.0'
make: *** [test_modules] 错误 2
怀疑是因为引用了系统保留字作为函数名(该死的网络教程!!)
修改后,编译正常。
执行insmod test_module.ko,装载成功(不能少了ko!)
执行lsmod 可以看到该模块已经装载。
执行rmmod test_module.ko卸载成功
但是,程序中的printk没有结果输出到终端屏幕。试图进入 /var/log/messages,发现/var/log下没有messages!
查到http://blog.sina.com.cn/s/blog_966f8e8501011gu6.html中描述修改信息输出级别的方法,无效。
终于看到一篇靠谱的文章,验证后发现很准确
http://www.linuxidc.com/Linux/2011-02/32132.htm
不管怎么做,总是在控制台看不到printk的输出,而查看日志的方法非常受限于日志文件的大小和刷新。
尝试很久,终于怀疑是ubuntu的特殊性导致的。
专门查了ubuntu的文章,果然有了如下收获.文章链接http://blog.163.com/ljf_gzhu/blog/static/1315534402012112443956156/
简要的说就是:
在Linux中,驱动程序工作在内核态,内核与用户之间的交互是通过控制台(dev/console)实现的,控制台与终端的概念对于我们“年轻人”来说是容易混淆的,我本人到现在也不是彻底搞明白(菜鸟啊)。内核打印函数printk的输出被定向到文件 /dev/console,但是在Ubuntu环境中,我们使用的通常是虚拟终端 /dev/pts/n。其中n为虚拟终端的编号,如果你当前打开了第3个虚拟终端,那么第3个终端的对应的设备文件即为 /dev/pts/3。可以通过命令tty查看当前终端所对应的设备文件。
在弄清楚了上述概念之后,就不难明白为什么平时在图形界面终端(虚拟终端)下调试驱动程序时printk的输出都看不到了。有两种比较简单的方法可以查看驱动的输出信息,如下所示。
方法一:dmesg命令
说明:采用该方法的缺点是需要每次手动执行命令查看消息。
方法二:cat /proc/kmsg &
说明:别忘记在命令参数最后加个与号 & 让cat命令工作在后台。采用该方法的优点是只要驱动有消息输出马上就会在终端看到。
注:此处只给出解决方法(策略)而非原理(机制)。
执行后,果然 看到了完整的输出!!
加载和卸载查看模块的命令众所周知是insmod,rmmod,lsmod。
修改编译的时候主要遇到的问题:
1,头文件缺乏
2,需要在makefile里指定源代码和头文件目录
3.make clean时错误删除了源代码!!
放一个ok的makefile上来。
ifneq ($(KERNELRELEASE),)
obj-m =test_module.o
ecc-objs =
else
KERNEL_DIR ?= /usr/src/linux-lts-saucy-3.11.0
PWD := $(shell pwd)
default:test_modules
test_modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) -I /usr/src/linux-lts-saucy-3.11.0/include/ modules
endif
clean:
rm -rf *.tmp *.o *.ko *.ko* *.cmd *.mod.* *~ *.symvers *.order
出错的makeclean是rm -rf *.tmp *.o *.ko *.ko* *.mod.* *.cmd *odule* *~
问题描述:
在编译内核模块驱动时,如果出现如下警告信息:
warning: the frame size of 1040 bytes is larger than 1024 bytes。主要是因为内核中设置了堆栈报警大小,其默认为1024bytes。我们主要将其修改为4096既可以消除告警信息。如果解决:
(1)make menuconfig(2)kernel hacking
(3)修改warn for stack frames larger than 的数值,将其修改为4096(最好不要大过这个数值)(4)重新编译内核模块则不会出现如上的告警信息。
小结
关于内核里的程序开发
1.大内存的申请,不推荐使用临时局部变量。因为临时局部变量占用了线程的堆栈空间。而,内核线程的堆栈空间限制为1024字节。即使重新编译内核,最大也不会超过4k。
但是如果使用全局变量定义或者局部静态变量定义,占用的是全局空间,因此不会需要收到1024字节的限制。
2.内核线程创建后,需要通过执行wake_up_process(),启动线程。并在模块退出时主动调用kthread_stop()进行线程的释放。
3.线程内部为了在线程运行期间,及时接收到线程退出的命令,并且及时退出线程,需要周期性调用kthread_should_stop()判断线程当前的状态。该函数返回成功,则break循环。
4.线程运行期间,如果空闲,需要调用schedule_timeout_interruptible(time)及时交出cpu使用权。