• kernel(一)编译体验



    title: kernel(一)编译体验
    tags: linux
    date: 2018-11-06 17:27:22

    打补丁

    1. 解压 tar xjf linux-2.6.22.6.tar.bz2
    2. 打补丁,cat下补丁文件知道需要忽略第一个/ patch -p1 < linux-2.6.22.6_jz2440.patch
    3. 打包下生成的文件 tar cjvf linux2.6.22_ok.tar.bz2 linux-2.6.22.6

    配置

    总结

    1. .config通过make自动生成autoconf.hauto.conf
    2. 源代码中通过autoconf.h决定代码编译,无论配置为m或者y都编译,因为头文件都定义为1
    3. 子目录的makefile通过auto.conf中的y/m决定编译为模块.ko还是编译到内核.这个文件是被顶层的Makefile包含

    配置方式

    配置有三种方法:

    1. make menuconfig 每一项都配置,非常多的选项,图形界面配置

      mark

    2. 使用默认配置后执行make menuconfig

      1. 查看下默认配置,使用find -name "*defconfig*"搜索,找到很多配置文件,比如在arch/arm下有文件夹configs保存默认配置

        ./arch/arm/configs/realview_defconfig
        ./arch/arm/configs/hackkit_defconfig
        ./arch/arm/configs/lpd270_defconfig
        .......
        ./arch/arm/configs/s3c2410_defconfig
        
      2. 进入这个目录查看下相关的config,发现 s3c2410_defconfig与我们的单板最为接近

        cd arch/arm/configs/
        ls
        make s3c2410_defconfig
        
      3. 使用对应的最接近的默认配置make s3c2410_defconfig

      4. 执行 make menuconfig.最后提示所有配置项目写入.config

        book@book-desktop:~/stu/kernel/linux2.6/linux-2.6.22.6$ make s3c2410_defconfig
        drivers/serial/Kconfig:235:warning: multi-line strings not supported
        .......
        #
        # configuration written to .config
        #
        
      5. 如果报错,则是因为make版本过新

        book@book-vm:~/work/linux-2.6.22.6$ make s3c2410_defconfig
        Makefile:416: *** mixed implicit and normal rules: deprecated syntax
        Makefile:1449: *** mixed implicit and normal rules: deprecated syntax
        make: *** No rule to make target 's3c2410_defconfig'。 停止。
        
        原因:是由于我的系统的make工具太新,make的旧版规则已经无法兼容新版。
        
        1在makefile中将416行代码
        config %config: scripts_basic outputmakefile FORCE
        改为
        %config: scripts_basic outputmakefile FORCE
        2在makefile中将1449行代码
        / %/: prepare scripts FORCE
        改为
        %/: prepare scripts FORCE
        
    3. 厂家提供配置,直接复制为名为.configcp config_ok .config,再执行 make menuconfig实际上方式2最终也是产生这个.config文件

    配置体验

    输入Y编译进内核
    输入N 不包含
    输入M编译为模块,所谓模块就类似于驱动,可以后置加载
    输入?进入查看具体的配置项
    输入 / 搜索
    

    比如我们配置网络DM9000,按下图进入配置

    mark

    mark

    mark

    mark

    配置详解

    配置的最终目的,是生成了.config文件,查看下这个文件,

    #
    # Automatically generated make config: don't edit
    # Linux kernel version: 2.6.22.6
    # Wed Dec 30 18:30:33 2009
    #
    CONFIG_ARM=y
    CONFIG_SYS_SUPPORTS_APM_EMULATION=y
    CONFIG_GENERIC_GPIO=y
    # CONFIG_GENERIC_TIME is not set
    # CONFIG_GENERIC_CLOCKEVENTS is not set
    CONFIG_MMU=y
    CONFIG_NO_IOPORT=y
    CONFIG_GENERIC_HARDIRQS=y
    .....
    CONFIG_DM9000=y					#来分析这个配置项目
    

    取其中一行 CONFIG_DM9000=y来分析,搜索下文件内容

    grep "CONFIG_DM9000" * -nwR
    
    1. 忽略掉默认配置文件(以defconfig命名的),可以看到代码中有以下.c文件中的宏肯定是源自于头文件,也就是4中的auto.conf
    arch/arm/plat-s3c24xx/common-smdk.c:46:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
    arch/arm/plat-s3c24xx/common-smdk.c:162:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
    arch/arm/plat-s3c24xx/common-smdk.c:200:#endif /* CONFIG_DM9000 */
    arch/arm/plat-s3c24xx/common-smdk.c:250:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
    
    1. 子目录下的Makefile中有以下,这里的会被区分是编译为模块还是编译为内核,参考子目录的Makefile
    drivers/net/Makefile:197:obj-$(CONFIG_DM9000) += dm9dev9000c.o
    drivers/net/Makefile:198:#obj-$(CONFIG_DM9000) += dm9000.o
    drivers/net/Makefile:199:#obj-$(CONFIG_DM9000) += dm9ks.o
    
    1. make之后还会在include下有以下,这个文件很明显就是.config产生的,2中根据这个编译模块还是内核.这个会被顶层的makefile包含
    include/config/auto.conf:144:CONFIG_DM9000=y
    
    1. 还有头文件自动产生的,这个给1中的源代码使用.不论配置为Y或者为M模块,在这个头文件都被定义为1
    include/linux/autoconf.h:145:#define CONFIG_DM9000 1
    //该文件下的内容都是类似的如下
    #define CONFIG_DM9000 1
    #define CONFIG_SOLARIS_X86_PARTITION 1
    #define CONFIG_SERIAL_NONSTANDARD 1
    #define CONFIG_BLK_DEV_RAM_BLOCKSIZE 1024
    

    至于是M与Y的区别,是在makefile中体现的

    Makefile解析

    详细的解释可以看/Documentation/kbuild/makefiles.txt

    子目录的Makefile

    子目录的makefile形式简单,诸如下:

    obj-$(CONFIG_DM9000) += dm9dev9000c.o
    

    然后 CONFIG_DM9000 是在auto.conf中定义,他是由.config中定义为y(内核文件)或者m(编译为.ko模块),所以也就是形如

    obj-y += xxx.o
    obj-m += xxx.o
    如果没有被定义则是
    obj - +=xxx.o 不被处理
    

    架构下面的Makefile

    arch/arm/Makefile
    

    我们执行命令make uImage并不在顶层的makefile,而是在架构下面的makefile,所以它一定会被顶层的makefile包含

    zImage Image xipImage bootpImage uImage: vmlinux
    	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
    

    顶层Makefile

    搜索下arch,可以下顶层的Makefile中找到包含了架构的Makefile

    include $(srctree)/arch/$(ARCH)/Makefile
    export KBUILD_DEFCONFIG
    

    可以继续搜发现同时定义了arm架构,这是在补丁文件修改的

    #ARCH		?= $(SUBARCH)
    ARCH		?= arm
    CROSS_COMPILE	?= arm-linux-
    

    同时搜索下.config文件生成的auto.conf,也在顶层包含

    ifeq ($(dot-config),1)
    # Read in config
    -include include/config/auto.conf
    

    Make解析

    总结:最终在各个目录下生成built-in.o,根据arch/$(ARCH)/kernel/vmlinux.lds的链接脚本链接

    uiamge依赖于vmlinux,uImage实际就是头部信息加上一个真正的内核,也就是vmlinux就是真正的内核

    zImage Image xipImage bootpImage uImage: vmlinux
    	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
    

    可以发现在顶层makefile存在以下,也就是说这也是默认的目标文件

    all: vmlinux
    

    继续搜索目标vmlinux的依赖

    # vmlinux image - including updated kernel symbols
    vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
    ifdef CONFIG_HEADERS_CHECK
    	$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
    endif
    	$(call if_changed_rule,vmlinux__)
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
    	$(Q)rm -f .old_version	
    
    

    其中相关变量继续搜索如下

    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
    # vmlinux-lds:链接脚本。
    # vmlinux-init:一些初始化代码。
    # vmlinux-main:一些主要的代码(与内核核心相关的)。
    

    vmlinux-init

    #顶层 /makefile
    init-y		:= init/
    init-y		:= $(patsubst %/, %/built-in.o, $(init-y))
    # 相当于 init-y =  init/built-in.o
    
    #架构 arch/arm/makefile
    head-y		:= arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
    #这里MMUEXT 并没有被定义,也就是最终就是 arch/arm/kernel/head.o  arch/arm/kernel/init_task.o
    

    patsubst分析

    格式:$(patsubst <pattern>,<replacement>,<text> ) 
    名称:模式字符串替换函数——patsubst。
    功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。
    
    • pattern=%/在这里应该是匹配所有的意思
    • replacement=%/built-in.o
    • text=init/

    也就是说在init/下的所有文件名都被替换为+built-in.o,视频讲的是最后会被编译为built-in.o,也就是相当于 init-y = init/built-in.o

    vmlinux-main

    core-y 内核

    vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
    # core-y
    core-y := usr/
    core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
    core-y := $(patsubst %/, %/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-y		:= lib/
    # 在lib/中查找 替换为lib.a
    libs-y1		:= $(patsubst %/, %/lib.a, $(libs-y))
    # 在lib/中查找 替换为built-in.o
    libs-y2		:= $(patsubst %/, %/built-in.o, $(libs-y))
    # 最终生成 built-in.o 和 lib.a
    libs-y		:= $(libs-y1) $(libs-y2)
    

    同样的,也就是在最终生成built-in.o和 替换为lib.a

    drivers-y 驱动

    drivers-y := drivers/ sound/ #(依赖了这两个目录)
    drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
    

    同样的,也就是在最终生成built-in.o

    net-y 网络

    net-y := net/
    net-y := $(patsubst %/, %/built-in.o, $(net-y))
    

    同样的替换,生成built-in.o

    编译

    uboot启动的是uImage格式的linux kernel, 就是带有头部信息的程序文件

    make uImage
    

    从编译流程分析下编译过程

    rm vmlinux #先删除
    make uImage V=1  # V=1 表示更加详细显示命令
    

    摘录下最后的日志记录

    链接

    文件的顺序在外部定义.第一个文件为arch/arm/kernel/head.o,具体的段由链接脚本决定.这个链接命令实际也是存在.vmlinux.cmd中的

      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  arch/arm/common/built-in.o  arch/arm/mach-s3c2410/built-in.o  arch/arm/mach-s3c2400/built-in.o  arch/arm/mach-s3c2412/built-in.o  arch/arm/mach-s3c2440/built-in.o  arch/arm/mach-s3c2442/built-in.o  arch/arm/mach-s3c2443/built-in.o  arch/arm/nwfpe/built-in.o  arch/arm/plat-s3c24xx/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  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  drivers/built-in.o  sound/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o
    
    
    #-T arch/arm/kernel/vmlinux.lds 
    arch/arm/kernel/head.o 
    arch/arm/kernel/init_task.o
    #这个与makefile中的是对应的
    vmlinux-init := $(head-y) $(init-y)
    head-y		:= arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
    

    链接脚本

    arch/arm/kernel/,文件里面的段按照顺序,比如代码段是先放.text.head,一开始是放“ *”(指所有文件)的 “ .text.head”段。再放init,文件的顺序由外部决定. 链接脚本 vmlinux.lds 是由 vmlinux.lds.S 文件生成的。

    SECTIONS
    {
    
     . = (0xc0000000) + 0x00008000;		#这个是虚拟地址
    
     .text.head : {						#先放所有文件的 .text.head 段
      _stext = .;
      _sinittext = .;
      *(.text.head)
     }
    
     .init : { /* Init code and data		*/ #再接着是放所有文件的“ .init.text”段。
       *(.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 = .;
       *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
      __initcall_end = .;
      __con_initcall_start = .;
       *(.con_initcall.init)
      __con_initcall_end = .;
      __security_initcall_start = .;
       *(.security_initcall.init)
      __security_initcall_end = .;
    
      . = ALIGN(32);
      __initramfs_start = .;
       usr/built-in.o(.init.ramfs)
      __initramfs_end = .;
    
      . = ALIGN(4096);
      __per_cpu_start = .;
       *(.data.percpu)
      __per_cpu_end = .;
    
      __init_begin = _stext;
      *(.init.data)
      . = ALIGN(4096);
      __init_end = .;
    
     }
    

    烧写内核

    在uboot下,输入k进入烧写,使用dnw烧写程序,具体的k实现了什么命令,查看下uboot代码,在cmd_menu.c定义

    strcpy(cmd_buf, 
    
    "usbslave 1 0x30000000; nand erase kernel; 
    nand write.jffs2 0x30000000 kernel $(filesize)");
    
    run_command(cmd_buf, 0);
    

    然后就可以通过b启动

  • 相关阅读:
    2018/01/01Java基础学习——如何通过dos系统的javadoc命令生成API文档
    Go语言的big包实现大整数运算
    HDU5100 Chessboard【组合数学】
    HDU5143 NPY and arithmetic progression【暴力】
    UVALive5402 UVA579 Clock Hands【水题】
    UVA11799 Horror Dash【求极值+水题】
    HDU1017 ZOJ1152 A Mathematical Curiosity【暴力】
    UVALive2536 POJ1248 HDU1015 ZOJ1403 Safecracker【密码+暴力】
    POJ1284 Primitive Roots【原根】
    UVA11340 Newspaper【输入流+map】
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10023694.html
Copyright © 2020-2023  润新知