学习编程第一个都是学习hello world程序,学习内核驱动自然也不例外,我也是!本文整理了网上的一些资料以及加上自己的一些心得体会,希望对初学者有帮助,可别小看这个简单的hello world,本人可是自己摸索了好几天呢!~~

在真正的写驱动之前我们先了解下linux内核,linux内核采用的是模块化编程,这样可以很容易的添加或删除一个功能,同时可以在内核运行的过程中可以动态的添加功能,这部分功能的代码被称为”模块“,我们写的驱动程序就是一个模块,但模块不仅仅局限于驱动,文件系统、网络……,都可以是模块。我们在真正的写驱动之前我们先需要了解模块的结构。

一、模块的编写

linux驱动是C语言编写的,就如我们写C语言的应用程序必须要有一个叫做“main”的函数,我们也模块的时候也有些类似的东西。

下面我们就来了解下模块。模块的组成中有些是必须的,有些是可选的我们先看下这些必须的,也就是一个模块最基本的组成,下面便是一个最基本的模块:

#include <linux/module.h>
        #include <linux/kernel.h>
        #include <linux/init.h>
        MODULE_LICENSE ("GPL");
        static int __init hello_init (void)
        {
                 printk (KERN_INFO "Hello world\n");
                 return 0;
        }
        static void __exit hello_exit (void)
        {
                 printk (KERN_INFO "Goodbye world\n");
        }
        module_init (hello_init);
        module_exit (hello_exit);

这个模块的代码可以分为四部分:

1、头文件
        #include <linux/module.h>
        #include <linux/kernel.h>
        #include <linux/init.h>

这些头文件是一个模块最基本的头文件,我们写一个模块的时候需要毫无理由的加这几个头文件,这几个头文件中有我们模块中最基本的一些符号的定义,所谓的符号就是一些结构体、宏等的定义。

2、许可证的申明

MODULE_LICENSE ("GPL");

我们在写模块的时候,一般都会申明许可证用来说明我们遵循"GPL"协议。Linux内核识别的许可证有“GPL”——任意版本的GNU通用公共许可证、“GPL v2”——GPL第二版、“GPL and additional rignts”——GPL及附加权利、“Dual BSD/GPL”——BSD/GPL双许可证和“Proprietary”——专有。

虽然许可证的申明没有严格的要求,但一般我们在写驱动的时候至少会选择一个"GPL",因为如果没有许可证的申明的话,那么这个模块就是一个专有的模块,而Linux本身遵循GPL,所以不愿意非GPL的模块加入内核。一个专有模块加入内核的时候,内核会认为内核被污染了而产生抱怨,如下:

[90518.655714] hello: module license 'unspecified' taints kernel.
        [90518.657320] Disabling lock debugging due to kernel taint

3、加载函数
        static int __init hello_init (void)
        {
        printk (KERN_INFO "Hello world\n");
        return 0;
        }
        module_init (hello_init);

加载函数又叫初始化函数,是向内核添加模块的时候被执行的。

4、卸载函数

static void __exit hello_exit (void)
        {
        printk (KERN_INFO "Goodbye world\n");
        }
        module_exit (hello_exit);

卸载函数又叫清除函数,是从内核中删除一个模块的时候被执行的。

上面的四部分是一个模块中最基本的组成,有了这四部分我们就可以说这是一个模块了。

二、模块的编译

有了代码了,而代码在执行前必须先编译。和编译应用程序不同,编译模块必须依赖一个配置并且编译过的内核源码树。因为Linux内核支持多平台,所以在使用之前必须先配置,而我们编译模块必然依赖一个和我们目标平台相同的内核源码树。怎么才能使用这个内核源码树编译我们的模块呢,这个在内核中有明确的说明,可以参考Linux内核目录下Documentation/kbuild/modules.txt,按照说明我们写一个Makefile,如下:

Obj-m := hello.o
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
        PWD := $(shell pwd)
        modules:
                 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
        modules_install:
                 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
        clean:
                 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
        .PHONY: modules modules_install clean

