全网仅此一篇!基于17.01.4版本的OpenWrt源码全面解析

一、顶层目录

目 录含 义
config编译选项配置文件,包含全局编译设置、开发人员编译设置、目标文件格式设置和内核 编译设置等 4 部分
include包含准备环境脚本、下载补丁脚本、编译 Makefile 以及编译指令等
package各种功能的软件包,软件包仅包含 Makefile 和修改补丁及配置文件。其中 Makefile 包含 源代码真正的地址及 MD5 值。OpenWrt 社区的修改代码以补丁包形式管理,package 只 保存一些常用的软件包
scripts包含准备环境脚本、下载补丁脚本、编译 Makefile 以及编译指令等
target指的是嵌入式平台,包括特定嵌入式平台的内容
toolchain编译器和C库等(交叉编译工具),例如包含编译工具gcc和glibc库
tools通用命令/工具,用来生成固件的辅助工具,如打补丁工具patch、编译工具make及squashfs等

1.1:config目录

  • 目录下存放的是编译配置文件,是OpenWrt 15.05的新增目录,是将一些编译选项配置文件放在此处,包含全局编译设置、开发人员编译设置、目标文件格式设置和内核编译设置等4部分。
  • 编译源码时,输入“make defconfig”命令,这个目录下的配置文件会被集中读取并生成一个“.config”配置文件,该文件在下面会介绍。

包含文件

  • Config-build.in:最基本的配置文件(全局编译)。
  • Config-devel.in:用于开发的编译配置文件。
  • Config-images.in:目标文件格式设置(生成某种镜像的配置文件)。
  • Config-kernel.in:关于内核编译的配置文件。

.in配置文件的内容与格式

  • 下面我们以“Config-build.in”格式为例。

  • menu:我们在目录下输入“make menuconfig”之后显示的主菜单项,例如上面我们的menu为“Global build settings”,在主菜单项中可以看到一个选项为“Global build settings”的内容。(备注:menu以endmenu结尾)。

  • config:config下的bool的内容为我们回车进入某一个菜单项之中显示的子项。但是如果一个config的上一级没有menu项,那么config下的bool内容直接显示在主菜单项中(例如下面的"Advanced configuration options (for developers)"项上一级不是menu,那么其直接显示在主菜单中)。

  • default:代表默认值,如果为y代表是默认选择的,如果为n代表默认不选择。
  • help:这个选项的帮助解释说明。
  • 例如下面我们有一个"Compile with support for patented functionality"子选项,其default为y,所以前面有“*”号为默认选择。当我们在下面选择“help”然后回车,可以看到此子选项的注释。

