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 每一项都配置,非常多的选项,图形界面配置

  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,按下图进入配置

配置详解

配置的最终目的,是生成了.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启动

kernel(一)编译体验的更多相关文章

  1. GPGPU OpenCL 获取kernel函数编译信息

    使用OpenCL编程时,kernel写成一个单独的文件或者将文件内容保存在一个string中.可以使用clBuildProgram对kernel进行编译链接(compiles & links) ...

  2. Build Android-x86 ICS 4 Virtualbox from Google Virtualbox Target and Intel Kernel 编译体验

    最近一直在研究android源码的编译,应该说研究的很辛苦,最难的是下源码,总是不停的断掉,最后感谢公司的高网速,找到方法后12G的源码只花了1个小时就下完了. 参考以下网址:http://softw ...

  3. 编译kernel:编译

    韦东山Linux视频第1期_裸板_UBoot_文件系统_驱动初步第10课第3节 内核启动流程分析之Makefile.WMV  1. 编译内核分三步: make xxx_defconfig [linux ...

  4. DevEco Device Tool 2.1 Beta1 的Hi3861在Windows平台的编译体验

    DevEco Device Tool迎来了2.1 Beta1,其中的亮点之一是:支持Hi3861开发板的源码在Windows平台编译.带着浓厚的兴趣,第一时间做了一次体验. 首先在官网下载" ...

  5. ARM开发板系统移植-----kernel的编译

    前面一篇文章http://www.cnblogs.com/linzizhang/p/4817336.html介绍了开发板上系统软件的第一部分--bootloader的编译方法. 背景:把bootloa ...

  6. 使用linux kernel代码编译perf工具

    环境:Qemu + ARMv8 perf是一款综合性分析工具,大到系统全局性性能,再小到进程线程级别,甚至到函数及汇编级别. 在内核源码目录下执行编译脚本: #!/bin/bash cross_com ...

  7. linux-2.6.22.6内核启动分析之编译体验

    1 解压缩.打补丁操作 1.1 打开ubuntu,通过FTP将windows相应文件夹下的linux-2.6.22.6.tar.bz2和补丁文件linux-2.6.22.6-jz2440.patch上 ...

  8. 韦东山 第9课第1节.u-boot分析之编译体验 http://www.100ask.net/index.html

    http://www.100ask.net/index.html 韦东山官网网址 http://wenku.baidu.com/view/ae78a00390c69ec3d5bb75ce.html h ...

  9. 4412开发板搭建Uboot、Kernel和Android4.0的编译环境方法

    本文转自迅为4412开发板实战教程书籍:http://www.topeetboard.com 迅为是基于Ubuntu12.04.2平台做开发,所有的配置和编译脚本也是基于此平台,没有在其它平台上测试过 ...

随机推荐

  1. Spring 使用介绍(一)—— 概述

    一.Spring设计原则 1.约定大于配置的契约式编程 2.非侵入式设计 从框架角度可以这样理解,无需继承框架提供的类,这种设计就可以看作是非侵入式设计,如果继承了这些框架类,就是侵入设计 3.面向接 ...

  2. puppet一些常用的参数

    puppet一些常用的参数 通过@,realize来定义使用虚拟资源 虚拟资源主要来解决在安装包的时候,互相冲突的问题 具体参考这里 简单说下,在定义资源的时候加上@ 例如: @package { & ...

  3. 「Splay」区间翻转

    传送门:>Here< 解法分析 用splay来维护这个序列. 一直没有搞明白的是,这里的splay的节点究竟维护的是什么?是权值吗?肯定不是,因为区间是会翻转的,如果维护权值的话很快平衡树 ...

  4. 谈谈IE针对Ajax请求结果的缓存

    在默认情况下,IE会针对请求地址缓存Ajax请求的结果.换句话说,在缓存过期之前,针对相同地址发起的多个Ajax请求,只有第一次会真正发送到服务端.在某些情况下,这种默认的缓存机制并不是我们希望的(比 ...

  5. 天哪又要搬家啦qvq

    CSDN现在怎么这么好看了qvq 搬家回去的欲望日渐强烈... update:2019/02/25 被csdn的侧栏广告烦死了

  6. 转----------数据库常见笔试面试题 - Hectorhua的专栏 - CSDN博客

    数据库基础(面试常见题) 一.数据库基础 1. 数据抽象:物理抽象.概念抽象.视图级抽象,内模式.模式.外模式 2. SQL语言包括数据定义.数据操纵(Data Manipulation),数据控制( ...

  7. centos7安装Jenkins更改默认端口并配置Ldap服务器进行用户认证

    应用环境:Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作.   Jenkins官网:https://jenkins.io/ 测试环境:一台Centos ...

  8. CentOS下Denyhosts的安装和使用

    安装 默认yum就可以进行安装 yum install denyhosts* -y 配置 配置文件路径: /etc/denyhosts.conf ; YUM安装时其实已经配置好了大部分,我们自己稍作改 ...

  9. JSON字符串 与 JSON对象 互转

    一,JSON字符串与JSON对象的区别 JSON对象是符合JSON格式的对象,可以用"对象.属性"进行存取值; JSON字符串是符合JSON格式的字符串; 二,JSON字符串-&g ...

  10. JS截取文件后缀名

    let fileName = this.file.name.lastIndexOf(".");//取到文件名开始到最后一个点的长度 let fileNameLength = thi ...