• 技术文章 >php教程 >PHP开发

    Linux内核配置、编译及Makefile简述

    高洛峰高洛峰2016-12-01 15:41:49原创794
      Hi,大家好!我是CrazyCatJack。最近在学习Linux内核的配置、编译及Makefile文件。今天总结一下学习成果,分享给大家^_^

    1.解压缩打补丁

      首先是解压缩你获取到的Linux内核。这里我用到的是linux.2.22.6版本的内核。在Linux下命令行通过tar xjf linux.2.22.6.tar.bz2解压内核。然后,如果你需要对这个内核打补丁的话,用patch命令:patch -px <../linux.2.22.6.patch。这里的px指的是忽略掉补丁文件中描述的第几个斜杠。也就是忽略前x个目录。

    --- linux-2.6.22.6/arch/arm/configs/s3c2410_defconfig+++ linux-2.6.22.6_jz2440/arch/arm/configs/s3c2410_defconfig

      如果你此刻就在内核的根目录下,即linux-2.6.22.6下,也就是说打补丁需要忽略掉一个斜杠的目录。那么打补丁的命令就是patch -p1 <../linux.2.22.6.patch。

    2.配置内核

      现在补丁已经打好了,接下来就是配置内核了。这里配置有3种方法:

      1>直接进行make menuconfig。这是最麻烦的一种方法,所有的配置都需要你来操作。

      2>在默认配置上自己修改,也就是修改defconfig文件。使用 find -name "*defconfig*"查找你的架构对应的默认配置文件。我是在arch/arm/configs找到自己板子的默认配置文件。执行defconfig文件: make XXX_defconfig。XXX是你具体使用的板子型号。执行这一操作后,结果保存在.config文件。然后再执行make menuconfig命令。这时的配置就是在默认配置上稍加修改就可以了。

      3>使用厂家的配置文件。如果你的硬件有厂家提供的config文件那是最轻松的。直接cp XXX .config。然后执行make menuconfig。

      这里详细给大家讲一下内核的配置。Linux的内核配置,就是为了生成.config文件。因为在编译时需要用.config文件生成其他相关配置文件。我们的配置项大多是例如CONFIG_XXXDRIVER,这里的XXXDRIVER指的是各种驱动。我们需要告诉内核,这些驱动是编译进内核,还是编译成模块。通过查找CONFIG_XXXDRIVER,我们可以发现,它出现在四个地方:

      1>C源代码

      2>子目录Makefile:drivers/XXX/Makefile

      3>include/config/auto.conf

      4>include/linux/autoconf.h

      这里首先说明:.config文件在进行内核编译时(make uImage)生成了include/config/auto.conf和include/linux/autoconf.h。通过查看C源代码我们发现CONFIG_XXXDRIVER是一个宏定义,等于一个常量。在include/linux/autoconf.h中宏定义CONFIG_XXXDRIVER为一个常量,可能是0或1。那么现在有一个问题,就是CONFIG_XXXDRIVER到底被编译进内核还是编译成一个模块呢?这在C语言中是无法进行区分的,这种区分体现在哪里呢?这种区分体现在子目录的Makefile文件中。在子目录的Makefile中,若有 obj -y += XXX.o则表示XXX.c被编译进内核;obj -m +=XXX.o则表示XXX被编译成模块,为XXX.ko。include/config/auto.conf文件则是对CONFIG_XXXDRIVER进行赋值,为y时表示编译进内核,为m时表示编译成独立模块。

    #这里是include/config/auto.conf的部分内容
    # Automatically generated make config: don't edit
    # Linux kernel version: 2.6.22.6
    # Sun Nov 27 18:34:38 2016
    #
    CONFIG_CPU_S3C244X=y
    CONFIG_CPU_COPY_V4WB=y
    CONFIG_CRYPTO_CBC=y
    CONFIG_CPU_S3C2410_DMA=y
    CONFIG_CRYPTO_ECB=m
    CONFIG_SMDK2440_CPU2440=y
    #这里是drivers/i2c/Makefile
    # Makefile for the i2c core.
    #
    
    obj-$(CONFIG_I2C_BOARDINFO)    += i2c-boardinfo.o
    obj-$(CONFIG_I2C)        += i2c-core.o
    obj-$(CONFIG_I2C_CHARDEV)    += i2c-dev.o
    obj-y                += busses/ chips/ algos/
    
    ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
    EXTRA_CFLAGS += -DDEBUG
    endif

    3.编译内核
      通过上面的描述,我们可以知道,在每个driver下,都有一个Makefile文件。来定义这个驱动是编译进内核还是编译成模块。这里稍稍提一下。上面我们讲到了在Makefile中单个文件怎样编译进内核和编译成模块。但是如果有两个以上的文件该如何书写呢?这里举个例子:obj -y += a.o b.o就表示将a.c和b.c编译进内核。

    obj -m += ab.o

    ab -objs := a.o b.o

    就可以表示将a.c和b.c共同编译成为一个模块。过程就是 a.c生成a.o , b.c生成b.o。a.o 和b.o共同生成ab.ko。在以往的对uboot启动内核的代码分析中,我们提到过编译内核生成的uImage是由两部分组成的:头部+Linux内核。这个头部包含了很多初始化的参数信息,例如内核的加载地址和入口地址。在编译内核时,我们直接执行make uImage即可。那么在文件中是怎样定义uImage的呢?又是怎样生成uImage的呢?

      首先,我们通过查找Makefile,发现uImage在arch/arm/Makefile中,而这个架构目录下的Makefile被包含进顶层目录的Makefile中。这样我们在执行 make uImage时,顶层目录的Makefile就可以调用架构子目录下的Makefile,实现对内核的编译,生成uImage。

     顶层目录下Makefile中相关命令:
    include $(srctree)/arch/$(ARCH)/Makefile
    架构目录下Makefile相关命令:
    zImage Image xipImage bootpImage uImage: vmlinux

      这就是刚刚所说的顶层Makefile调用架构目录下Makefile,架构目录下Makefile生成uImage,而且依赖于vmlinux文件。下面我们就开始讲解如何生成vmlinux文件。在顶层Makefile中,我们找到了有关生成vmlinux的大部分命令。

    顶层目录Makefile:
    init-y        := init/
    init-y        := $(patsubst %/, %/built-in.o, $(init-y)) 
    
    core-y        := usr/
    core-y        += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
    core-y        := $(patsubst %/, %/built-in.o, $(core-y)) 
    
    libs-y        := lib/
    libs-y1        := $(patsubst %/, %/lib.a, $(libs-y))
    libs-y2        := $(patsubst %/, %/built-in.o, $(libs-y))
    libs-y        := $(libs-y1) $(libs-y2) 
    
    drivers-y    := drivers/ sound/
    drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y))   
    
    net-y        := net/
    net-y        := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
    
    
    vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
    
    vmlinux-init := $(head-y) $(init-y)
    vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
    vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
    vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds
    export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
    
    
    
    架构目录Makefile:
    zImage Image xipImage bootpImage uImage: vmlinux
    
    head-y        := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

      我已经把顶层目录和架构目录下生成vmlinux的命令摘选出来。首先,我们看要想生成vmlinux,需要vmlinux-lds文件、vmlinux-init文件、vmlinux-main文件。其中,vmlinux-lds是链接脚本文件,定义了代码段,数据段的存放位置。这里我们接着往下看,vmlinux-init需要head-y和init-y,通过查看两个Makefile,我们可以得到经过转换后的结果:

    head-y        := 
    arch/arm/kernel/head$(MMUEXT).o    arch/arm/kernel/init_task.o
    
    init-y        := $(patsubst %/, %/built-in.o, $(init-y)) = init/built-in.o
    
    core-y        := $(patsubst %/, %/built-in.o, $(core-y)) 
                             = usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
    
    libs-y        := $(libs-y1) $(libs-y2) =lib/lib.a lib/built-in.o
    
    drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y)) = drivers/built-in.o  sound/built-in.o  
    
    net-y        := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o

      现在已经分析了内核编译的全部过程。那怎样知道我们分析的到底对不对,通过实际执行make uImage我们就可以看到执行过程。这是执行make uImage过程中的部分相关命令:

    arm-linux-ld -EL  -p --no-undefined -X -o vmlinux 
    -T arch/arm/kernel/vmlinux.lds 
    arch/arm/kernel/head.o 
    arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o

      可以看到,首先目标要生成vmlinux,然后是链接脚本为vmlinux.lds。开始生成第一个文件:head.o,第二个文件:init_task.o。这和我们分析的完全一致。接下来以此类推,和我们分析的相同,也就是说我们分析的是正确的。

    SECTIONS
    {
    
    
    
     . = (0xc0000000) + 0x00008000;
    
     .text.head : {
      _stext = .;
      _sinittext = .;
      *(.text.head)
     }
    
     .init : { /* Init code and data        */
       *(.init.text)
      _einittext = .;
      __proc_info_begin = .;
       *(.proc.info.init)
      __proc_info_end = .;
      __arch_info_begin = .;
       *(.arch.info.init)
      __arch_info_end = .;
      __tagtable_begin = .;
       *(.taglist.init)
      __tagtable_end = .;
      . = ALIGN(16);
      __setup_start = .;
       *(.init.setup)
      __setup_end = .;
      __early_begin = .;
       *(.early_param.init)
      __early_end = .;
      __initcall_start = .;

      这是链接脚本vmlinux.lds中的部分内容。首先定义了虚拟地址:(0xc0000000) + 0x00008000。 然后是首先执行头部文件,这与我们分析的完全一致。代码段,初始化代码段等等。

      这就是Linux内核的从配置到编译的全部分析了^_^

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:Linux
    上一篇:php与mysql三日通 下一篇:css和javascript中图片路径的不同
    千万级数据并发解决方案

    相关文章推荐

    • php 应用程序安全防范技术研究• Yii框架分析(二)——CComponent类剖析• linux awk命令详解• linux awk命令详解• Symfony2实现从数据库获取数据的方法小结
    1/1

    PHP中文网