• linux内核启动地址的确定 2017-02-27 15:38:09
    内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds, vmlinux-armv.lds的生成过程在kernel/arch/arm/Makefile中 ifeq ($(CONFIG_CPU_32),y) PROCESSOR = armv ...
    

    内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,

    vmlinux-armv.lds的生成过程在kernel/arch/arm/Makefile中

    ifeq ($(CONFIG_CPU_32),y)
    PROCESSOR     = armv
    TEXTADDR     = 0xC0008000
    LDSCRIPT     = arch/arm/vmlinux-armv.lds.in
    endif
    
    arch/arm/vmlinux.lds: $(LDSCRIPT) dummy
        @sed 's/TEXTADDR/$(TEXTADDR)/;s/DATAADDR/$(DATAADDR)/' $(LDSCRIPT) >$@

    查看arch/arm/vmlinux.lds 中

    OUTPUT_ARCH(arm)
    ENTRY(stext)
    SECTIONS
    {
        . = 0xC0008000;
        .init : {            /* Init code and data        */
            _stext = .;
            __init_begin = .;
                *(.text.init)
            __proc_info_begin = .;
                *(.proc.info)
            __proc_info_end = .;
            __arch_info_begin = .;
                *(.arch.info)
            __arch_info_end = .;
            __tagtable_begin = .;
                *(.taglist)
            __tagtable_end = .;
                *(.data.init)
            . = ALIGN(16);
            __setup_start = .;
                *(.setup.init)
            __setup_end = .;
            __initcall_start = .;
                *(.initcall.init)
            __initcall_end = .;
            . = ALIGN(4096);
            __init_end = .;
        }
    
        /DISCARD/ : {            /* Exit code and data        */
            *(.text.exit)
            *(.data.exit)
            *(.exitcall.exit)
        }
    
        .text : {            /* Real text segment        */
            _text = .;        /* Text and read-only data    */
                *(.text)
                *(.fixup)
                *(.gnu.warning)
                *(.rodata)
                *(.rodata.*)
                *(.glue_7)
                *(.glue_7t)
            *(.got)            /* Global offset table        */
    
            _etext = .;        /* End of text section        */
        }
    
        .kstrtab : { *(.kstrtab) }
    
        . = ALIGN(16);
        __ex_table : {            /* Exception table        */
            __start___ex_table = .;
                *(__ex_table)
            __stop___ex_table = .;
        }
    
        __ksymtab : {            /* Kernel symbol table        */
            __start___ksymtab = .;
                *(__ksymtab)
            __stop___ksymtab = .;
        }
    
        . = ALIGN(8192);
    
        .data : {
            /*
             * first, the init task union, aligned
             * to an 8192 byte boundary.
             */
            *(.init.task)
    
            /*
             * then the cacheline aligned data
             */
            . = ALIGN(32);
            *(.data.cacheline_aligned)
    
            /*
             * and the usual data section
             */
            *(.data)
            CONSTRUCTORS
    
            _edata = .;
        }
    
        .bss : {
            __bss_start = .;    /* BSS                */
            *(.bss)
            *(COMMON)
            _end = . ;
        }
                        /* Stabs debugging sections.    */
        .stab 0 : { *(.stab) }
        .stabstr 0 : { *(.stabstr) }
        .stab.excl 0 : { *(.stab.excl) }
        .stab.exclstr 0 : { *(.stab.exclstr) }
        .stab.index 0 : { *(.stab.index) }
        .stab.indexstr 0 : { *(.stab.indexstr) }
        .comment 0 : { *(.comment) }
    }

    arch/arm/Makefile中:

    vmlinux: arch/arm/vmlinux.lds
    
    arch/arm/vmlinux.lds: $(LDSCRIPT) dummy
        @sed 's/TEXTADDR/$(TEXTADDR)/;s/DATAADDR/$(DATAADDR)/' $(LDSCRIPT) >$@
    
    
    MAKEBOOT     = $(MAKE) -C arch/$(ARCH)/boot
    
    bzImage zImage zinstall Image bootpImage install: vmlinux
        @$(MAKEBOOT) $@

    但在kernel/arch/arm/boot/Makefile

    ifeq ($(CONFIG_ARCH_S3C2410),y)
    ZTEXTADDR     = 0x30008000
    ZRELADDR     = 0x30008000
    endif
    
    zImage:    $(CONFIGURE) compressed/vmlinux
        $(OBJCOPY) -O binary -R .note -R .comment -S compressed/vmlinux $@
    
    compressed/vmlinux: $(TOPDIR)/vmlinux dep
        @$(MAKE) -C compressed vmlinux
    
    在compressed目录下的Makefile中
    
    ZLDFLAGS     = -p -X -T vmlinux.lds
    
    SEDFLAGS    = s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;s/BSS_START/$(ZBSSADDR)/
    
    all:        vmlinux
    
    vmlinux:    $(HEAD) $(OBJS) piggy.o vmlinux.lds
            $(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(LIBGCC) -o vmlinux
    
    
    vmlinux.lds:    vmlinux.lds.in Makefile $(TOPDIR)/arch/$(ARCH)/boot/Makefile $(TOPDIR)/.config
            @sed "$(SEDFLAGS)" < vmlinux.lds.in > $@

    vmlinux-armv.lds.in文件的内容:

    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
      . = LOAD_ADDR;
      _load_addr = .;
    
      . = TEXT_START;
      _text = .;
    
      .text : {
        _start = .;
        *(.start)
        *(.text)
        *(.fixup)
        *(.gnu.warning)
        *(.rodata)
        *(.rodata.*)
        *(.glue_7)
        *(.glue_7t)
        input_data = .;
        piggy.o
        input_data_end = .;
        . = ALIGN(4);
      }
    
      _etext = .;
    
      _got_start = .;
      .got            : { *(.got) }
      _got_end = .;
      .got.plt        : { *(.got.plt) }
      .data            : { *(.data) }
      _edata = .;
    
      . = BSS_START;
      __bss_start = .;
      .bss            : { *(.bss) }
      _end = .;
    
      .stack (NOLOAD)    : { *(.stack) }
    
      .stab 0        : { *(.stab) }
      .stabstr 0        : { *(.stabstr) }
      .stab.excl 0        : { *(.stab.excl) }
      .stab.exclstr 0    : { *(.stab.exclstr) }
      .stab.index 0        : { *(.stab.index) }
      .stab.indexstr 0    : { *(.stab.indexstr) }
      .comment 0        : { *(.comment) }
    }

    vmlinux.lds内容为

    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
      . = 0x30008000;
      _load_addr = .;
    
      . = 0;
      _text = .;
    
      .text : {
        _start = .;
        *(.start)
        *(.text)
        *(.fixup)
        *(.gnu.warning)
        *(.rodata)
        *(.rodata.*)
        *(.glue_7)
        *(.glue_7t)
        input_data = .;
        piggy.o
        input_data_end = .;
        . = ALIGN(4);
      }
    
      _etext = .;
    
      _got_start = .;
      .got            : { *(.got) }
      _got_end = .;
      .got.plt        : { *(.got.plt) }
      .data            : { *(.data) }
      _edata = .;
    
      . = ALIGN(4);
      __bss_start = .;
      .bss            : { *(.bss) }
      _end = .;
    
      .stack (NOLOAD)    : { *(.stack) }
    
      .stab 0        : { *(.stab) }
      .stabstr 0        : { *(.stabstr) }
      .stab.excl 0        : { *(.stab.excl) }
      .stab.exclstr 0    : { *(.stab.exclstr) }
      .stab.index 0        : { *(.stab.index) }
      .stab.indexstr 0    : { *(.stab.indexstr) }
      .comment 0        : { *(.comment) }
    }

    一般情况下都在生成vmlinux后,再对内核进行压缩成为zImage,压缩的目录是kernel/arch/arm/boot。

    下载到flash中的是压缩后的zImage文件,zImage是由压缩后的vmlinux和解压缩程序组成,如下图所示:

    |-----------------|\    |-----------------|
                |                 | \   |                 |
                |                 |  \  | decompress code |
                |     vmlinux     |   \ |-----------------|    zImage
                |                 |    \|                 |
                |                 |     |                 |
                |                 |     |                 |
                |                 |     |                 |
                |                 |    /|-----------------|
                |                 |   /
                |                 |  /
                |                 | /
                |-----------------|/

    zImage链接脚本也叫做vmlinux.lds,位于kernel/arch/arm/boot/compressed。

    是由同一目录下的vmlinux.lds.in文件生成的在kernel/arch/arm/boot/Makefile文件中定义了:

    ifeq ($(CONFIG_ARCH_S3C2410),y)
      ZTEXTADDR     = 0x30008000
      ZRELADDR     = 0x30008000
    endif

    ZTEXTADDR就是解压缩代码的ram偏移地址,ZRELADDR是内核ram启动的偏移地址,这里看到指定ZTEXTADDR的地址为30008000,

    以上就是我对内核启动地址的分析,总结一下内核启动地址的设置:

    设置kernel/arch/arm/boot/Makefile文件中的

    ifeq ($(CONFIG_ARCH_S3C2410),y)
      ZTEXTADDR     = 0x30008000          
      ZRELADDR     = 0x30008000
    endif
    # We now have a PIC decompressor implementation.  Decompressors running
    # from RAM should not define ZTEXTADDR.  Decompressors running directly
    # from ROM or Flash must define ZTEXTADDR (preferably via the config)
    #

    查看2410的datasheet ,发现内存映射的基址是0x3000 0000 ,那么 0x30008000又是如何来的呢?

    在内核文档kernel/Document/arm/Booting 文件中有:

    Calling the kernel image
    
    Existing boot loaders: MANDATORY
    New boot loaders: MANDATORY
    
    There are two options for calling the kernel zImage. If the zImage is stored in flash, and is linked correctly to be run from flash, then it is legal for the boot loader to call the zImage in flash directly.
    
    The zImage may also be placed in system RAM (at any location) and called there. Note that the kernel uses 16K of RAM below the image to store page tables. The recommended placement is 32KiB into RAM.

    看来在image下面用了32K(0x8000)的空间存放内核页表,0x30008000就是2410的内核在RAM中的启动地址,这个地址就是这么来的。

    关于内核解压缩的过程分析

    内核压缩和解压缩代码都在目录kernel/arch/arm/boot/compressed,编译完成后将产生vmlinux、head.o、misc.o、head-s3c2410.o、piggy.o这几个文件,head.o是内核的头部文件,负责初始设置;

    misc.o将主要负责内核的解压工作,它在head.o之后;

    head-s3c2410.o文件主要针对的初始化,将在链接时与head.o合并;

    piggy.o是一个中间文件,其实是一个压缩的内核(kernel/vmlinux),只不过没有和初始化文件及解压文件链接而已;

    vmlinux是没有(zImage是压缩过的内核)压缩过的内核,就是由piggy.o、head.o、misc.o、head-s3c2410.o组成的。

    在BootLoader完成系统的引导以后并将Linux内核调入内存之后,调用bootLinux(),

    这个函数将跳转到kernel的起始位置。

    如果kernel没有压缩,就可以启动了。

    如果kernel压缩过,则要进行解压,在压缩过的kernel头部有解压程序。

    压缩过得kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。

    它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,

    decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,

    然后使用在打印出信息“Uncompressing Linux...”后,调用gunzip()。将内核放于指定的位置。

    以下分析head.S文件:

    (1)对于各种Arm CPU的DEBUG输出设定,通过定义宏来统一操作。

    (2)设置kernel开始和结束地址,保存architecture ID。

    (3)如果在ARM2以上的CPU中,用的是普通用户模式,则升到超级用户模式,然后关中断。

    (4)分析LC0结构delta offset,判断是否需要重载内核地址(r0存入偏移量,判断r0是否为零)。

    这里是否需要重载内核地址,我以为主要分析arch/arm/boot/Makefile、arch/arm/boot/compressed/Makefile

    和arch/arm/boot/compressed/vmlinux.lds.in三个文件,主要看vmlinux.lds.in链接文件的主要段的位置,

    LOAD_ADDR(_load_addr)=0x30008000,而对于TEXT_START(_text、_start)的位置只设为0,BSS_START(__bss_start)=ALIGN(4)。

    对于这样的结果依赖于,对内核解压的运行方式,也就是说,内核解压前是在内存(RAM)中还是在FLASH上,

    因为这里,我们的BOOTLOADER将压缩内核(zImage)移到了RAM的0x30008000位置,我们的压缩内核是在内存(RAM)从0x30008000地址开始顺序排列,

    因此我们的r0获得的偏移量是载入地址(0x30008000)。

    接下来的工作是要把内核镜像的相对地址转化为内存的物理地址,即重载内核地址。

    (5)需要重载内核地址,将r0的偏移量加到BSS region和GOT table中。

    (6)清空bss堆栈空间r2-r3。

    (7)建立C程序运行需要的缓存,并赋于64K的栈空间。

    (8)这时r2是缓存的结束地址,r4是kernel的最后执行地址,r5是kernel境象文件的开始地址。检查是否地址有冲突。

    将r5等于r2,使decompress后的kernel地址就在64K的栈之后。

    (9)调用文件misc.c的函数decompress_kernel(),解压内核于缓存结束的地方(r2地址之后)。此时各寄存器值有如下变化:

    r0为解压后kernel的大小

    r4为kernel执行时的地址

    r5为解压后kernel的起始地址

    r6为CPU类型值(processor ID)

    r7为系统类型值(architecture ID)

    (10)将reloc_start代码拷贝之kernel之后(r5+r0之后),首先清除缓存,而后执行reloc_start。

    (11)reloc_start将r5开始的kernel重载于r4地址处。

    (12)清除cache内容,关闭cache,将r7中architecture ID赋于r1,执行r4开始的kernel代码。

    下面简单介绍一下解压缩过程,也就是函数decompress_kernel实现的功能:

    解压缩代码位于kernel/lib/inflate.c,inflate.c是从gzip源程序中分离出来的。包含了一些对全局数据的直接引用。

    在使用时需要直接嵌入到代码中。gzip压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码,

    在解压时需要一个至少为32K字节的解压缓冲区,它定义为window[WSIZE]。inflate.c使用get_byte()读取输入文件,

    它被定义成宏来提高效率。输入缓冲区指针必须定义为inptr,inflate.c中对之有减量操作。inflate.c调用flush_window()

    来输出window缓冲区中的解压出的字节串,每次输出长度用outcnt变量表示。在flush_window()中,还必

    须对输出字节串计算CRC并且刷新crc变量。在调用gunzip()开始解压之前,调用makecrc()初始化CRC计算表。

    最后gunzip()返回0表示解压成功。

    我们在内核启动的开始都会看到这样的输出:

    Uncompressing Linux...done, booting the kernel.

    这也是由decompress_kernel函数内部输出的,它调用了puts()输出字符串,

    puts是在kernel/include/asm-arm/arch-s3c2410/uncompress.h中实现的。

    执行完解压过程,再返回到head.S中,启动内核:

    call_kernel:    bl  cache_clean_flush
             bl  cache_off
             mov r0, #0
             mov r1, r7          @ restore architecture number
             mov pc, r4          @ call kernel

    下面就开始真正的内核了。

    展开全文
  • Linux内核启动地址 2006-12-18 21:27:00
    内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,但是该文件是由vmlinux-armv.lds.in生成的,根据编译选项的不同源文件还可以是vmlinux-armo.lds.in,vmlinux-...
  • Linux修改内核参数 2018-09-15 17:41:11
    Linux /proc/sys/kernel 和/proc/sys/vm下的文件控制内核的运行,可以通过修改参数的方式来改变内核功能(立即生效) 修改参数方法: 方法一: echo value &gt; /proc/sys/path [root@lulijuan ~]# ...
  • Linux修改内核版本 2017-07-04 16:04:11
    如果执行以上命令后,输出的内核版本号低于 3.8,请参考下面的方法来来升级您的 Linux 内核。 对于 CentOS 6.5 而言,内核版本默认是 2.6。首先,可通过以下命令安装最新内核: rpm --import ...
  •  下载2.6.19或更新的Linux内核,配置该内核使其支持NTFS,并在新的内核中修改其版本为Linux NameTestKernelx.x.x,其中,Name是你的名字(汉语拼音);x.x.x是新内核的版本号,最后在你的机器上编译安装这个新内核...
  • Linux升级内核的正确姿势 2018-08-21 11:26:10
    Linux升级内核的正确姿势 很多童鞋在玩耍linux发行版的时候,都会遇到各种各样的问题,比如:网卡不能使用,亮度不能调节,触摸板不能识别...Linux内核最早是于1991年由芬兰黑客林纳斯·托瓦兹为自己的个人计算机开...
  • 1. 查看当前系统具有的内核 $ cat /boot/grub2/grub.cfg |grep menuentry 查看到如下内容列表 menuentry ‘CentOS Linux (3.10.0-327.22.2.el7.x86_64) 7 (Core)’ –class centos menuentry ‘CentOS...
  • Linux升级内核以及删除Linux多余的内核 很多童鞋在玩耍linux发行版的时候,都会遇到各种各样的问题,比如:网卡不能使用,亮度不能调节,触摸板不能识别...Linux内核最早是于1991年由芬兰黑客林纳斯·托瓦兹为自己...
  • Linux2.6.24内核注释 2020-07-01 11:47:20
    这是半年来,在看ULA的过程中,针对Linux 2.6.24内核顺手做的一点注释。内容不多,个人觉得文件系统和USB这两个模块的注释还有一点意思。 所有注释都是中文,您可以与标准2.6.24内核进行比较,看看具体的注释内容。 ...
  • Linux内核简介 2018-06-16 18:56:29
    大纲:理解Linux内核最好预备的知识点...虚拟地址与物理地址 特权级别(Linux的两种状态) 系统调用设备驱动程序、块设备和字符设备网络文件系统模块和热插拔注:本文为《深入Linux内核架构》 的学习笔记理解Linux...
  • linux2.6.1内核源码注释 2020-07-03 21:31:47
    包含LINUX内核同步、信号、内存、调度、文件系统、网络系统、时钟等部分的源码注释。前后历时三年,算是干货。
  • uboot引导linux内核镜像(uImage)启动时,会有2个地址 加载地址(Load Address),即内核镜像整体要放置的内存空间位置 入口地址(Entry Address),即从内核镜像中开始执行的地址 示意图如下, 其中,内核镜像...
  • Linux内核的整体架构简介 2017-10-23 17:46:51
    本文是“Linux内核分析”系列文章的第一篇,会以内核的核心功能为出发点,描述Linux内核的整体架构,以及架构之下主要的软件子系统。之后,会介绍Linux内核源文件的目录结构,并和各个软件子系统对应。 注:本文和...
  • Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能...Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型。进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存
  • linux内核编译详解 2020-05-18 23:47:36
    前言Linux内核是Linux操作系统的核心,也是整个Linux功能体现的核心,就如同发动机在汽车中的重要性。内核主要功能包括进程管理、内存管理、文件管理、设备管理、网络管理等。Linux内核是单内核设计,但却采用了微...
  • Linux内核编译详细总结 2018-03-12 16:16:31
    学习重新编译Linux内核,理解、掌握Linux内核和发行版本的区别。 &nbsp; 二、实验内容 在Linux操作系统环境下重新编译内核。实验主要内容: A.&nbsp;查找并且下载一份内核源代码,本实验使用最新的Linux内核...
  • 转载于:21运维 » Linux CentOS 7.X 如何修改内核启动默认顺序 我们知道,centos 6.x是通过/etc/grub.conf就行内核启动顺序修改的,而且比较直观查看。但centos 7的系统和6就不一样了,是通过grub2为引导程序。下边...
  • 内核也是程序,也应该具有自己的虚存空间,但是作为一种为用户程序服务的程序,内核空间有它自己的特点。   内核空间与用户空间的关系 在一个32位系统中,一个程序的虚拟空间最大可以是4GB,那么最直接的做法...
  • 本博实时更新《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)的最新进展。 目前已经完成稿件。 2015年8月9日,china-pub开始上线预售: ... 2015年8月20日,各路朋友报喜...
  • 学习linux内核是需要一步一步循序渐进,掌握正确的linux内核学习路线对学习至关重要,本篇文章就来分享学习linux内核的一些建议吧。 1. 了解操作系统基本概念。如果不会,可以学习《操作系统:设计与实现》Andrew S...
1 2 3 4 5 ... 20
收藏数 651,508
精华内容 260,603