热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

kernel编译openwrt_记录Openwrt产品定制开发过程,(二)Make过程

前一章节:(一)环境配置和目录结构说明Openwrt所有的代码都会在make过程中从网上获取,因为墙的原因,有时会出现下载问题࿰

前一章节:(一) 环境配置和目录结构说明

===========================

Openwrt所有的代码都会在make过程中从网上获取,因为墙的原因,有时会出现下载问题,甚至会出现下载完成了但无法在脚本中识别的情况,其过程颇为痛苦。别无他法,只有一个问题,一个问题的解决。俺也是这么摸过来的。 :)

另外,如果您要实践一下,最好就买一块openwrt社区支持的开发板来,过手比什么都有效。这个是我平常做验证实验的一块板子。 :)

da1ccd7485cce699d4d2b31f95c44169.png

===========================

1 Make过程

1.1 Package的make过程

一个package,例如:核心mips,应用luci,会在各自make脚本的控制下完成下载、打包、编译、链接bin文件的过程。具体如下。

以luci应用为例。

  • make 读取luci的makefile, ./package/utils/lua/Makefile 文件内容。
  • clone源代码到 ./tmp/dl==>压缩源码为lua-x.x.x.tar.gz或.bz2包==>copy到./dl/目录==>删除.git 或 .svn 目录和源文件。
  • 将 dl/ 目录下的 lua-x.x.x.tar.gz 文件解压到 build_dir/target-mips_/ 目录下。
  • 进入 build_dir/target-mips_/lua-x.x.x/
  • ./configure,make,make install。
  • make install 会将生成的二进制文件安装到 build_dir/target-mips_/lua-x.x.x/ipkg-ar71xx/ 目录下。
  • 最后将 build_dir/target-mips_/lua-x.x.x/ipkg-ar71xx/ 打成包成lua-x.x.x-1_ar71xx.ipk,并复制到 bin/ar71xx/packages/luci/ 。

1.2 makefile分析

openwrt根目录下的Makefile是执行make命令时的入口。从这里开始分析。

world:ifneq ($(OPENWRT_BUILD),1) override OPENWRT_BUILD=1 export OPENWRT_BUILD GREP_OPTIONS= export GREP_OPTIONS include $(TOPDIR)/include/debug.mk include $(TOPDIR)/include/depends.mk include $(TOPDIR)/include/toplevel.mkelse include rules.mk include $(INCLUDE_DIR)/depends.mk include $(INCLUDE_DIR)/subdir.mk include target/Makefile include package/Makefile include tools/Makefile include toolchain/Makefile

上面这段是主Makefile的结构,主要有两个部分:一个是执行编译前的准备工作,else分支主要执行编译工作。

执行make时,若无任何目标指定,默认目标是world。

初始时OPENWRT_BUILD=0,执行编译准备工作;如果执行命令make OPENWRT_BUILD=1,则直接进入else逻辑。

编译时一般直接使用make V=s -j5这样的命令,不会指定OPENWRT_BUILD变量。

1.3 OPENWRT_BUILD!=1

override OPENWRT_BUILD=1 export OPENWRT_BUILD

更改了OPENWRT_BUILD变量的值。这里起到的作用是下次执行make时,会进入到else逻辑中。

toplevel.mk中可以看到默认的目标:

%:: @+$(PREP_MK) $(NO_TRACE_MAKE) -r -s prereq @./scripts/config/conf --defconfig=.config Config.in @+$(ULIMIT_FIX) $(SUBMAKE) -r $@

在toplevel.mk的顶部定义了PREP_MK= OPENWRT_BUILD= QUIET=0,将OPENWRT_BUILD的值赋为0。

在执行@+$(PREP_MK) $(NO_TRACE_MAKE) -r -s prereq命令的时候,在make命令行里有$(PREP_MK)变量,而由于OPENWRT_BUILD的值为0,在verbose.mk文件里,所以会执行顶层目录的Makefile第一个分支部分的目标prereq,即toplevel.mk文件中的目标

prereq:: prepare-tmpinfo .config @+$(NO_TRACE_MAKE) -r -s $@

这里会进行一些编译前的准备工作,然后执行@+$(NO_TRACE_MAKE) -r -s $@,再次去执行顶层Makefile。

中最终执行完成prereq和world目标,这两个目标都会进入到else逻辑的编译处理中。

1.4 OPENWRT_BUILD==1

首先就引入了target, package, tools, toolchain这四个关键目录里的Makefile文件

include target/Makefile include package/Makefile include tools/Makefile include toolchain/Makefile

