Ubuntu下Hello World驱动实现全过程

来源:互联网 发布:淘宝外星伴侣 正品吗 编辑:程序博客网 时间:2024/04/29 10:40


今天是一个值得纪念的日子,我也不知道我花了多长时间才编译成功!在编写这个驱动的过程中,真的体会到驱动编程不是那么简单的~,现在有点小小的激动,真的,很不容易啊~我把整个过程在重复一遍吧!

在编写这个驱动的过程中参考了网上的很多文档,最终的结果是弄的我头都晕了,每个人写的都不一样,其实我现在还有一些概念不是很清楚。

1:到底丫的什么是内核源码树?

2:为什么要编译内核源码树?

1:内核源码树我现在的理解就是整个linux内核源代码,它是编译驱动的前提。Ubuntu系统默认情况下是没有的,

内核源码树是要自己下载的。

2:驱动最终以*.ko的形式生成,insmod的本质就是将ko文件与运行的内核进行链接的过程。类似于编译helloworld的链接过程。

链接必然需要先进行编译,以便确定所需的外部符号(EXPORT_SYMBOLS)是否存在,因为有些符号(函数或全局变量)在

内核中。在驱动中如果使用到这些符号,必须预留一个位置,insmod时进一步确定这些符号的具体位置(符号绑定)。

如果内核都没有编译过,怎么知道这些符号有没有编入内核中。

驱动程序简介

驱动程序作为系统内核的一部分,它工作在核心态,而应用程序工作在用户态。也就是说,不能直接通过指针,把用户空间的数据地址传递给内核(因为MMU映射的地址根本不一样)。要想在应用程序和驱动程序之间传递数据(指针),就需要经过转换。把用户态“看到”的空间地址转换成内核态可访问的地址。Linux系统提供了一系列方便的函数实现这种转换,如get_user、put_user、copy_from_user、copy_to_user等,它们自己负责访问权限的检查,使用时,不需要关系更多的问题。

     Linux内核把驱动程序划分为3种类型:字符设备、块设备和网络设备。

字符设备和块设备可以像文件一样被访问。它们的主要区别不在于能否seek,而是在于系统对于这两种类型设备的管理方式。应用程序对于字符设备的每一个I/O操作,都会直接传递给系统内核对应的驱动程序;而应用程序对于块设备的操作,要经过系统的缓冲区管理,间接传递给驱动程序处理。块设备的这种管理方式是为存储提供优化的;而字符设备的管理方式是为操作提供优化的。

至于网络设备,它在Linux系统中是一类比较特殊的设备它不像字符设备或块设备那样通过对应的设备文件节点去访问,内核也不再通过read和write等调用去访问网络设备。Linux的网络系统主要是基于BSD UNIX的套接字机制,在系统和驱动程序之间有专门的数据结构进行数据传输,系统支持对数据发送和数据接收缓存,提供流量控制机制,提供更多的协议支持。

     在linux系统中,驱动程序都做成模块的形式,也就是module。简单的说,一个模块提供一个功能,这些模块是可以按照需要随时装入内核空间和从内核空间卸载的。因此,内核模块是为了给内核动态增减功能而设计的,并不仅仅是限于驱动程序。

     因为内核模块需要加载到内核空间,所以其程序的编写与一般应用程序不同,在里面再也找不到类似main()这样的入口函数。


Hello World驱动实现全过程

1:首先要查看自己系统是使用的内核版本

www.linuxidc.com @linux:~$  uname -r

3.2.0-34-generic-pae  //我用的是ubuntu12.04版本的,比较高

如果系统自动安装了源码。在/usr/src目录下面会有对应的使用的版本,我那个版本没有,我是自己下载的

www.linuxidc.com @linux:/usr/src$ ls

linux-headers-3.2.0-34              linux-source-3.2.0(我下载的)

linux-headers-3.2.0-34-generic-pae

如果没有源码,查看一下可以下载的源码包(不要使用超级用户使用此命令,他会提示没有这个命令)

#apt-cache  search  linux-source

www.linuxidc.com @linux:/usr/src$ apt-cache  search  linux-source

linux-source - Linux kernel source with Ubuntu patches

linux-source-3.2.0 - Linux kernel source for version 3.2.0 with Ubuntu patches

2:然后下载linux-source-3.2.0

#sudo apt-get  install  linux-source-3.2.0

下载完成之后,会在/usr/src下,文件名为linux-source-3.2.0.tar.bz2这个压缩包,解压之后就可以得到整个源代码

# sudo  tar jxvf  linux-source-3.2.0.tar.bz2

解压之后会生成一个新的目录/usr/src/linux-source-3.2.0,现在所有的源代码都在里面

3:现在开始配置内核,有三种选择选择方式1:make oldconfig  2:make  menuconfig 3:make  xconfig

