热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LDD3源码分析之hello.c与Makefile模板

作者:刘昊昱博客:http:blog.csdn.netliuhaoyutz编译环境:Ubuntu10.10内核版本:2.6.32-38-generic-paeLDD3源码路径:exampl

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

编译环境:Ubuntu 10.10

内核版本:2.6.32-38-generic-pae

LDD3源码路径:examples/misc-modules/hello.c

 

一、hello.c文件分析

 

这个程序非常简单,它的目的是向我们展示Linux模块编程的架构,而Linux设备驱动程序的开发方法,就是利用了Linux模块编程。

首先来分析一下这个程序。对于任何一个模块程序,不论是简单如这个hello.c,还是复杂如usb模块的代码,我们要分析其源码,首先要找的是module_initmodule_exit两个宏。module_init宏的参数是模块被装载时要调用的函数,这是我们分析的起点。而module_exit宏的参数,则是模块被卸载时要调用的函数,这是完成最后清理工作的地方。所以对于hello.c,编译成模块后,装载模块时,hello_init函数就会被调用,打印了一句话“Hello,world”,卸载模块时,hello_exit函数就会执行,打印“Goodbye, cruel world”。

 

二、最简单的Makefile

 要把hello.c编译成内核模块,需要配置好内核源码树,还需要我们提供一个Makefile文件。下面我们来看Makefile的写法。

最简单的Makefile只需要一句话:

obj-m := hello.o

我们先来看一下整个编译过程,再解释这一句话是什么意思。如下图所示:

首先解释一下上图中各命令的作用:

1行,执行ls命令,可以看到当前目录下有hello.cMakefile两个文件。

3行,执行cat Makefile命令,可以看到Makefile的内容只有一句话obj-m := hello.o

5行,执行make -C /usr/src/linux-headers-2.6.32-38-generic-pae M=`pwd` modules命令,编译hello模块。注意,-C后跟的是内核源码树所在目录(根据自己的配置指定相应路径)M=后面是反引号(ESC按键下面)而不是单引号,表示把pwd命令执行的结果(即当前路径)赋值给M

13行,执行ls命令,显示编译后当前目录下的内容,可以看到,已经生成了hello.ko,即hello模块。

15行,执行dmesg命令,可以看到,没有任何信息输出。

16行,执行sudo insmod hello.ko命令,安装hello模块。

17行,再次执行dmesg命令,可以看到”Hello, world”信息,这就是刚才安装hello模块时,模块初始化函数hello_init函数打印的语句。

19行,执行sudo rmmod hello命令,从内核中删除hello模块。注意,指定模块名时用的是hello,而不是hello.ko

20行,再次执行dmesg命令,可以看到,除了刚才安装模块时hello_init打印的”Hello, world”,又多了一条语句”Goodbye, cruel world”,这句话就是模块卸载函数hello_exit函数打印的。

至此,可以看到hello模块的编译,安装,卸载都成功了。

上面介绍的这个最简单的Makefile只有一句话” obj-m := hello.o”,显然,按照标准的Makefile语法,这个Makefile应该无法完成任何编译工作才对,但是从实际编译过程可以看出,通过这个Makefile确实把hello模块编译出来了。它是怎么做到的呢?正如LDD3上所说的“问题的答案当然是内核构造系统处理了其余的问题”。也就是说,我们要把这个Makefile放在Linux内核编译系统这个大环境下使用,这样才能编译出hello模块。如果没有Linux内核编译系统,只有这一个Makefile文件,肯定是无法完成编译工作的。

那么我们怎么把这个Makefile放在Linux内核编译系统这个大环境下来使用呢?答案就是上图第5行执行的命令make -C /usr/src/linux-headers-2.6.32-38-generic-pae M=`pwd` modules。这里make命令的”-C”选项,指定了内核源码树所在的路径,其中保存了Linux内核源码顶层Makefile,这个顶层MakefileLinux内核编译系统的入口点。”M=”选项,表明在构造modules目标之前,返回到当前目录,即把要生成的modules目标放在当前目录下。

现在可以来看我们的Makefile中的这一句话了” obj-m := hello.o”,这句话表明,当执行make modules命令时(这里忽略”-C””M=”选项),要求从hello.o文件来生成一个目标模块,该目标模块名为hello.ko

 

三、功能完整的Makefile

 我们前面用的Makefile很简单,功能比较单一,LDD3为我们提供了一个比较好的Makefile模板,简单修改即可拿来编译自己的模块,如下图所示:

使用这个Makefile,编译hello模块的过程显示如下:

下面我们分析一下这个加强版的Makefile的内容。注意,这里需要你对Makefile的基本语法有一定了解,如果对Makefile的基本语法不了解,请先学习相关知识。

7行,ifeq ($(KERNELRELEASE),),判断KERNELRELEASE变量是否为空,如果为空则继续向下到11行执行。如果不为空,即已经定义了KERNELRELEASE,说明是从内核编译系统调用的,则跳到第26行执行。其效果就和我们前面只有一句话的最简单的Makefile相同了。