这些子目录里的Makefile使用include/subdir.mk里定义的两个函数来动态生成规则,这两个函数是subdir和stampfile

1.4.1 stampfile

拿target/Makefile举例:

(eval(call stampfile,$(curdir),target,prereq,.config))

会生成类似这样的规则:

target/stamp-prereq:=$(STAGING_DIR)/stamp/.target_prereq $$(target/stamp-prereq): $(TMP_DIR)/.build .config @+$(SCRIPT_DIR)/timestamp.pl -n $$(target/stamp-prereq) target .config || make $$(target/flags-prereq) target/prereq @mkdir -p $$$$(dirname $$(target/stamp-prereq)) @touch $$(target/stamp-prereq) $$(if $(call debug,target,v),,.SILENT: $$(target/stamp-prereq)) .PRECIOUS: $$(target/stamp-prereq) # work around a make bug target//clean:=target/stamp-prereq/clean target/stamp-prereq/clean: FORCE @rm -f $$(target/stamp-prereq)

可以简单的看作: (eval(call stampfile,(curdir),target,prereq,.config))生成了目标(target/stamp-prereq)

  • 对于target分别生成了:(target/stamp?preq),(target/stamp-copile), $(target/stamp-install)
  • toolchain : $(toolchain/stamp-install)
  • package : (package/stamp?preq),(package/stamp-cleanup), (package/stamp?compile),(package/stamp-install)
  • tools : $(tools/stamp-install)

1.4.2 subdir

subdir这个函数写了一大堆东西,看起来很复杂 。

$(call subdir, target) 会遍历下的子目录,执行 make -C 操作。这样就切入子目录中去了。

1.5 目录变量

几个重要的目录路径:

KERNEL_BUILD_DIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18 LINUX_DIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18 KDIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a BIN_DIR bin/ramips Makefile中包含了rules.mk, target.mk等.mk文件,这些文件中定义了许多变量,有些是路径相关的,有些是软件相关的。这些变量在整个Makefile工程中经常被用到, TARGET_ROOTFS_DIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2 BUILD_DIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2 STAGING_DIR_HOST staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2 TARGET_DIR build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips

1.6 kernel 编译:

target/linux/ramips/Makefile: $(eval $(call BuildTarget)) target/linux/Makefile : export TARGET_BUILD=1 include/target.mk: ifeq ($(TARGET_BUILD),1) include $(INCLUDE_DIR)/kernel-build.mk BuildTarget?=$(BuildKernel)endif

BuildKernel是include/kernel-build.mk定义的一个多行变量,其中描述了如何编译内核, 主要关注其中install规则的依赖链:

$(KERNEL_BUILD_DIR)/symtab.h: FORCE rm -f $(KERNEL_BUILD_DIR)/symtab.h touch $(KERNEL_BUILD_DIR)/symtab.h +$(MAKE) $(KERNEL_MAKEOPTS) vmlinux ... $(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE $(Kernel/CompileImage) $(Kernel/CollectDebug) touch $$@

install: $(LINUX_DIR)/.image +$(MAKE) -C image compile install TARGET_BUILD=1. 触发make vmlinux命令生成vmlinux:

install --> $(LINUX_DIR)/.image --> $(KERNEL_BUILD_DIR)/symtab.h --> `$(MAKE) $(KERNEL_MAKEOPTS) vmlinux` 2. 对vmlinux做objcopy, strip操作: $(LINUX_DIR)/.image --> $(Kernel/CompileImage) --> $(call Kernel/CompileImage/Default) --> $(call Kernel/CompileImage/Default) $(KERNEL_CROSS)objcopy -O binary $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(LINUX_KERNEL)$(1)

--> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux $(KERNEL_CROSS)objcopy $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux$(1).elf

--> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.elf $(CP) $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux.debug

--> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.debug

1.7 生成firmware

firmware由kernel和rootfs两个部分组成,要对两个部分先分别处理,然后再合并成一个.bin文件。先看一下这个流程。

"target/linux/ramips/image/Makefile" 文件中的最后一句:$(eval $(call BuildImage)),将BuildImage展开在这里。BuildImage定义在 include/image.mk 文件中,其中定义了数个目标的规则。

define BuildImage compile: compile-targets FORCE **$(call Build/Compile)** install: compile install-targets FORCE ... $(call Image/BuildKernel) ## 处理vmlinux ... $(call Image/mkfs/squashfs) ## 生成squashfs,并与vmlinux合并成一个.bin文件 ... endef

1.8 处理vmlinux: Image/BuildKernel

target/linux/ramips/image/Makefile:

define Image/BuildKernel cp $(KDIR)/vmlinux.elf $(BIN_DIR)/$(VMLINUX).elf cp $(KDIR)/vmlinux $(BIN_DIR)/$(VMLINUX).bin $(call CompressLzma,$(KDIR)/vmlinux,$(KDIR)/vmlinux.bin.lzma) $(call MkImage,lzma,$(KDIR)/vmlinux.bin.lzma,$(KDIR)/uImage.lzma) cp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).binifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) cp $(KDIR)/vmlinux-initramfs.elf $(BIN_DIR)/$(VMLINUX)-initramfs.elf cp $(KDIR)/vmlinux-initramfs $(BIN_DIR)/$(VMLINUX)-initramfs.bin $(call CompressLzma,$(KDIR)/vmlinux-initramfs,$(KDIR)/vmlinux-initramfs.bin.lzma) $(call MkImage,lzma,$(KDIR)/vmlinux-initramfs.bin.lzma,$(KDIR)/uImage-initramfs.lzma) cp $(KDIR)/uImage-initramfs.lzma $(BIN_DIR)/$(UIMAGE)-initramfs.binendif $(call Image/Build/Initramfs) endef