这个Makefile是一个通用的编译模块的Makefile,需要注意的是modules: 下一行必须要以tab键隔开,clean, PHONY同理,否则会报错——make: 没有什么可以做的为 `modules'。

在这里简单说一下内核源码树:内核源码的一种组织形式(经过编译的源代码),编写makefile时,需要指定你的内核源码树目录,方便使用内核中的符号和模块,上面已经说了,linux内核支持多平台,所以源码树也是多平台的,如:x86arm
上面/lib/modules/$(shell uname -r)/build 这个路径就是x86的内核源码地址
针对开发板arm架构的,需要用交叉编译器编译一个arm架构的内核源码树

在终端输入make

make -C /lib/modules/2.6.35-22-generic/build M=/home/linux/ex2-init-exit modules M=/home/linux/ex2-init-exit modules
        make[1]: Entering directory `/usr/src/linux-headers-2.6.35-22-generic'
         LD /home/linux/ex2-init-exit/built-in.o
         CC [M] /home/linux/ex2-init-exit/hello.o
         Building modules, stage 2.
                  MODPOST 1 modules
                 CC /home/linux/ex2-init-exit/hello.mod.o
                  LD [M] /home/linux/ex2-init-exit/hello.ko
        make[1]: Nothing to be done for `modules'.
        make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-22-generic'

通过编译就会生成一个.ko的文件,这就是我们的模块了。

三、模块的加载和卸载

我们在终端输入:
        insmod hello.ko   插入驱动
        rmmod hello.ko 删除驱动
        然后用dmesg|tail或者dmesg –c 清除内核log,再用dmesg 查看,就能看到我们在加载函数和卸载函数里打印的内容。

至此,x86平台hello world的驱动已经演示完毕,下面讲下如何编译arm架构的。

上面的驱动hello.ko,用file hello.ko 命令看下,你会看到它是x86的,但是最终我们的驱动是需要在车机上运行的,也就是在arm架构下运行,这是你需要用交叉编译器(我这里用的是arm-linux-gcc)编译成arm平台的驱动。

四、编译成arm平台的驱动模块

安装交叉编译工具arm-linux-gcc

参考链接:http://blog.csdn.net/hanzengyi/article/details/5991915

编译hello驱动

这里需要修改下makefile,把上面的内核源码树地址

KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
修改为arm的内核地址:如: KERNELDIR ?= /media/文档/xxx/kernel 
修改完makefile后,在终端输入:make ARCH=arm CROSS_COMPILE=arm-linux- 进行编译,编译后的hello.ko,使用file命令看下,你就会看到是arm平台的。

传到arm架构的开发板中

把编译好的驱动hello.ko传到开发板中,可以使用adb或者360手机助手来,建议使用adb工具。

将usb线连接电脑,上传到车机的目录,我一直都是传到 /data/local/tmp 中,因为在linux运行时,其他目录都是受linux保护的,不能执行。

adb push hello.ko /data/local/tmp

在终端敲上面的命令,把驱动传到开发板的/data/local/tmp目录下。

有没有传成功? 你可以使用adb shell 或者 连接串口线用超级终端或putty和开发板通信,进入开发板,然后打ls命令看下。(具体串口线怎么连接,超级终端或putty怎么使用,串口线需要根据实际的开发板的原理图,而超级终端或putty可以网上找)

在开发板中加载和卸载

上传到开发板中后,就可以使用和x86一样的命令来加载、卸载模块:insmod,rmmod,lsmod,dmesg, dmesg -c

五、总结

本文讲了x86、arm架构下hello world驱动的编写、编译、加载、卸载、dmesg查看内核log,您应该对内核驱动的开发流程有个了解,为以后编写复杂驱动打下基础。

linux内核驱动——从helloworld开始的更多相关文章

  1. Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...

  2. Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

    1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2 ...

  3. linux内核驱动模型

    linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构 ...

  4. linux 内核驱动--Platform Device和Platform_driver注册过程

    linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Pla ...

  5. 【引用】Linux 内核驱动--多点触摸接口

    本文转载自James<Linux 内核驱动--多点触摸接口>   译自:linux-2.6.31.14\Documentation\input\multi-touch-protocol.t ...

  6. Linux内核驱动开发之KGDB原理介绍及kgdboe方式配置

    接博文<Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)>.上篇博文中,仅简单介绍使用串口的Kgbd的流程(kgdboc方式),本文将重点介绍KGDB调试Linux内核的原 ...

  7. 嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始化

    2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: ] = {,,,,,,,,}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a ...

  8. OMAP4之DSP核(Tesla)软件开发学习(二)Linux内核驱动支持OMAP4 DSP核

    注:必须是Linux/arm 3.0以上内核才支持RPMSG,在此使用的是.config - Linux/arm 3.0.31 Kernel Configuration.(soure code fro ...

  9. linux内核驱动学习指南

    1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动

随机推荐

  1. Qt出现常量有换行符的错误的解决方法

    可以使用 QString::fromLocal8Bit 来将本地字符编码转换为 Unicode 形式的 QString.

  2. hibernate简单的增删改查

    获取当前线程的session protected Session getSession() { return sessionFactory.getCurrentSession(); } 增加:save ...

  3. DHCP底层参考

    [原创翻译,水平有限] ISC DHCP支持802.1的以太网帧,令牌环和FDDI等网络.为了桥接物理层和DHCP层,它还必须实现IP和UDP协议帧. 这源于UNIX BSD socket 对未配置接 ...

  4. win10下安装Django

    Django的核心(1.4+)可以运行在从2.5到2.7之间的任何Python版本. 我的电脑是操作系统是window10 ,内存是4G. 1.下载django 官网地址:https://www.dj ...

  5. 难以记住的sql语句

    天,把这篇文章转移到这里,增强一下记忆,找起来也更方便. 导出: mysqldump -u username -p password -h hname dbname tblname > file ...

  6. 扩展kmp——原创

    扩展kmp                 LRH 所谓扩展kmp指的是与kmp相似的求辅助数组的原理,但是本身与kmp关系不大. 1.exkmp的用途:给定一个主串s和一个子串t,求出s中每一个后缀 ...

  7. 《JS权威指南学习总结--9.5 类和类型》

    内容要点: 介绍了三种用以检测任意对象的类的技术,instanceof运算符.constructor属性,以及构造函数的名字. 但每种技术都不甚完美,本节总结了鸭式辩型,这种编程哲学更加关注对象可以完 ...

  8. 【Python】@property的用法

    设想我们要给一个student()类的一个实例s,添加一个score的属性,比如: s.score=999999 这个值明显是不合理的,但是它却是可行的,怎么能改变这种情况?我们能想到的就是用类方法 ...

  9. hdu_4417_Super Mario(主席树)

    题目链接:hdu_4417_Super Mario 题意: 给你n个树,有m个询问,每个询问有一个区间和一个k,问你这个区间内不大于k的数有多少个. 题解: 考虑用主席树的话就比较裸,当然也可以用其他 ...

  10. 使用Angular构建单页面应用(SPA)

    什么是SPA?看下图就是SPA: 下面说正经的,个人理解SPA就是整个应用只有一个页面,所有的交互都在一个页面完成,不需要在页面之间跳转. 单页面的好处是更快的响应速度,更流畅的用户体验,甚至和桌面应 ...