Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379691
  • 博文数量: 87
  • 博客积分: 983
  • 博客等级: 准尉
  • 技术积分: 685
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-25 07:20
文章分类

全部博文(87)

文章存档

2016年(1)

2015年(3)

2014年(55)

2013年(13)

2012年(15)

分类: 嵌入式

2014-03-27 15:35:15

转载自http://blog.csdn.net/yanghanxing110/article/details/21884111
很多人和我一样应该都是从应用开发,想着手开始驱动的学习,我现在也是在做嵌入式这方面的开发,但对于驱动的掌握也不太好,我现在手上有本《LINUX设备驱动程序》第三版的书,我通过这本书及网上资源的学习,尽量每天都把学习的内容及编写的代码贴出来,下面就开始了。

首先必须要有LINUX操作系统的环境,建议在虚拟机上装,硬盘分配的空间多一点,我现在用的是Ubantu10.4的,内核版本是2.6.32的版本。我硬盘分的大小是60G,不过现在发现已经不太够用,因为一个安卓的源代码都十几G了,反正如果空间够,尽量分配多点空间给虚拟机。安装时最好把所有的工具都安装上,以防要用的时候不能马上用上。

要想为内核构造模块,必须在自己的系统中配置并构造好内核树驱动程序和用户程序可不一样,它是作为一个模块连接到内核模块来运行的,运行在内核空间里面。所以要运行我们自己构造的模块,需要自己的系统已经配置好内核树,然后把目标模块和内核树连接起来运行。
     
    1、构建内核树:
     (1)查看自己的系统是否已经有了内核树。
如果在该目录下已存在build文件夹,则代表你已经有内核数了,如果没有,那也没事,那就得自己构建内核树了。
     (2)构建内核树:我虚拟机上已经有了,在网上我查到了如下构建内核树的步骤。

内核树构建过程

安装编译内核所需要的软件(也可不装,除非你要用 make menuconfig,用make oldconfig不要)

sudo apt-get install build-essential kernel-package libncurses5-dev fakeroot

下载内核源码

先查看linux内核版本:$uname -r

网上说用apt-cache search linux-source命令, 会列出一些可选源码包,对准你的内核版本号,选择“with Ubuntu patche”的那个

最后用apt-get install linux-source-2.6.35下载之。解压缩源码包,进入解压后的源码目录。

 可是我试了,搜不到,但是还是可以直接用上面的apt-get 命令下载的,但是我下载,也可以直接到这个网址下源代码,这里面有各个版本的内核,从1.0到2.6的,都有。

在编译之前我们需要Ubuntu原来内核的一个配置文件,这是我/usr/src目录下的文件预览:ls -al

drwxr-xr-x  4 root root     4096 2010-09-04 21:31 fglrx-8.723.1

drwxr-xr-x 24 root root     4096 2010-09-04 20:35 linux-headers-2.6.35-22

drwxr-xr-x  7 root root     4096 2010-09-04 20:35 linux-headers-2.6.35-22-generic

drwxr-xr-x 25 root root     4096 2010-09-16 21:39 linux-source-2.6.35

-rw-r--r--  1 root root 65846876 2010-09-01 22:41 linux-source-2.6.35.tar.bz2

现在我们需要/boot目录下的config-2.6.35-22-generic文件,我们把它拷贝到我们刚下好解压的目录,也就是linux-source-2.6.35

sudo cp /boot/config-2.6.35-22-generic /usr/src/linux-source-2.6.35/.config

接下来切换到root用户

sudo -i

cd /usr/src/linux-source-2.6.35

make menuconfig或者直接make oldconfig(无需拷贝.config)

终端会弹出一个配置界面

最后有两项:load a alternative kernel configuration... 

save a alternative configuration... 

选择load a kernel configuration保存,然后在选择save akernel configuration再保存退出,并退出配置环境。

 

接下来我们就要开始编译了。

#cd /usr/src/linux-source-2.6.35

#make

记住一定要是管理员帐号运行,这个过程很久,如果你的cpu是双核的可以在make后面加个参数,make -j4.


#make bzImage 执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。 


2、构建完内核树,就可以开始驱动程序的编写及运行了。

以下通过实现打印Hello,world。来编写第一个驱动程序


[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. hello.c  
[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include   
  2. #include   
  3. MODULE_LICENSE("Dual BSD/GPL");  
  4.   
  5.   
  6. static int hello_init(void)  
  7. {  
  8.   printk(KERN_ALERT "Hello, world\n");  
  9.   return 0;  
  10. }  
  11.   
  12.   
  13. static void hello_exit(void)  
  14. {  
  15.   printk(KERN_ALERT "Goodbye, cruel world\n");  
  16. }  
  17.   
  18. module_init(hello_init);  
  19. module_exit(hello_exit);  



[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Makefile  
[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ifeq ($(KERNELRELEASE),)  
  2.     # Assume the source tree is where the running kernel was built  
  3.     # You should set KERNELDIR in the environment if it's elsewhere  
  4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build  
  5.     # The current directory is passed to sub-makes as argument  
  6.     PWD := $(shell pwd)  
  7. modules:  
  8.   $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  9. modules_install:  
  10.   $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  
  11. clean:  
  12.   rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions  
  13. .PHONY: modules modules_install clean  
  14. else  
  15.     # called from kernel build system: just declare what our modules are  
  16.     obj-m := hello.o   
  17. endif  

以上为hello.c及Makefile文件,通过make即可得到hello.ko文件,该文件即为将要加载如内核的模块。


hello_init(void)函数将在模块被装载入内核时调用。

hello_exit(void)函数将在模块被移除时调用。

printk()函数类似于printf函数,用于信息打印。

Makefile看不懂的话,不急,先把程序Make下,接下来会专门把makefile的知识进行一次解读的,务必做到简单,每个人都会用,现在先跳过。

根据函数的功能,可以预期代码实现的效果为,当模块载入时,打印“Hello,world”
当模块被移除时,打印“Goodbye,cruel world”.
接下来让我们把hello.ko模块载入内核,在命令行中敲入insmod hello.ko,此动作将把hello.ko载入内核。
相似的rmmod则为移除。从字面上很容易理解两个操作的含义。以下为实际操作情况。

不对,那个美好的Hello,world并没有输出耶!
是不是程序有问题?那大家想想问题会是出在哪里呢?
很简单的代码,很容易想到,问题出在printk函数上。
让我们来看下代码:
[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1.   printk(KERN_ALERT "Hello, world\n");  
是的,KERN_ALERT这个参数出现了问题。
接下来讲解下关于printk的简单知识,你就清楚为什么没打印了。
3、printk相关注意事项:
   printf和printk的一个不同的地方
  用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。
  没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到
  日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):
  #define KERN_EMERG    0
  #define KERN_ALERT     1
  #define KERN_CRIT       2
  #define KERN_ERR        3
  #define KERN_WARNING  4
  #define KERN_NOTICE    5
  #define KERN_INFO       6
  #define KERN_DEBUG     7
可以看到代码中我们用的是KERN_ALERT修改代码将printk语句改为:
printk(KERN_EMERG "Hello, world\n");
重新编译,便查看结果,就正确了。

      那刚刚的载入时,信息跑哪了,怎么无故失踪了?接下来看看一个地方,就恍然大悟了:

没错,刚刚的信息打印在这了。
阅读(750) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~