11行,KERNELDIR ?= /lib/modules/$(shell uname -r)/build,给KERNELDIR变量赋值,该变量保存内核源码树所在的路径。注意,linux各发行版本会把内核源码树的一个符号链接放在/lib/modules/$(shell uname -r)/build,对于我的系统,这个符号链接指向的实际就是/usr/src/linux-headers-2.6.32-38-generic-pae

13行,PWD := $(shell pwd),给PWD变量赋值,该变量保存当前路径。

16行,$(MAKE) -C $(KERNELDIR) M=$(PWD) modules,如果在命令行执行make modules命令,则相应会执行这条命令编译模块。

19行,$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install,如果在命令行执行make modules install命令,则相应会执行这条命令安装模块。

22行,rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions,如果在命令行执行make clean命令,则相应会执行这条命令删除指定文件。

28行,如果第7行的判断不成立,则会执行这条指令。与前面我们介绍的最简单的Makefile效果相同。

这时我来提出一个问题:

按照上面对Makefile的分析,当我们在命令行执行make命令时,因为KERNELRELEASE变量为空,所以会执行给变量KERNELDIRPWD的赋值,然后执行第一个目标modules对应的命令,即第16$(MAKE) -C $(KERNELDIR) M=$(PWD) modules,编译模块。但现在并没有指定obj-m := hello.o,按照我们对第一个最简单的Makefile的分析,这个变量obj-m := hello.o是非常重要的,它指定了当执行make modules命令时(这里忽略”-C””M=”选项),要求从hello.o文件来生成一个目标模块,该目标模块名为hello.ko。因为没有指定obj-m := hello.o,那么编译系统怎么知道要编译的目标模块是什么,怎么编译这个目标模块呢?

答案在LDD3上也有列出了,大家可以理解一下LDD3(中文版)第30页上的第一段文字。概括一下:我们在命令行执行make时,这个makefile文件将会被调用两次,第一次调用时,因为没有设置KERNELRELEASE,所以会设置KERNELDIRPWD变量,然后执行modules目标对应的命令,即第16$(MAKE) -C $(KERNELDIR) M=$(PWD) modules。执行这个命令时,”-C”选项决定了首先会创建内核构造系统(其中包括创建KERNELRELEASE变量),然后再按照该makefile执行,这次执行,因为已经定义了KERNELRELEASE变量,所以会直接跳到26行对应的else语句执行,并在第28行设置了obj-m := hello.o。后面的过程就和我们前面介绍的最简单的只有一句话的Makefile一样了。

 

四、总结

本文首先通过分析LDD3自带的hello.c程序,介绍了Linux模块编程的概念,Linux设备驱动程序就是建立在Linux模块编程的基础上。本文的另一个重点是介绍了Linux设备驱动程序makefile的写法及工作原理,以后我们的驱动程序,就可以使用这里介绍的makefile模板来完成编译工作。


推荐阅读
  • linux qt打开常用文件格式,设置Linux Qt文件默认打开方式为QtCreator
    Linux自定义文件打开方式也可参照文本抱歉,本文前段时间写的ubuntu下的Qt工程文件默认打开方式是不好用的,因为其他的文本文件也会受到影响,强迫症患者,每次打开Qt工程都是先 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • Ubuntu安装常用软件详细步骤
    目录1.GoogleChrome浏览器2.搜狗拼音输入法3.Pycharm4.Clion5.其他软件1.GoogleChrome浏览器通过直接下载安装GoogleChro ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文介绍了在Ubuntu下制作deb安装包及离线安装包的方法,通过备份/var/cache/apt/archives文件夹中的安装包,并建立包列表及依赖信息文件,添加本地源,更新源列表,可以在没有网络的情况下更新系统。同时提供了命令示例和资源下载链接。 ... [详细]
  • 本文介绍了5个基本Linux命令行工具的现代化替代品,包括du、top和ncdu。这些替代品在功能上进行了改进,提高了可用性,并且适用于现代化系统。其中,ncdu是du的替代品,它提供了与du类似的结果,但在一个基于curses的交互式界面中,重点关注占用磁盘空间较多的目录。 ... [详细]
  • 本文介绍了在Android Studio中使用命令行build gradle的方法,并解决了一些常见问题,包括手动配置gradle环境变量和解决External Native Build Issues的方法。同时提供了相关参考文章链接。 ... [详细]
  • 本文介绍了在Ubuntu系统中清理残余配置文件和无用内容的方法,包括清理残余配置文件、清理下载缓存包、清理不再需要的包、清理无用的语言文件和清理无用的翻译内容。通过这些清理操作可以节省硬盘空间,提高系统的运行效率。 ... [详细]
  • 本文介绍了在Win10上安装WinPythonHadoop的详细步骤,包括安装Python环境、安装JDK8、安装pyspark、安装Hadoop和Spark、设置环境变量、下载winutils.exe等。同时提醒注意Hadoop版本与pyspark版本的一致性,并建议重启电脑以确保安装成功。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文讨论了在Linux系统中,使用chown命令将django项目目录下的static目录的拥有者从root改为eureka的问题。作者尝试了多种命令,包括chown和sudo chown等,但都没有成功修改拥有者。文章提供了相关目录的权限信息,并补充了项目所在磁盘和操作系统的信息。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
author-avatar
許宸瑜_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有