1.8.1 lzma压缩内核

build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/ 目录中:

lzma e vmlinux -lc1 -lp2 -pb2 vmlinux.bin.lzma

1.8.2 MkImage

build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/ 目录中:

mkimage -A mips -O linux -T kernel -C lzma -a 0x80000000 -e 0x80000000 -n "MIPS OpenWrt Linux-3.14.18" -d vmlinux.bin.lzma uImage.lzma

1.8.3 copy

VMLINUX:=$(IMG_PREFIX)-vmlinux --> openwrt-ramips-mt7620a-vmlinux UIMAGE:=$(IMG_PREFIX)-uImage --> openwrt-ramips-mt7620a-uImagecp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).bin

把uImage.lzma复制到bin/ramips/目录下:

cp $(KDIR)/uImage.lzma bin/ramips/openwrt-ramips-mt7620a-uImage

1.9 制作squashfs,生成.bin: $(call Image/mkfs/squashfs)

define Image/mkfs/squashfs @mkdir -p $(TARGET_DIR)/overlay $(STAGING_DIR_HOST)/bin/mksquashfs4 $(TARGET_DIR) $(KDIR)/root.squashfs -nopad -noappend -root-owned -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) -processors $(if $(CONFIG_PKG_BUILD_JOBS),$(CONFIG_PKG_BUILD_JOBS),1) $(call Image/Build,squashfs)endif

1.9.1 mkdir -p $(TARGET_DIR)/overlay

mkdir -p build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips/overlay

1.9.2 mksquashfs4

$(STAGING_DIR_HOST)/bin/mksquashfs4 $(TARGET_DIR) $(KDIR)/root.squashfs -nopad -noappend -root-owned -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) -processors $(if $(CONFIG_PKG_BUILD_JOBS),$(CONFIG_PKG_BUILD_JOBS),1)

制作squashfs文件系统,生成root.squashfs:

mksquashfs4 root-ramips root.squashfs -nopad -noappend -root-owned -comp gzip -b 256k -p '/dev d 755 0 0' -p '/dev/console c 600 0 0 5 1' -processors 1

1.9.3 $(call Image/Build,squashfs)

在 target/linux/ramips/image/Makefile 中:

define Image/Build $(call Image/Build/$(1)) dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-root.$(1) bs=128k conv=sync $(call Image/Build/Profile/$(PROFILE),$(1))endef

· dd if=(KDIR)/root.squashfsof=(BIN_DIR)/$(IMG_PREFIX)-root.squashfs bs=128k conv=sync

dd if=build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/root.squashfs of=bin/ramips/openwrt-ramips-mt7620-root.squashfs bs=128k conv=sync

· (callImage/Build/Profile/(PROFILE),squashfs)

target/linux/ramips/mt7620a/profiles/00-default.mk, 中调用 Profile 函数:$(eval $(call Profile,Default))

include/target.mk 中定义了 Profile 函数, 其中令 PROFILE=Default

define Image/Build/Profile/Default $(call Image/Build/Profile/MT7620a,$(1)) ... endef

规则依赖序列如下:

