zoukankan      html  css  js  c++  java
  • 一步一步实现Linux设备驱动的Helloworld模块

    学了那么多程序语言,总是有一个Hello world开头,不禁感叹Hello world的强大。呵呵,废话少说,咋们的故事当然要从这个Hello world开始。

    先查看自己OS使用的内核版本
    [dongliang@dongliang:~]$ uname -r
    2.6.22-14-generic /* 这是我显示的结果 */

    如果安装系统时,自动安装了源码。在 /usr/src 目录下有对应的使用的版本目录。例如下(我是自己下的)
    [root@localhost :/usr/src]# ls
    linux-headers-2.6.22-14
    linux-headers-2.6.22-14-generic
    linux-source-2.6.22 /*这个就是解压后的源码目录 */
    linux-source-2.6.22.tar.bz2 /* 这是我下的源码 包 */

    如果没有源码。(一般ubuntu 都没有吧)
    查看一下可一下载的源码包(切记不要使用超级用户使用此命令否则……会提示没有此命令)
    root@localhost :/usr/src# apt-cache search linux-source
    linux-source - Linux kernel source with Ubuntu patches
    xen-source-2.6.16 - Linux kernel source for version 2.6.17 with Ubuntu patches
    linux-source-2.6.22 - Linux kernel source for version 2.6.22 with Ubuntu patches
    root@localhost :/usr/src#

    我选择了 linux-source-2.6.22 - Linux kernel source for version 2.6.22 with Ubuntu patches 这个~
    然后 install 之

    root@localhost # sudo apt-get install linux-source-2.6.22

    下载完成后,在/usr/src下,文件名为:linux-source-2.6.22.tar.bz2,是一个压缩包,解压缩既可以得到整个内核的源代码:

    注意 已经切换到超级用户模式

    root@localhost :/usr/src# tar -jxvf linux-source-2.6.20.tar.bz2

    解压后生成一个新的目录/usr/src/linux-source-2.6.22,所有的源代码都在该目录下。

    进入该目录

    开始配置内核 选择最快的原版的配置(默认)方式 (我是如此)

    root@localhost :/usr/src/linux-source-2.6.22# make menuconfig

    当然你也可以使用 自己喜欢的配置方式 如 menuconfig , xconfig(必须有GTK环境吧)。反正不用剪裁什么,所以不管那种方式能配置它就行了。

    完成后,开始make 吧 这儿比较久 一般有1一个小时吧。(保证空间足够 我编译完成后 使用了1.8G) 我分区时分给/目录30G的空间,我没遇到这问题。倒是我朋友遇到了。

    root@localhost :/usr/src/linux-source-2.6.22$ make

    root@localhost :/usr/src/linux-source-2.6.22$ make bzImage

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

    然后 :

    root@localhost :/usr/src/linux-source-2.6.22# make modules /* 编译 模块 */

    root@localhost :/usr/src/linux-source-2.6.22# make modules_install /* 安装 模块 */

    执行结束之后,会在/lib/modules下生成新的目录/lib/modules/2.6.22-14-generic/
    。 在随后的编译模块文件时,要用到这个路径下的build目录。至此,内核编译完成。可以重启一下系统。

    至此 内核树就建立啦 原来不是很难.....

    (1)linux开源当然少不了源代码的贡献,请看下边(至于什么是开源,悲剧的我现在也没整明白):

    #include <linux/init.h>  //所有模块代码中都包含一下两个头文件
    #include <linux/module.h>
    MODULE_LICENSE("Dual BSD/GPL"); //所有模块代码都应该指定所使用的许可证
    
    static int hello_init(void)  
    {  
          printk(KERN_ALERT "Hello,world
    ");  
    return 0;  
    }   
    
    static void hello_exit(void)  
    {  
          printk(KERN_ALERT "Goodbye,Cruel world
    ");  
    
    }  
    
    module_init(hello_init);  
    module_exit(hello_exit);   
    

       看到这里我们明白了,驱动程序说白了就是提供函数接口给用户空间的程序调用。在c语言中都有main()入口,那设备驱动程序的入口在哪儿呢?你猜对了,就是module_init(),它的参数是一个函数指针,告诉说咋们的入口在hello_init()。明白这层意思,module_exit()就不用多说了吧..

    (2)连源码都给你了,也就不吝啬一个makefile了如下:

    ifneq ($(KERNELRELEASE),)  
         obj-m := hello.o        #设置模块名字
    else
         KERNELDIR :=/lib/modules/$(shell uname -r)/build   #将目录改为内核所在目录
         PWD := $(shell pwd)  
    all:  
         $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules  
    endif  
    clean:  
         rm -f *.o *.ko *.mod.c .hello*    
    

    千万不要说不懂makefile,大千世界,连地图都不懂,也不知道咋混的。这里要说的是 $(MAKE) 这里一定是大写MAKE,我开始小写,怎么都过不去,郁闷啊..

         $(MAKE) -C $(KERNELDIR) SUBDIRS = $(PWD) modules这句啥意思?就是说首先改变目录到-C选项指定的目录(即内核源代码目录),其中保存了内核的顶层makefile文件。SUBDIRS=选项让该makefile在构造modules目标返回之前到模块源代码目录。然后,modules目标指向obj-m变量设定的模块。(其实,这样的写Makefile命令还是有些烦人,可有啥办法呢,谁让咱们是笨鸟,聪明的方法?有,那要见下篇介绍-----聪明的makefile,嘿嘿)。

    (3)好了,该有的都有了,不该有的咋一点也不贪。下面make一番:

    [root@localhost~]# make
    make -C /lib/modules/2.6.29.4-167.fc11.i686.PAE/build SUBDIRS=/root/device modules
    make[1]: Entering directory `/usr/src/kernels/2.6.29.4-167.fc11.i686.PAE'
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /root/device/hello.mod.o
      LD [M]  /root/device/hello.ko
    make[1]: Leaving directory `/usr/src/kernels/2.6.29.4-167.fc11.i686.PAE'

    这是生成了hello.ko模块(如果没有,是有八九就是makefile有问题,还说会写,露了原形吧),接下来可以看结果了

    首先再打开一个终端B(刚才make的那个不要关了,叫它A吧),像这样
    [root@localhost~]# tail -f /var/log/messages 

    然后在A终端输入
    [root@localhost~]# insmod ./hello.ko  哈哈在B终端是不是看到了localhost kernel:hello,world

    然后在A终端输入
    [root@localhost ~]# rmmod hello 哈哈在B终端是不是看到了localhost kernel:Goodbye,Cruel world

    查看加载模块
    [root@localhost ~]# lsmod
    Module Size Used by
    hello 2560 0
    已经加载上咯~~

    那程序的输出在那呢?书中说明 如果不出现在终端 则会写进 syslog 文件中
    dongliang@dongliang:~/linux_驱动开发$ cat /var/log/syslog | grep world
    Mar 16 12:14:53 shana kernel: [ 5937.529297] Hello, world
    Mar 16 12:16:05 shana kernel: [ 6009.439036] Goodbye, cruel world

       至此,一个最简单的helloworld的设备驱动演示程序就完成了,是不是挺好玩,关键是挺兴奋,这才是关键。哈哈

       当工人就得劳动;当军人就的准备打仗(不然某些人老是欺负咋们了不是),所以嘛,当程序员,就一定要在最后来个说明注意什么的,烦躁啊..

    那就说明吧:
    (1) 这个helloworld,我建议在fedora 下实现,在centos或redhat或那些我没试过的,是有些问题的。    这主要是要在某些系统下是不支持模块的,这就要重新编译内核,选上“Enable loadable module        support”,这样才可以。你偏要忘北走,我也不能用个绳子套着你喝水是吧,不管了,反正我今天关心的helloworld完成了,这难道就是传说中的责任分工,那个?呵呵,等着,我改天一定不上。
    (2) 在编写makefile时,所有的换行后的空格都是tab键,而不是space(空格键)。
    (3) 本例程的文件保存为 hello.c  ,不然编译不通过。

    学习心得:
    (1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。
    (2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
    (3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。
    (4)要十分注意驱动程序的并发处理。
    (5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。
    (6)内核代码不能实现浮点书运算。

  • 相关阅读:
    Matlab绘图基础——利用axes(坐标系图形对象)绘制重叠图像 及 一图多轴(一幅图绘制多个坐标轴)
    [学习笔记]Javaweb开发视频教程之Tomcat9配置
    Matlab绘图基础——axis设置坐标轴取值范围
    Cauchy-Binet公式的证明 及 对《来自特征值的特征向量》的理解
    [问题解决]win10误删启动项(BCD)(HP电脑亲测,无需启动盘,并非重装系统)
    [经验分享]用自相似的思想来理解二叉树的三种遍历方法
    [参考]用递归的方法获取 字符 对应的 二进制字符串 (C/C++)
    [经验分享]SecureCRT导出操作日志 + Notepad自定义语言格式高亮日志文件
    [公式推导]一般线性秩统计量的方差函数 及其 极限分布
    [问题解决]RedHat7更换CentOS7的yum源时踩过的坑
  • 原文地址:https://www.cnblogs.com/hdk1993/p/4910386.html
Copyright © 2011-2022 走看看