驱动入门——Makefile分析

来源:互联网 发布:淘宝优惠券群怎么弄 编辑:程序博客网 时间:2024/06/07 15:46
Makefile:
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build# 开发板的linux内核的源码树目录KERN_DIR = /root/driver/kernelobj-m+= module_test.oall:make -C $(KERN_DIR) M=`pwd` modules cp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: cleanclean:make -C $(KERN_DIR) M=`pwd` modules clean

(1)KERN_DIR,变量的值就是我们用来编译这个模块的内核源码树的目录

(2)obj-m += module_test.o,这一行就表示我们要将module_test.c文件编译成一个模块

    如果是obj-y += module_test.o就表示我们要将module_test.c文件静态编译链接进zImage中

(3)make -C $(KERN_DIR) M=`pwd` modules 这个命令用来实际编译模块,工作原理就是:利用make -C进入到  我们指定的内核源码树目录下,然后在源码目录树下借用内核源码中定义的模块编译规则去编译这个模块,编    译成后把生成的文件还拷贝到当前目录下,完成编译。

(4).PHONY

PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能。
如果编写一个规则,并不产生目标文件,则其命令在每次make 该目标时都执行。例如:
  clean:
  rm *.o temp
因为"rm"命令并不产生"clean"文件,则每次执行"make clean"的时候,该命令都会执行。如果目录中出现了"clean"文件,则规则失效了:没有依赖文件,文件"clean"始终是最新的,命令永远不会 执行;为避免这个问题,可使用".PHONY"指明该目标。如:
  .PHONY : clean
  这样执行"make clean"会无视"clean"文件存在与否。

已知phony 目标并非是由其它文件生成的实际文件,make 会跳过隐含规则搜索。这就是声明phony 目标会改善性能的原因,即使你并不担心实际文件存在与否。
  完整的例子如下:
  .PHONY : clean
  clean :
  rm *.o temp

(5)make clean ,用来清除编译痕迹

总结】:

模块的makefile非常简单,本身并不能完成模块的编译,而是通过make -C进入到内核源码树下借用内核源码的体系来完成模块的编译链接的。这个Makefile本身是非常模式化的,3和4部分是永远不用动的,只有1和2需要动。1是内核源码树的目录,你必须根据自己的编译环境进行配置


Module_test.c

#include <linux/module.h>// module_init  module_exit#include <linux/init.h>// __init   __exit// 模块安装函数static int __init chrdev_init(void){printk(KERN_INFO "chrdev_init helloworld init\n");//printk("<7>" "chrdev_init helloworld init\n");//printk("<7> chrdev_init helloworld init\n");return 0;}// 模块下载函数static void __exit chrdev_exit(void){printk(KERN_INFO "chrdev_exit helloworld exit\n");}module_init(chrdev_init);module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息MODULE_LICENSE("GPL");// 描述模块的许可证MODULE_AUTHOR("aston");// 描述模块的作者MODULE_DESCRIPTION("module test");// 描述模块的介绍信息MODULE_ALIAS("alias xxx");// 描述模块的别名信息
1>、函数修饰符
(1)__init

本质:宏定义,内核源代码定义:#define __init xxxx。

作用:将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被放入.text段中)。

整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。

(2)__exit同上

2>、printk函数详解

(1)printk在内核源码中用来打印信息的函数,用法和printf非常相似。

(2)printk和printf最大的差别:printf是C库函数,是在应用层编程中使用的,不能在linux内核源代码中使用;printk是linux内核源代码中自己封装出来的一个打印函数,是内核源码中的一个普通函数,只能在内核源码范围内使用,不能在应用编程中使用。

(3)printk相比printf来说还多了个:打印级别的设置。【值为0-7,值越小打印要求越高】

应用程序中的调试信息要么全部打开要么全部关闭,一般用条件编译来实现(DEBUG宏),但是在内核中,因为内核非常庞大,打印信息非常多,为了方便调试,用printk的打印级别来控制printk打印的这条信息是否在终端上显示的。


Reference:

《朱老师物联网大讲堂》——驱动开发





原创粉丝点击