增加自定义模块

  • 根据.in文件的格式,我们也可以自己设置菜单项和内容,然后在编译时使用。
  • 例如:下面我们在target/Config-buidl.in配置文件中加上下面内容(一个“TEST”主选项,一个“This is my test”子选项,子选项默认值为“y”,帮助内容为“dongshao CSDN url: https://me.csdn.net/qq_41453285”)。

  • 接着来到来到顶级目录下,输入“make menuconfig”进入配置工具选项菜单,可以看到我们修改的内容。

  • 进入“TEST”选项。

  • 选择下方的Help查看帮助信息。

1.2:include目录

  • 保存各种makefile文件:

.mk脚本在编译时的作用

  • 在顶层目录下有一个“Makefile”脚本,当我们在编译源码时,输入make命令,“Makefile”脚本被执行,紧接着.mk脚本会被顶级目录下的Makefile调用,然后这些.mk脚本会解析“.config”文件(.config文件下面有介绍),并且根据“.config”文件的内容来编译相关的内容(例如下载、编译安装packages目录下的第三方软件等)。

1.3:package目录

  • 存放各种必要的软件包,可以供二次开放使用。
  • 当我们在顶级目录下输入“./script/feeds update -a、./script/feeds install -a”命令时更新和下载的就是这个目录下的软件。
  • 特性:我们可以根据自己的需求,在这个目录下新建目录(根据软件类型存放在对应的目录下),然后编写程序和Makefile脚本,这样就可以编译生成自己想要的软件了。

目录结构

  • 目录结构分为:功能目录-->软件目录。例如下面以“devel”这个功能模块为例,其目录下有“gdb、perf、strace”这些软件。

Makefile脚本

  • 在每一个软件的目录下,都有一个Makefile文件。这个Makefile脚本会在编译源码时被执行(何时执行?include目录下的.mk脚本执行时,.mk脚本解析.config脚本,.config脚本中记录着软件的名称以及是否要安装“y/n”,如果某个软件选项为“y”,那么软件的Makefile脚本会被执行,于是这个软件就会被下载安装;如果为“n”,那么就不会被安装)。

  • 小面我们以gdb这个软件为例,其目录下有一个Makefile文件,Makefile内有软件的名称、软件版本、要下载的包文件名、下载的软件的名称、软件包的HASH算法,软件的名称下载地址。

1.4:scripts目录

  • 目录scripts为编译工具脚本文件。

一些比较重要的脚本及功能

  • 例如patch-kernel.sh封装了patch命令,在编译时,首先将 patches 目录下的所有补丁文件打上,并且判断如果打补丁失败将退出编译过程。
  • download.pl 为下载源代码的工具脚本,封装下载工具 wget 的选项以及设置从哪里下载。
  • 简述如下:
脚 本 文 件含 义
scripts/download.pl下载编译软件包源代码
scripts/patch-kernel.sh打补丁脚本,并且判断如果打补丁失败将退出编译过程
scripts/feeds收集扩展软件包的工具,用于下载和安装编译扩展软件包工具
scripts/diffconfig.sh收集和默认配置不同之处的工具
scripts/kconfig.pl处理内核配置
scripts/deptest.sh自动 OpenWrt 的软件包依赖项检查
scripts/metadata.pl检查 metadata
scritps/rstrp.sh丢弃目标文件中的符号,这样就将执行文件和动态库变小
scripts/timestamp.pl生成文件的时间戳
scripts/ipkg-make-index.sh生成软件包的 ipkg 索引,在使用 opkg 安装软件时使用
scripts/ext-toolchain.sh工具链
scripts/strip-kmod.sh删除内核模块的符号信息,使文件变小

1.5:target目录

  • 存放用于编译各类平台使用的二进制文件,定义了各类平台编译固件和内核的具体过程。
  • 是指目标嵌入式设备,针对不同的平台有不同的特性代码。

  • 例如下面的“target/linux”目录是对Linux系统的划分,目录下包括Linux系统针对于各种平台标准内核的不订及特殊配置(如ar7、arm系统)。

1.6:tools、toolchain目录

tools目录

  • 是一些通用命令/工具,用来生成固件的辅助工具,如打补丁工具patch、编译工具make及squashfs等。
  • 与package目录结构类似,每个工具占一个目录,工具目录下都有该工具下载所对应的Makefile。

toolchain目录

  • 与tools差不多,也是一些工具,编译器和C库等(交叉编译工具),例如包含编译工具gcc和glibc库,在嵌入式交叉编译时有用。
  • 与package目录结构类似,每个工具占一个目录,工具目录下都有该工具下载所对应的Makefile。

1.7:顶级目录Makefile

  • 进行编译的总体工作。
  • 自己开发时,这个Makefile不需要改动,因此这个Makefile对我们也不重要,只需要大致了解一下即可。

Makefile的world目标

  • 执行make时,如果不指定任何目标,因为world目标处于第一位,所以默认执行world。

  • 在make时不指定OPENWRT_BUILD参数时,进入ifneq语句,如果编译时make OPENWRT_BUILD=1则进入else。

  • 进入ifneq语句:
    • 重写OPENWRT_BUILD变量并export导出OPENWRT_BUILD变量。
    • 包含仅顶级目录下include目录下的debug.mk、depends.mk、toplevel.mk。
    • debug.mk:在编译过程中各类信息的输出 (V=s参数用到)。
    • depends.m:检查当前系统在编译内核阶段所有需要依赖的包是否安装。
    • toplevel.mk:解析编译world目标的规则。

  • toplevel.mk:主要为了生成下面两个目标(prereq和world),在toplevel.mk180行开始。
prereq:: prepare-tmpinfo .config
	@make -r -s tmp/.prereq-build 
	@make V=ss -r -s 

prereq %:
	@make V=ss -r -s
	@make -w -r world 

Makefile其他目标

  • “make clean”:删除编译目录。
  • “make dirclean”:除了删除编译目录之外还删除编译工具目录。
  • “make printdb”:输出所有的编译变量定义。

1.8:feeds.conf.default、feeds.conf文件​​​​​​

  • 这两个是我们自己添加进去的文件。
  • script/feeds脚本文件会调用这两个配置文件,配置文件中定义了大量第三方软件包的下载地址(使用了国内的镜像源,下载速度更快),下图是script/feeds脚本文件,可以看到其调用了这两个配置文件。

配置文件支持的语法:

  • src-git:通过git的方式从后面的链接进行下载。
  • src-cpy:通过path进行拷贝 (通过U盘更新等)。
  • src-bzr:通过bzr的方式从后面的链接进行下载 。
  • src-link:创建一个数据源path的symlink。
  • src-svn:通过svn的方式从后面的链接进行下载。
演示案例
src-git telephony https://git.lede-project.org/feed/telephony.git^ac6415e61f147a6892fd2785337aec93ddc68fa9

二、编译时创建的临时目录

  • 编译工具链、目标平台的软件包等需要下载的文件都放在dl目录下。目标平台和软件包两部分都需要“build_dir/”作为编译的临时目录,并且会将目录 staging_dir作为编译的临时安装目录,最终的生成文件保存在目录bin下。
目 录含 义
dl下载软件代码包临时目录。编译前,将原始的软件代码包下载到该目录
feeds扩展软件包目录。将一些不常用的软件包放在其他代码库中,通过feed机制可以自定义下载及配置
bin编译完成后的最终成果目录。例如安装映像文件及 ipk 安装包
build_dir编译中间文件目录。例如生成的.o 文件
staging_dir编译安装目录。文件安装到这里,并由这里的文件生成最终的编译成果
log如果打开了针对开发人员 log 选项,则将编译 log 保存在这个目录下,否则该目录并不会创建
tmp编译过程的大量临时文件都会在此

2.1:feeds目录

  • 在OpenWrt固件中,几乎所有东西都是软件包(package),可以编译为以“.ipk”结尾的安装包,这样就可以很方便地安装、升级和卸载了。注意,扩展软件包不是在主分支中维护的,但是可以使用软件包编译扩展机制(feeds)来进行扩展安装。这些包能够扩展基本系统的功能,只需要将它们链接进入主干。之后,这些软件包将会显示在编译配置菜单中。
  • 目录feeds用于保存扩展软件包,可以使用软件包编译扩展机制来进行扩展安装。这些包能够扩展基本系统的功能,只需要将它们链接进入编译主目录的package目录下。 之后,这些软件包将会显示在配置菜单中。
  • ./sripts/feeds install -a时,feeds目录就产生了,安装的软件就存放在这个目录下了。

2.2dl目录

  • 编译工具链、目标平台的软件包等需要下载的文件都放在dl目录下。
  • 在编译过程中,各类需要下载的包都保存在这个目录下 (编译过程中用的工具)。
  • 当编译的过程中,如果出错,出错的原因是某个软件包下载错误或丢失,可以手动下载对应的软件包(压缩文件形式),并放在这个目录下,之后重新编译。

dl目录与feeds目录的区别

  • dl中存放的是编译过程中需要用到的工具,而feeds中存放的是系统编译好之后在系统中需要用的软件。

2.3:build_dir目录

  • 交叉编译工具的编译中间文件目录。例如生成的.o文件

“build_dir/host”目录

  • “build_dir/host”是一个临时目录,用来储存不依赖于目标平台的工具。
  • tools目录中各类工具编译的结果存放在host中,因此可以看到这些目录的名称与tools下的工具名称相同。例如进入一个软件目录,看到其编译结果如下:

“build_dir/toolchain-*”目录

  • 用来储存依赖于指定平台的编译工具链。
  • tools-chain目录交叉编译工具最终编译的结果文件。

“build_dir/<arch>”目录

  • 目标平台和软件包两部分都需要“build_dir/”作为编译的临时目录。

2.4:staging_dir目录

  • staging_dir作为编译的临时安装目录

“staging_dir/toolchain-*”目录

  • 是编译工具链的最终安装位置。
  • 通常我们不需要改动编译链目录下的任何东西,除非要更新编译工具版本等。

tools、toolchain、build_dir、staging_dir四者的关系

  • tools、toolchain目录中的编译中间文件存放在buidl_dir目录下。例如生成的.o 文件等。
  • buidl_dir目录存放的软件编译文件,最终安装在staging_dir目录下,因此staging_dir目录为编译安装目录,文件安装到staging_dir目录,并由staging_dir目录的文件生成最终的编译成果。
  • 所以:流程是:tools、toolchain==>编译到build_dir中==>安装到staging_dir中。

2.5:bin目录

  • 编译完成后的最终成果目录。例如安装映像文件及ipk安装包。

packages目录

  • 这个目录下主要保存我们编译生成的系统软件包(.ipk)。
  • 我们进入packages目录。

  • 在进入i386_pentium4目录,可以看到有软件包的分类目录。

  • 进入base软件包目录(一些系统基础的软件),进入之后可以看到很多我们在编译时选择的很多软件生成的软件包。

  • 进入luci软件包目录,我们在编译时选择了生成luci软件,可以看到目录下一些的luci软件包。

  • 进入packages目录,其中也有一些文件。

targets目录

  • 这个目录保存我们的系统镜像文件(按照系统的类别分类)。
  • 我们在make menuconfig时选择了x86平台,就进入targets目录之后就可以看到一个属于x86平台的目录。

2.6:log目录

  • 如果打开了针对开发人员 log 选项,则将编译log保存在这个目录下,否则该目录并不会创建。

2.7:tmp目录

  • 编译过程的大量临时文件都会在此。

三、附加知识

.config配置文件

  • 编译源码时,输入“make defconfig”之后会生成一个“.config”文件,内容大致如下:

  • 文件的内容是什么,从何处来?这个.config会读取config目录下的4个配置文件(Config-build.in、Config-devel.in、Config-images.in、Config-kernel.in),根据.in文件中的设置生成对应的“选项”与选项对应的“内容/值”。
  • 例如上面图中我们.config文件读取的“Config-Images.in”文件的内容中,“CONFIG_TARGET_ROOTFS_EXT4FS”设置为“y”,“CONFIG_TARGET_EXT4_RESERVED_PCT”被设置为“0”,都是读取“Config-Images.in”文件获取的(下图是Config-Images.in的内容)。

  • 该文件何时被用到?当我们在编译源码时,输入“make menuconfig”进入配置工具选项菜单,此时就会读取这个“.config”文件,并根据“.config”文件的内容设置选项的默认值。例如上面的“.config”文件中“ext4”选项默认为“y”,配置选项菜单时“ext4”前面默认有个“*”号就代表被选中;“ext4”选项的子选项"Percentage of reserved blocks in root filesystem"默认值为“0”,可以看到其默认值就为“0”。

  • .config内容的修改:上面说过配置工具选项菜单会读取.config的内容,因此在配置工具选项菜单中修改的内容保存也是在这个配置文件中。例如上面将上面的那个“ext4”选项取消,可以看到在右侧的“.config”文件中“Root filesystem images”下方的那3个选项消失了。

“scripts/download.pl”下载工具

  • OpenWrt在构建时首先下载代码,就是使用 scripts/download.pl 脚本进行下载。
  • 这个下载功能最重要的接口是我们可以通过“scripts/localmirrors”文件自定义软件包 下载地址,方便开发人员进行设置。
  • 最近有很多 iPhone/Android 编译工具爆出后门问题,就是因为使用其他第三方镜像地 址文件来下载编译工具,但没有对下载的软件内容进行 MD5 值对比,从而导致编译的应 用程序感染后门。OpenWrt 的下载检查机制从源头上解决了这类问题。在我开发 OpenWrt 时也发现了下载的一些内容被感染的问题,但检查机制丢弃了不正确的内容,从下一个的 镜像网站上继续下载。

使用方法如下:

 ./download.pl <target dir> <filename> <md5sum> [<mirror> ...]
  • <target dir>:为下载之后的保存位置,下载代码通常均保存在 dl 目录下。
  • <filename>:待下载的文件名。
  • <md5sum>:下载内容的 MD5,用于校验下载文件是否正确。
  • <mirror>:为可选的参数,是下载文件的镜像地址,可以有多个地址,优先选择第一个, 如果下载失败则顺序选择后面的地址。

代码解析:

  • 该程序由 Perl 语言开发出来,代码并不复杂。代码首先进行初始条件检查,判断参数 是否足够,至少需要 3 个参数分别为下载文件保存位置、下载文件名及下载内容 MD5 值。

  • 接着从命令行参数中顺序读取数据,并赋值给局部变量,最后判断 md5sum 或 md5 工具是否存在,如果不存在提示工具不存在后退出。
  • 紧接着调用 localmirrors()函数读取本地的源码镜像地址,我们可以在企业内部创建自 己的代码镜像服务器,然后将镜像地址放在“scripts/localmirrors”文件中,这样我们就不 用每次编译时都从互联网上去下载了。例如我这里修改如下:
cat localmirrors
http://192.168.1.106:8080/openwrt/
http://mirror.bjtu.edu.cn/gnu/

  • 紧接着遍历命令行并将代码中的镜像地址加到备选镜像中。

  • 最后使用 while 循环进行下载,如果下载完成就对下载文件的 MD5 进行对比,如果 MD5 值一致则退出循环,否则 进入下一个镜像地址进行下载。下载成功后调用 cleanup()函数来清理临时变量。

“patch-kernel.sh”脚本

  • OpenWrt 的代码包中大多均有 patches 目录。下载代码包完成后进行打补丁,采用的 就是 patck-kernel.sh 脚本。
  • 脚本的第一个参数为编译代码目录,第二个为补丁目录。
  1. 调用脚本形式举例如下:
../scripts/patch-kernel.sh iproute2-3.3.0 ../package/iproute2/patches/
  • 上述命令执行流程如下:
    • ①首先进行参数赋值,第一个参数为代码目录,第二个参数为补丁目录
    • ②第二步判定代码目录和补丁目录是否存在,如果不存在则提示错误并退出。
    • ③遍历补丁文件,根据后缀判断补丁文件类型。
    • ④调用 patch 命令应用补丁
    • ⑤检查补丁应用是否正确,如果存在“*.rej”文件表示出现错误,返回“1”并退出。
    • ⑥最后检查如果存在应用补丁后的备份文件,则删除备份文件

编译扩展机制feeds

  • 传统的 Linux 操作系统在编译某一个软件的时候,会检查其依赖软件及头文件是否存在,如果没有安装,则会报缺少头文件或缺少链接库等错误,编译将退出。这种机制使得 开发者在编译一个软件之前,需要查找该软件所需的依赖库及头文件,并手动去安装这些 软件。有时候碰到比较娇贵的软件时,嵌套式的安装依赖文件,会使得开发者头昏脑胀。 OpenWrt 通过引入 feeds 机制,很好地解决了这个问题。
  • feeds 是OpenWrt开发所需要的软件包套件的工具及更新地址集合,这些软件包通过 一个统一的接口地址进行访问。这样用户可以不用关心扩展包的存储位置,可以减少扩展 软件包和核心代码部分的耦合。它由两部分组成,即扩展包位置配置文件 feeds.conf.default 和脚本工具 feeds。

目前在配置文件中保存最重要的扩展软件包集合有以下4个:

  • ‘LuCI’OpenWrt 默认的 Web 浏览器图形用户接口。
  • ‘routing’一些额外的基础路由器特性软件,包含动态路由 Quagga 等。
  • ‘telephony’IP 电话相关的软件包,例如 freeswitch 和 Asterisk 等。
  • ‘management’TR069 等各种管理软件包。

在前面编译OpenWrt源码之前,我们做了如下的操作:

  • 上述操作,就是利用 feeds 提供的接口将 OpenWrt 所需的全部扩展软件包进行下载并 安装。在更新时,需要能够访问互联网。在下载之前可以通过查看“feeds.conf.default”文 件,来检查哪些文件需要包含在编译环境中。
./scripts/feeds update –a

./scripts/feeds install -a

feeds工具用法如下:

  • update:下载在 feeds.conf 或 feeds.conf.default 文件中的软件包列表并创建索引。-a 表 示更新所有的软件包。只有更新后才能进行后面的操作
  • list:从创建的索引文件“feed.index”中读取列表并显示。只有进行更新之后才能查 看列表
  • install:安装软件包以及它所依赖的软件包,从 feeds 目录安装到 package 目录,即在 “package/feeds”目录创建软件包的软链接。只有安装之后,在后面执行“make menuconfig” 时,才可以对相关软件包是否编译进行选择
例如安装 luci-app-firewall:

./scripts/feeds install luci-app-firewall
  • search:按照给定的字符串来查找软件包,需要传入一个字符串参数。
  • uninstall:卸载软件包,但它没有处理依赖关系,仅仅删除本软件包的软链接。
  • clean:删除 update 命令下载和生成的索引文件,但不会删除 install 创建的链接。

feeds代码处理流程:

  • 这个命令首先读取并解析feeds.conf 配置文件,然 后执行相应的命令,例如 install 时,将安装应用程序包和它所有直接或间接依赖的所有软件包。安装时将创建一个符号链接,从 packages/feeds/$feed_name/$package_name指feeds/$feed_name/$package_name, 这样在“make menuconfig”时,feeds 的软件包就可 以被处理到,就可以选择编译了。
  • 例如“luci-app-firewall”指向“feeds/luci/applications/luciapp-firewall”:
ls package/feeds/luci/luci-app-firewall -alht

相关配置文件:

  • 用一句话来说,编译扩展安装过程就是将feeds目录下的软件包链接到packages/feeds对应目录下。可使用的feeds列表配置为feeds.conf或者 feeds.conf.default。优先选择feeds.conf 文件,这个文件包含了扩展安装源列表,每一行由3部分组成,包含feed方法、 feed名字和feed源。
  • 下面是一个扩展安装源配置文件的例子:
src-git luci https://github.com/openwrt/luci.git;for-15.05

src-git routing https://github.com/openwrt-routing/packages.git;for15.05

src-git telephony https://github.com/openwrt/telephony.git;for-15.05

src-gitmanagement https://github.com/openwrt-management/packages.git;for-15.05
  • 我们可以修改该文件使编译时从自己指定的位置进行下载。主要支持feed方法的类型有以下3种:
    • src-cpy:通过从数据源路径复制数据。
    • src-git:通过使用 Git 从代码仓库地址下载代码数据。
    • src-svn:通过使用 SVN 从代码仓库地址下载代码数据。

  • 我是小董,V公众点击"笔记白嫖"解锁更多OpenWrt资料内容。

  • 21
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值