Linux 内核模块编译 Makefile
驱动编译分为静态编译和动态编译;静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块。
而动态编译又分为两种:
a -- 内部编译
在内核源码目录内编译
b -- 外部编译
在内核源码的目录外编译
二、具体编译过程分析
注:本次编译是外部编译,使用的内核源码是Ubuntu 的源代码,而非开发板所用linux 3.14内核源码,运行平台为X86。
对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以完成大部分驱动的编译,使用时只需要修改一下要编译生成的驱动名称即可。只需修改obj-m的值。
ifneq ($(KERNELRELEASE),)
obj-m:=hello.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.symvers *.cmd *.cmd.o
endif
|
1、makefile 中的变量
先说明以下makefile中一些变量意义:
(1)KERNELRELEASE 在linux内核源代码中的顶层makefile中有定义
(2)shell pwd 取得当前工作路径
(3)shell uname -r 取得当前内核的版本号
(4)KDIR 当前内核的源代码目录。
关于linux源码的目录有两个,分别为
"/lib/modules/$(shell uname -r)/build"
"/usr/src/linux-header-$(shell uname -r)/"
但如果编译过内核就会知道,usr目录下那个源代码一般是我们自己下载后解压的,而lib目录下的则是在编译时自动copy过去的,两者的文件结构完全一样,因此有时也将内核源码目录设置成/usr/src/linux-header-$(shell uname -r)/。关于内核源码目录可以根据自己的存放位置进行修改。
(5)make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
这就是编译模块了:
a -- 首先改变目录到-C选项指定的位置(即内核源代码目录),其中保存有内核的顶层makefile;
b -- M=选项让该makefile在构造modules目标之前返回到模块源代码目录;然后,modueles目标指向obj-m变量中设定的模块;在上面的例子中,我们将该变量设置成了hello.o。
2、make 的的执行步骤
a -- 第一次进来的时候,宏“KERNELRELEASE”未定义,因此进入 else;
b -- 记录内核路径,记录当前路径;
由于make 后面没有目标,所以make会在Makefile中的第一个不是以.开头的目标作为默认的目标执行。默认执行all这个规则
c -- make -C $(KDIR) M=$(PWD) modules
-C 进入到内核的目录执行Makefile ,在执行的时候KERNELRELEASE就会被赋值,M=$(PWD)表示返回当前目录,再次执行makefile,modules 编译成模块的意思
所以这里实际运行的是
make -C /lib/modules/2.6.13-study/build M=/home/fs/code/1/module/hello/ modules
d -- 再次执行该makefile,KERNELRELEASE就有值了,就会执行obj-m:=hello.o
obj-m:表示把hello.o 和其他的目标文件链接成hello.ko模块文件,编译的时候还要先把hello.c编译成hello.o文件
可以看出make在这里一共调用了3次
1)-- make
2)-- linux内核源码树的顶层makedile调用,产生。o文件
3)-- linux内核源码树makefile调用,把.o文件链接成ko文件
3、编译多文件
若有多个源文件,则采用如下方法:
obj-m := hello.o
hello-objs := file1.o file2.o file3.o
三、内部编译简单说明
如果把hello模块移动到内核源代码中。例如放到/usr/src/linux/driver/中, KERNELRELEASE就有定义了。
在/usr/src/linux/Makefile中有KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)。
这时候,hello模块也不再是单独用make编译,而是在内核中用make modules进行编译,此时驱动模块便和内核编译在一起。
顶层:
然后src中:是所有的源程序以及头文件( 我这里是使用自己的IR树的代码作为实验 )
而build文件夹是为了编译使用的!下面有:
obj文件夹里面放的是编译过程中的.o和.d文件,还有一个subdir.mk的子文件,
用于指示怎么生成.o
obj中:
下面我们从顶层开始慢慢分析:
*******温馨提示:下面的注释是为了方便处理,写在每一条语句后面,其实这样的风格是不好的,所以,如果
你使用了这个makefile,请将注释换行...或者去掉,否则可能编译异常!谢谢记住!
*******
最外层的makefile:
- SHELL = /bin/sh # 这个地方是指示使用的shell是sh
- EXEC = ir_tree # 最终生成的binary的名称
- BUILD_DIR = build # 这个子文件夹,此处也就是我们build文件夹
- all: # all在此处是终极目标,这个你应该知道的。一般我们make的时候,第一个目标作为终极目标
- @( cd ${BUILD_DIR}; make ) # 这句是进去build文件夹去执行那个makefile
- clean: # clean就不说了
- @echo 'start clean...'
- @($(RM) $(EXEC))
- @(cd ${BUILD_DIR}; make clean)
- @echo 'Finished!'
- @echo ''
现在进入build文件夹,看这个文件夹下面的makefile
- SHELL = /bin/sh # 同上
- INCLUDE_DIR := # include文件夹,一般我们在引用库的时候,需要将其头文件放在一个include中,然后自己的程序 # 编译的时候需要包含这个include,例如-I$(<span style="font-family: SimHei;">INCLUDE_DIR</span><span style="font-family: SimHei;">)</span>
- LIB_DIR := -lm # 引入的库
- EXEC = ../ir_tree # 这是一个最终binary名称,这里是将这个可执行放在了上层文件夹中
- -include obj/subdir.mk # 这个地方是include了一个子文件
- # 这里子文件作用是,为了生成所有的.o文件(当然附带生成.d文件!),生成.o之后,才能回到这一 # 层的makefile进行链接成最终的可执行的操作!具体操作我们稍后再看
- all:${EXEC} # 好!这里是这个makefile的第一个目标。即终极目标,所有需要找<span style="font-family: SimHei;">${EXEC}的生成规则!</span>
- ${EXEC}: ${OBJS} # <span style="font-family: SimHei;">${EXEC}的生成规则,注意这里我们没有看到$(OBJS),那是因为在</span><span style="font-family: SimHei;">obj/subdir.mk中!</span><span style="font-family: SimHei;">
- </span> @echo ' Building target: $@ '
- gcc -o $@ $(OBJS) $(LIB_DIR) # 这一句就是为了将所有的.o文件 + 引用的库 链接起来,生成最后的$@,也就是$(EX # EC),也就是最后的binary!
- @echo 'Finished building target: $@'
- @echo ''
- clean:
- @echo 'start rm objs and deps ...'
- $(RM) $(OBJS) \
- $(C_DEPS)
- @echo 'Finish rm objs and deps ...'
- .PHONY: all clean # 伪目标
- .SECONDARY:
下面需要看看obj中的subdir.mk的内容了!这个是为了生成所有的.o文件。
同时!请注意:当我们的一个.c或者.h被修改之后,需要重新编译!这一点非常重要!
特别是.h被修改的时候,不能忘记重新编译( 当然,有些时候.h修改,我们不需要编译,这个先暂时不说,后面在讨论!其实,你使用一个make --touch就可以~ )
- C_SRCS += \ # 所有的.c文件,当然你喜欢使用wildcard也是可的!
- ../src/card.c \ # $(<span style="font-family: SimHei;">wildcard ../src/*.c</span><span style="font-family: SimHei;">)</span>
- ../src/index.c \
- ../src/node.c \
- ../src/rect.c \
- ../src/split_l.c \
- ../src/test.c
- OBJS += \ <span style="font-family: SimHei;"># 所有的.c文件,当然你喜欢使用wildcard也是可的!</span>
- ./obj/card.o \ # OBJS = $(patsubst %.c,%.o,$(wildcard ../src/*.c))
- ./obj/index.o \ # 但是你要将src文件目录改成obj的 <span style="font-family: SimHei;">OBJS := $(addprefix "./obj/",$(notdir $(OBJS)))</span>
- ./obj/node.o \
- ./obj/rect.o \
- ./obj/split_l.o \
- ./obj/test.o
- C_DEPS += \ # deps
- ./obj/card.d \
- ./obj/index.d \
- ./obj/node.d \
- ./obj/rect.d \
- ./obj/split_l.d \
- ./obj/test.d
- all: $(OBJS) # 注意在这个subdir中,这个是终极目标,也就是所有的objs
- obj/%.o: ../src/%.c ./obj/%.d #这里是o文件的依赖规则:注意是.c和.d同时成为依赖,.d文件中是一个目标的所有的依赖文 # 件,包括.c和.h文件,所有一旦.h被修改,这个地方也是可以识别的!
- @echo 'start building $< ...'
- gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" \
- -MT"$(@:%.o=%.d)" -o "$@" "$<"
- @echo 'Finished building: $< '
- @echo ''
- -include $(C_DEPS) # 注意:这里将所有的.d文件引入!注意,第一次引入时候,没有.d文件,会寻找.d的生成规则,也就是下面 # 的,这个.d又是依赖.c文件的,所有,一旦一个.c文件中多了一个头文件之类,又可以跟新.d,从而,执行 # 上面的.o生成时候,就能够实时跟新
- ./obj/%.d: ../src/%.c # 注意:这里为了生成.d
- @echo 'start building $(notdir $@)...'
- $(CC) $< $(INCLUDE) -MM -MD -o $@
好了,上面所有的都分析完了,然后可以make一下,.、ir_tree 看看效果吧~
默认的图形界面是很简陋的界面,可以根据需要再安装GNOME或KDE桌面环境
安装X图形界面
#可查询哪些组件是否已经安装(可用来对照组件名称)
yum grouplist
#安装X图形界面系统
yum groupinstall "X Window System" -y
#安装GNOME桌面环境
yum groupinstall "GNOME Desktop Environment" -y
#安装KDE桌面环境
yum groupinstall "KDE (K Desktop Environment)"
卸载
卸载GNOME桌面环境
yum groupremove GNOME Desktop Environment'
卸载KDE桌面环境
yum groupremove 'K Desktop Environment'
启动X图形界面的方法
1、startx
2、设置开机自动启动,修改/etc/inittab
id:3:initdefault: ------> id:5:initdefault:
3、init 5
默认桌面环境选择
一、设置GNOME或者KDE为默认的启动桌面环境
方法1:修改/etc/sysconfig/desktop,根据需要将“DESKTOP”后面的参数设置为KDE或GNOME。
方法2:在当前用户目录下建立“.xinitrc”这个文件,文件的内容就一行startkde或gnome-session。
二、GNOME和KDE的切换
1、如果需要切换到GNOME:
#switchdesk gnome
2、如果需要切换到KDE:
#switchdesk kde
2、如果需要切换到KDE:
#startkde
图形界面与字符界面的切换
在LINUX中是有多控制台的,其中前6个是字符界面,第七个是图形界面。
如果你需要切换到字符界面,可以使用CTRL+ALT+Fn来实现,其中Fn是F1-F6中的任何一个,
当然如果你是在字符界面之间互相切换就没必要CTRL了,直接ALT Fn
5)如果在OS启动时需要启动图形化界面,则需要编辑 /etc/inittab 文件的 level 3 换成 level 5
# vi /etc/inittab
id:3:initdefault:
6)可以在 level 3 和 level 5 之间切换
# init 3
# init 5
初次接触Linux的用户
/
/swap缺点:一旦有磁盘有任何问题,根目录将整体毁灭。
初级分配方式
/boot = 1G
/
/usr
/home
/var
/tmp
/swap = 2倍内存大小(如果内存较大,swap可适当减小)提示:/boot 要放在整块硬盘的最前面!(一般分区时,安装系统会主动将其置于最前方)
/boot 存放系统引导文件;(由于BIOS原因,boot只能在1024柱面前,否则会找不到。一般只需要100M,但是很多时候系统升级的时候,该目录下会保存旧引导文件)
- / 根目录;
- /usr 最庞大的目录,几乎所有应用程序、文件都在这里;
- /home 用户主目录;
- /var 某些大文件溢出区、cache存放、email存放;
- /tmp 公共的临时文件;
- /swap 虚拟内存交换区。
几种文件类型
- ext2/ext3:Linux 适用的文件系统类型。由于ext3文件系统多了日志的记录, 对于系统的复原比较快速,因此建议务必要选择新的ext3不要用 ext2 了。
- LVM:用来弹性调整文件系统容量的一种机制, 可以让你的文件系统容量变大或变小而不改变原有的档案数据内容!
- RAID:利用Linux操作系统的特性,用软件仿真出磁盘阵列的功能!这东西很棒!不过目前暂时还用不到!
- swap:只用于操作系统虚拟内存置换,无法用于挂载。
vfat:如果同时存在Windows/Linux操作系统,则可以选择vfat为虚拟内存置换区。
- 提示:一般只需要选择ext3或者swap,ext3一般也叫标准模式。
Linux 内核模块编译 Makefile的更多相关文章
- linux内核模块编译makefile
linux内核可加载模块的makefile 在开发linux内核驱动时,免不了要接触到makefile的编写和修改,尽管网上的makefile模板一大堆,做一些简单的修改就能用到自己的项目上,但是,对 ...
- Linux内核模块Makefile学习
在<Linux设备驱动程序>一书中读到的内核模块编译Makefile,不是非常理解,在查询很多资料后,在这里做个总结. 书中Makefile代码: ifneq ($(KERNELRELEA ...
- [ARM-LInux开发]linux设备驱动makefile入门解析
以下内容仅作参考,能力有限,如有错误还请纠正.对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以完成大部分驱动的编译,使用时只需要修改一下要编 ...
- 关于linux内核模块Makefile的解析
转载:http://www.embeddedlinux.org.cn/html/yingjianqudong/201403/23-2820.html Linux内核是一种单体内核,但是通过动态加载模块 ...
- linux/module.h: No such file or directory 内核模块编译过程
1.缺少Linux kernel头文件 To install just the headers in Ubuntu: sudo apt-get install linux-headers-$(unam ...
- Linux环境下使用VSCode编译makefile文件的注意事项
Linux环境下使用VSCode编译makefile文件的注意事项 首先安装C/C++的两个依赖 在debug,launch会自动的生成下方的launch.json launch.json { // ...
- linux如何编译安装新内核支持NTFS文件系统?(以redhat7.2x64为例)
内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性.Linux作为一个自由软件,在广大爱好者的支持下,内核版本不断更新.新的内核修订了旧内核 ...
- 5.linux内核模块基础,内核模块学习
linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...
- Linux内核模块简介
一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...
随机推荐
- 解决unbuntu14.04上的eclipse自动退出的问题
新安装的ubuntu14.04版,把以前12.04上正常使用的eclipse拷贝到14.04上后,启动eclipse后,输入代码时出现点"."提示符就会自动重启. jdk是1.7. ...
- TCP 的那些事儿(上)(转)
本文转载自陈皓博文TCP 的那些事儿(上). TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面.所以学习TCP本身是个比较痛苦的过程,但对于学习的过程却能让人有很多 ...
- Dijkstra算法 c语言实现
Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法能得出最短路径的最优 ...
- EBS R12安装升级(FRESH)(五)
7.4.5 用DBUA升级 Database Upgrade Assistant提供图形界面进行升级. 将zysong.ttf复制到 /u01/oracle/TEST/db/tech_st/11.2. ...
- Android高通平台调试Camera驱动全纪录
项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...
- UITabbar的一些常规用法(总结)
往往系统自带的UITabbar 不能满足我们的样式或者颜色设计,所以需要调整UITabbar. 1.自定义UITabbar,也是我学到的第一种方式(简单暴力). 先记录一下思路: 首先,隐藏系统自带的 ...
- 初探linux子系统集之led子系统(二)
巴西世界杯,德国7比1东道主,那个惨不忍睹啊,早上起来看新闻,第一眼看到7:1还以为点球也能踢成这样,后来想想,点球对多嘛6比1啊,接着就是各种新闻铺天盖地的来了.其实失败并没有什么,人生若是能够成功 ...
- javac编译同一个包内的java文件
问题描述:包a.b.c里有d.java e.java f.java三个文件,其中d中包含main. 错误: 第一种:javac d.java 报错:里面用到其他类,找不到 第二种:javac *.ja ...
- 数据准备<2>:数据质量检查-实战篇
上一篇文章:<数据质量检查-理论篇>主要介绍了数据质量检查的基本思路与方法,本文作为补充,从Python实战角度,提供具体的实现方法. 承接上文,仍然从重复值检查.缺失值检查.数据倾斜问题 ...
- Java IO学习--(二)文件
在Java应用程序中,文件是一种常用的数据源或者存储数据的媒介.所以这一小节将会对Java中文件的使用做一个简短的概述.这篇文章不会对每一个技术细节都做出解释,而是会针对文件存取的方法提供给你一些必要 ...