$(call Image/Build/Profile/$(PROFILE),squashfs) --> $(call BuildFirmware/Default8M/squashfs,squashfs,mt7620a,MT7620a) --> $(call BuildFirmware/OF,squashfs,mt7620a,MT7620a,8060928) --> $(call MkImageLzmaDtb,mt7620a,MT7620a) --> $(call PatchKernelLzmaDtb,mt7620a,MT7620a) --> $(call MkImage,lzma,$(KDIR)/vmlinux-mt7620a.bin.lzma,$(KDIR)/vmlinux-mt7620a.uImage) --> $(call MkImageSysupgrade/squashfs,squashfs,mt7620a,8060928)

其中的主要步骤:

· 复制: cp (KDIR)/vmlinux(KDIR)/vmlinux-mt7620a

· 生成dtb文件: (LINUXDIR)/scripts/dtc/dtc?Odtb?o(KDIR)/MT7620a.dtb ../dts/MT7620a.dts

· 将内核与dtb文件合并:(STAGINGDIRHOST)/bin/patch?dtb(KDIR)/vmlinux-mt7620a $(KDIR)/MT7620a.dtb

· 使用lzma压缩:(callCompressLzma,(KDIR)/vmlinux-mt7620a,$(KDIR)/vmlinux-mt7620a.bin.lzma)

· 将lzma压缩后的文件经过mkimage工具处理,即在头部添加uboot可识别的信息。

接下来就是合并生成firmware固件了:

MkImageSysupgrade/squashfs, squashfs, mt7620a,8060928

cat vmlinux-mt7620a.uImage root.squashfs > openwrt-ramips-mt7620-mt7620a-squashfs-sysupgrade.bin

--> 制作squashfs bin文档, 并确认它的大小 <8060928 才是有效的&#xff0c;否则报错。

总结&#xff1a; 整个流程下来&#xff0c;其实最烦索的还是对内核生成文件vmlinux的操作&#xff0c;经过了objcopy, patch-dtb, lzma, mkimage 等过程生成一个uImage&#xff0c;再与mksquashfs工具制作的文件系统rootfs.squashfs合并。

&#61;&#61;&#61;&#61;&#61;未完待续&#61;&#61;&#61;&#61;&#61;&#61;

下一节&#xff1a;(三) 开发定制



推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文详细介绍了在Linux虚拟化部署中进行VLAN配置的方法。首先要确认Linux系统内核是否已经支持VLAN功能,然后配置物理网卡、子网卡和虚拟VLAN网卡的关系。接着介绍了在Linux配置VLAN Trunk的步骤,包括将物理网卡添加到VLAN、检查添加的VLAN虚拟网卡信息以及重启网络服务等。最后,通过验证连通性来确认配置是否成功。 ... [详细]
  • intellij idea的安装与使用(保姆级教程)
    intellijidea的安装与使用(保姆级教程)IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(gi ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • Maven入门、什么是Maven、如何使用Maven、Maven的项目结构、简单的Mavenjava项目、Maven常用命令、Maven项目之间的引用、Maven依赖的传递、可选、排除day01
    目录第一节Maven入门1.1什么是Maven1.2如何使用Maven第一步:下载Maven第二步:配置Maven的环境变量第三步:了解什 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文详细介绍了git常用命令及其操作方法,包括查看、添加、提交、删除、找回等操作,以及如何重置修改文件、抛弃工作区修改、将工作文件提交到本地暂存区、从版本库中删除文件等。同时还介绍了如何从暂存区恢复到工作文件、恢复最近一次提交过的状态,以及如何合并多个操作等。 ... [详细]
  • CentOS7.8下编译muduo库找不到Boost库报错的解决方法
    本文介绍了在CentOS7.8下编译muduo库时出现找不到Boost库报错的问题,并提供了解决方法。文章详细介绍了从Github上下载muduo和muduo-tutorial源代码的步骤,并指导如何编译muduo库。最后,作者提供了陈硕老师的Github链接和muduo库的简介。 ... [详细]
  • Redis API
    安装启动最简启动命令行输入验证动态参数启动配置文件启动常用配置通用命令keysbdsize计算key的总数exists判断是否存在delkeyvalue删除指定的keyvalue成 ... [详细]
  • npminstall-Dbabelcorebabelpreset-envbabelplugin-transform-runtimebabelpolyfillbabel-loader ... [详细]
  • 使用gitolite搭建一个私有的git服务器,来管理git仓库。有了它,就可以跟小伙伴们愉快地进行远程协作啦。今天又折腾了一遍,在这里把几个关键的步骤记下来,方便以后查阅。准备工 ... [详细]
author-avatar
花懋1274238844
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有