zoukankan      html  css  js  c++  java
  • 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:

    [plain] view plain copy
     
     print?
    1. SHELL = /bin/sh             # 这个地方是指示使用的shell是sh  
    2. EXEC = ir_tree              # 最终生成的binary的名称  
    3. BUILD_DIR = build           # 这个子文件夹,此处也就是我们build文件夹  
    4.   
    5. all:                        # all在此处是终极目标,这个你应该知道的。一般我们make的时候,第一个目标作为终极目标  
    6.     @( cd ${BUILD_DIR}; make )  # 这句是进去build文件夹去执行那个makefile  
    7.                                 
    8. clean:                      # clean就不说了  
    9.     @echo 'start clean...'  
    10.     @($(RM) $(EXEC))  
    11.     @(cd ${BUILD_DIR}; make clean)  
    12.     @echo 'Finished!'  
    13.     @echo ''  

    现在进入build文件夹,看这个文件夹下面的makefile

    [plain] view plain copy
     
     print?
    1. SHELL = /bin/sh            # 同上  
    2.   
    3. INCLUDE_DIR :=             # include文件夹,一般我们在引用库的时候,需要将其头文件放在一个include中,然后自己的程序                           # 编译的时候需要包含这个include,例如-I$(<span style="font-family: SimHei;">INCLUDE_DIR</span><span style="font-family: SimHei;">)</span>  
    4. LIB_DIR := -lm             # 引入的库  
    5. EXEC = ../ir_tree          # 这是一个最终binary名称,这里是将这个可执行放在了上层文件夹中  
    6.   
    7. -include obj/subdir.mk     # 这个地方是include了一个子文件  
    8.                            # 这里子文件作用是,为了生成所有的.o文件(当然附带生成.d文件!),生成.o之后,才能回到这一                           # 层的makefile进行链接成最终的可执行的操作!具体操作我们稍后再看  
    9.   
    10. all:${EXEC}                # 好!这里是这个makefile的第一个目标。即终极目标,所有需要找<span style="font-family: SimHei;">${EXEC}的生成规则!</span>  
    11.   
    12. ${EXEC}: ${OBJS}           # <span style="font-family: SimHei;">${EXEC}的生成规则,注意这里我们没有看到$(OBJS),那是因为在</span><span style="font-family: SimHei;">obj/subdir.mk中!</span><span style="font-family: SimHei;">  
    13. </span> @echo ' Building target: $@ '  
    14.     gcc -o $@ $(OBJS) $(LIB_DIR)   # 这一句就是为了将所有的.o文件 + 引用的库 链接起来,生成最后的$@,也就是$(EX                                       # EC),也就是最后的binary!  
    15.     @echo 'Finished building target: $@'  
    16.     @echo ''  
    17.   
    18. clean:  
    19.     @echo 'start rm objs and deps ...'  
    20.     $(RM) $(OBJS)   
    21.     $(C_DEPS)  
    22.     @echo 'Finish rm objs and deps ...'  
    23.   
    24. .PHONY: all clean                      # 伪目标  
    25. .SECONDARY:  


    下面需要看看obj中的subdir.mk的内容了!这个是为了生成所有的.o文件。

    同时!请注意:当我们的一个.c或者.h被修改之后,需要重新编译!这一点非常重要!

    特别是.h被修改的时候,不能忘记重新编译( 当然,有些时候.h修改,我们不需要编译,这个先暂时不说,后面在讨论!其实,你使用一个make --touch就可以~ )

    [plain] view plain copy
     
     print?
    1. C_SRCS +=             # 所有的.c文件,当然你喜欢使用wildcard也是可的!  
    2. ../src/card.c         # $(<span style="font-family: SimHei;">wildcard ../src/*.c</span><span style="font-family: SimHei;">)</span>  
    3. ../src/index.c   
    4. ../src/node.c   
    5. ../src/rect.c   
    6. ../src/split_l.c   
    7. ../src/test.c  
    8.   
    9. OBJS +=              <span style="font-family: SimHei;"># 所有的.c文件,当然你喜欢使用wildcard也是可的!</span>  
    10. ./obj/card.o         # OBJS = $(patsubst %.c,%.o,$(wildcard ../src/*.c))  
    11. ./obj/index.o        # 但是你要将src文件目录改成obj的 <span style="font-family: SimHei;">OBJS := $(addprefix "./obj/",$(notdir $(OBJS)))</span>  
    12. ./obj/node.o   
    13. ./obj/rect.o   
    14. ./obj/split_l.o   
    15. ./obj/test.o  
    16.   
    17. C_DEPS +=           # deps  
    18. ./obj/card.d   
    19. ./obj/index.d   
    20. ./obj/node.d   
    21. ./obj/rect.d   
    22. ./obj/split_l.d   
    23. ./obj/test.d  
    24.   
    25. all: $(OBJS)        # 注意在这个subdir中,这个是终极目标,也就是所有的objs  
    26.   
    27. obj/%.o: ../src/%.c ./obj/%.d    #这里是o文件的依赖规则:注意是.c和.d同时成为依赖,.d文件中是一个目标的所有的依赖文                                 # 件,包括.c和.h文件,所有一旦.h被修改,这个地方也是可以识别的!  
    28.     @echo 'start building $< ...'  
    29.     gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)"   
    30. -MT"$(@:%.o=%.d)" -o "$@" "$<"  
    31.     @echo 'Finished building: $< '  
    32.     @echo ''  
    33.   
    34. -include $(C_DEPS) # 注意:这里将所有的.d文件引入!注意,第一次引入时候,没有.d文件,会寻找.d的生成规则,也就是下面                   # 的,这个.d又是依赖.c文件的,所有,一旦一个.c文件中多了一个头文件之类,又可以跟新.d,从而,执行                   # 上面的.o生成时候,就能够实时跟新  
    35.   
    36. ./obj/%.d: ../src/%.c   # 注意:这里为了生成.d  
    37.     @echo 'start building $(notdir $@)...'  
    38.     $(CC) $< $(INCLUDE) -MM -MD -o $@  


    好了,上面所有的都分析完了,然后可以make一下,.、ir_tree 看看效果吧~

    默认的图形界面是很简陋的界面,可以根据需要再安装GNOME或KDE桌面环境

    安装X图形界面
    #可查询哪些组件是否已经安装(可用来对照组件名称)

    yum grouplist

    #安装X图形界面系统

    yum list 列出所有可安装的软件包 可以通过 yum grouplist 来查看可能批量安装哪些列表 比如 #yum groupinstall "DNS Name Server" //安装 bind 及 bind-chroot 套件

    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

    1. 初次接触Linux的用户 

      /swap

      缺点:一旦有磁盘有任何问题,根目录将整体毁灭。

    2. 初级分配方式 
      /boot = 1G 

      /usr 
      /home 
      /var 
      /tmp 
      /swap = 2倍内存大小(如果内存较大,swap可适当减小)

      • 提示:/boot 要放在整块硬盘的最前面!(一般分区时,安装系统会主动将其置于最前方)

      • /boot 存放系统引导文件;(由于BIOS原因,boot只能在1024柱面前,否则会找不到。一般只需要100M,但是很多时候系统升级的时候,该目录下会保存旧引导文件)

      • / 根目录;
      • /usr 最庞大的目录,几乎所有应用程序、文件都在这里;
      • /home 用户主目录;
      • /var 某些大文件溢出区、cache存放、email存放;
      • /tmp 公共的临时文件;
      • /swap 虚拟内存交换区。

    几种文件类型

      1. ext2/ext3:Linux 适用的文件系统类型。由于ext3文件系统多了日志的记录, 对于系统的复原比较快速,因此建议务必要选择新的ext3不要用 ext2 了。
      2. LVM:用来弹性调整文件系统容量的一种机制, 可以让你的文件系统容量变大或变小而不改变原有的档案数据内容!
      3. RAID:利用Linux操作系统的特性,用软件仿真出磁盘阵列的功能!这东西很棒!不过目前暂时还用不到!
      4. swap:只用于操作系统虚拟内存置换,无法用于挂载。
      5. vfat:如果同时存在Windows/Linux操作系统,则可以选择vfat为虚拟内存置换区。

        • 提示:一般只需要选择ext3或者swap,ext3一般也叫标准模式。
  • 相关阅读:
    meta属性
    博客
    概念术语
    装饰器与生成器
    Linux基础
    线程
    网络编程之socket
    网络编程之网络基础部分

    内置函数(max,min,zip)及文件处理
  • 原文地址:https://www.cnblogs.com/klb561/p/9048662.html
Copyright © 2011-2022 走看看