我选择的是最快的配置原版的配置方法

#sudo  make  oldconfig

(如果配置的过程中出现问题,原因是因为你没有下载配置环境的那个依赖的库文件,可以自己下载)

配置完成之后就要开始对内核进行编译了

4:编译内核

#sudo  make

这个过程很漫长,建议去做做别别的事情吧~,我大概花了一个多小时,吃完晚饭回来就ok了

#sudo  make  zImage    // 或者 make bzImage

编译内核的结果最终出现了几个错误,但最终对那个Hello  World 没有造成影响

执行结束之后,会在当前目录下面生成一个新的文件:vmlinux

5:然后就是编译模块了

#sudo make  modules

6:安装模块

#sudo  make  modules_install

执行结束之后,会在/lib/modules下生成一个新的目录/lib/modules/3.2.31/

在随后的编译模块文件时,需要用到这个路径下的build目录。到这一步,内核就编译完成了

好了,下了很长时间的Hello  World 程序现在终于可以派上用场了

我的Hello  World在/home/xiongyao/下面


//hello.c

#include<linux/init.h>#include<linux/kernel.h>#include<linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){printk(KERN_EMERG "Hello ,Linux Driver!\n");return 0;}static void hello_exit(void){printk(KERN_EMERG "Hello Diver Exit !\n");}module_init(hello_init);module_exit(hello_exit);

Makefile 文件

obj-m := hello.oKERNELDIR := /lib/modules/3.2.31/buildPWD :=$(shell pwd)  modules:  $(MAKE)  -C  $(KERNELDIR)  M=$(PWD)  modules.PHONY :cleanclean:rm -rf *.o *ko

make -C $(KERNELDIR) M=$(PWD) modules

这句话的意思: -C是改变make的执行目录,$(KERNELDIR)是你本机的kernel的路径地址,

M=$(PWD) modules这个是使makefile在试图生成模块之前,回到模块源码目录,说的在直接点就是把该makefile同目录下的 modules 给到M,执行make指令。

obj-m :=hello.o目标文件依赖hello.o


最后关键的一步到了,所有准备就绪,进入makefile的那个目录

www.linuxidc.com @linux:~$ sudo make
make -C /lib/modules/3.2.31/build M=/home/xiongyao 
make[1]: 正在进入目录 `/usr/src/linux-source-3.2.0/linux-source-3.2.0'
  LD      /home/xiongyao/built-in.o
  CC [M]  /home/xiongyao/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/xiongyao/hello.mod.o
  LD [M]  /home/xiongyao/hello.ko
make[1]:正在离开目录 `/usr/src/linux-source-3.2.0/linux-source-3.2.0'

上面证明已经成功了

然后加载模块
#sudo  insmod  ./hello.ko

本应该会在终端显示hello,world  但是终端什么也没有显示(以后在去解决)

查看加载模块

#sudo  lsmod 

里面已经生成了hello

hello  2560 0

^_^,已经加载上了 

删除模块

#sudo  rmmod  hello

那么程序的输出到底在哪里呢?在网上看到,如果不出现在终端,则会下进syslog中

#cat  /var/log/syslog  |grep  world

#Hello,world
#Goodbye,linux  world

现在所有工作全部完成了,希望做这个Hello World 是我进入linux驱动的第一步~,相信你们也能编译的!成功的编译成功的。

注意事项

程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。
    也可以如下编译:
    gcc -c -I /usr/src/linux-2.4/include/ hello.c
    运行:
    insmod hello.o

<1>gcc编译选项中增加-c
<2>gcc编译选项中定义两个宏:-DMODULE -D__KERENL__
或直接在源文件中定义这两个宏:
#define MODULE
#define __KERNEL__
<3>在源文件中包括module.h文件:
include "linux/module.h"
<4>假定你现在运行的内核的源码目录绝对路径是MyKernelSrcPath,在gcc编译时增加选项:
-I $MyKernelSrcPath/include (-I /usr/src/linux/include)
<5>某些时候用insmod -f能够成功加载,但需谨慎使用。
<6>如果看不到用printk打印的信息,可以用dmesg命令看。
<7>打印消息受级别的限制,消息级别可以通过printk设置,:
printk(" nsomething");   /* 其中0<=n<=7 */
假设控制台的消息级别为m, n
这样一方面可以提高要打印消息本身的级别(数字越小级别越高),
另一方面可以改变控制台的消息级别(可从18),如改为8可用以下命令:
# echo "8" > /proc/sys/kernel/printk


http://www.linuxidc.com/Linux/2012-12/75891.htm

http://www.cnblogs.com/xmphoenix/archive/2011/10/24/2223148.html

http://www.360doc.com/content/10/0817/22/2611294_46807637.shtml

0 0
原创粉丝点击