hello world及内核模块 && 生成.ko的驱动改生成.o

来源:互联网 发布:国际认证大数据证书 编辑:程序博客网 时间:2024/06/07 22:58

(1)用户程序,实例

启动终端,首先用VI编写一个C程序:vi hello.c

#include  <stdio.h>int main(){         printf("hello world!!!/n");         return 0;}

       接着用GCC进行编译:gcc -o hello hello.c生成hello,最后运行该程序:./hello,在终端上你会看到:hello world!!!

        上面是在操作系统基础上进行的用户应用程序的开发。然而对于linux驱动程序的开发是绝然不同的,因为驱动程序的开发是运行在内核空间的,而应用程序是运行在用户空间的。也就是说,程序不能直接通过指针,把用户空间的数据地址传递给内核。要想在应用程序和驱动程序之间传递数据(指针),就需要经过转换。把用户态“看到”的空间地址转换成内核态可访问的地址。
        Linux内核把驱动程序划分为3种类型:字符设备、块设备和网络设备。应用程序对于字符设备的每一个I/O操作,都会直接传递给系统内核对应的驱动程序;而应用程序对于块设备的操作,要经过系统的缓冲区管理,间接传递给驱动程序处理。块设备的这种管理方式是为存储提供优化的;而字符设备的管理方式是为操作提供优化的。

(2) 内核程序,驱动模块实例

       在linux系统中,驱动程序都做成模块的形式,也就是module。简单的说,一个模块提供一个功能,这些模块是可以按照需要随时装入内核空间和从内核空间卸载的。因此,内核模块是为了给内核动态增减功能而设计的,并不仅仅是限于驱动程序。当用户输入命令“insmod 模块文件名”加载内核模块时,系统会检测此模块能否被加载,如果能被加载,内核调用模块的初始化函数module_init()(2.4内核为init_module())。当用户输入命令“rmmod 模块文件名”卸载内核模块时,此时,系统会检测此模块是否能被卸载,内核将调用模块清除函数module_exit()(2.4内核为cleanup_module())。

        内核模块驱动以my.c实例来说明,它没有main函数:

#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/jiffies.h>#include <linux/timer.h>static void __exit accel_sens_exit(void){printk("module out kernel_test_exit \r\n");}static int __init accel_sens_init(void){printk("module in kernel_test_init \r\n");return 0;}module_init(accel_sens_init);module_exit(accel_sens_exit);MODULE_DESCRIPTION("QL_ULPSH Accel  driver");MODULE_AUTHOR("QuickLogic Inc");MODULE_LICENSE("GPL v2");

       另外,编写一个Makefile,如下:    

obj-m:=my.oKDIR:=/lib/modules/$(shell uname -r)/buildMAKE:=makedefault:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modulesclean:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

以上经过验证。执行make即可编译,生成若干项文件。注意,如果执行时提示Nothing to be done for `modules',则是形如$(MAKE) -C $(KERNELDIR) M=$(PWD) modules的make命令之前要有一个Tab键,而不是空格。
         在ubuntu终端中执行加载一下insmod my.ko     dmesg|grep module 看到模块初始化时候的打印(用lsmod也可以看模块),卸载模块rmmod my  dmesg|grep module  看模块卸载时候的打印。正确结果如下:

       在PC机上提示ERROR: Removing 'my': Device or resource busy,可能原因是Ubuntu的内核选项默认没有勾上Module unloading。这样的话,我们可以在嵌入式平台上做这个实验,需要注意的是编译器也要是交叉编译器,且实验的目标板系统与编译器版本要一致。
        比如在MTK上手机平台代码中放置这个实验代码,PC.MK中添加目录,makefile直接用

# Each configuration option enables a list of files.obj-m+= helloworld.o
让文件和内核一起编译,再将生成的ko push到机子的system/bin。执行insmod helloworld.ko,用dmesg即可看到对应的Log。用lsmod可确认已加载的模块信息

再执行rmmod helloworld,即可查看退出信息。


(3)对于内核模块,我们会见到很多种申明,有什么差别呢?

#define pure_initcall(fn)  __define_initcall("0",fn,1)#define core_initcall(fn)  __define_initcall("1",fn,1)#define core_initcall_sync(fn)  __define_initcall("1s",fn,1s)#define postcore_initcall(fn)  __define_initcall("2",fn,2)#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)#define arch_initcall(fn)  __define_initcall("3",fn,3)#define arch_initcall_sync(fn)  __define_initcall("3s",fn,3s)#define subsys_initcall(fn)  __define_initcall("4",fn,4)#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)#define fs_initcall(fn)   __define_initcall("5",fn,5)#define fs_initcall_sync(fn)  __define_initcall("5s",fn,5s)#define rootfs_initcall(fn)  __define_initcall("rootfs",fn,rootfs)#define device_initcall(fn)  __define_initcall("6",fn,6)#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)#define late_initcall(fn)  __define_initcall("7",fn,7)#define late_initcall_sync(fn)  __define_initcall("7s",fn,7s)#define module_init(x) __initcall(x);#define __initcall(fn) device_initcall(fn)
值越小,越早调用。所以module_init还在late_initcall前面调用

====================================生成.ko的驱动改生成.o==================================

       调试驱动时碰到一个现象:要在A.o的驱动中调用B.ko的驱动中的变量c,结果编译都编不过,提示C没有定义。究其原因,在编译状态,B驱动中的C变量对A来说,是不存在的,所以才会有这个提示。

       如何解决?把B.ko也改成生成.o,这样AB就能互相访问了。把B驱动中的makefile的obj-m换成obj-y即可,编译开机会提示在某驱动注册异常导致不停重启。修改makefile,使先加载的o放在后加载o的前面即可解决。


参考原文:http://blog.csdn.net/sabalol/article/details/2076610

参考原文:http://blank-dic.blog.163.com/blog/static/310186222011221112425351/

参考原文:http://hi.baidu.com/serial_story/blog/item/90c81734e58009335ab5f562.html

参考原文:http://blog.csdn.net/elfylin/article/details/5908265 

原创粉丝点击