注,部分内容参照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$ ll总用量 26776
drwxr-xr-x 3 root root 4096 3月 15 00:06 ./
drwxr-xr-x 23 root root 4096 3月 15 00:04 ../
-rw-r--r-- 1 root root 1007780 1月 31 02:07 abi-3.11.0-15-generic
-rw-r--r-- 1 root root 168536 1月 31 02:07 config-3.11.0-15-generic
drwxr-xr-x 3 root root 12288 3月 15 00:04 grub/
-rw-r--r-- 1 root root 17118280 3月 15 00:06 initrd.img-3.11.0-15-generic
-rw-r--r-- 1 root root 176764 11月 27 2011 memtest86+.bin
-rw-r--r-- 1 root root 178944 11月 27 2011 memtest86+_multiboot.bin
-rw------- 1 root root 2768346 1月 31 02:07 System.map-3.11.0-15-generic
-rw-r--r-- 1 root root 5962944 2月 5 04:12 vmlinuz-3.11.0-15-generic
安装必要的软件
sudo apt-get install fakeroot build-essential crash kexec-tools makedumpfile kernel-wedge
sudo apt-get build-dep linux
sudo apt-get install git-core libncurses5 libncurses5-dev libelf-dev asciidoc binutils-dev
获取源代码archive
sudo apt-get build-dep --no-install-recommends linux-image-$(uname -r)
apt-get source linux-image-$(uname -r)
工作目录home/wlan_ac下生成相关tar.gz文件以及子目录linux-lts-saucy-3.11.0
sudo make mrproper 清除代码(如果不需要重新编译整个内核,仅更新了部分代码,则不需要执行词句。)
进入/boot目录,看见系统当前配置文件config-3.11.0-15-generic
进行备份config-3.11.0-15-generic-org-bak
运行make menuconfig进入图形配置界面。通过加载系统当前配置/boot/config-3.11.0-15-generic 获得初始状态,然后可以通过修改该界面参数。修改之后可以选择保存到源代码所在目录/home/wlan_ac/linux-lts-saucy-3.11.0 后退出。
注意,系统默认生成的config文件名称为config.版本号。但是,编译内核时,需要在代码目录下指定需要使用的config文件,否则make dep失败.指定方法为,将需要编译的config文件修改为.config名称即可。
执行sudo make dep生成依赖关系
执行gcc -v查看gcc版本为4.6.3
执行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 <linux/module.h> #include <linux/vermagic.h> #include <linux/compiler.h> 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使用权。