欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关注公粽号 《机器和智能》 回复关键词 “python项目实战” 即可获取美哆商城视频资源!
博主介绍:
CSDN博客专家,CSDN优质创作者,CSDN实力新星,CSDN内容合伙人;
阿里云社区专家博主;
华为云社区云享专家;
51CTO社区入驻博主,掘金社区入驻博主,支付宝社区入驻博主,博客园博主。
二、编译内核及内核开发的特点
- 获取内核源码
- 编译内核
- 内核开发的特点
- 图书推荐 - 点击购买:[链接1](https://item.jd.com/14065180.html) | [链接2](http://product.dangdang.com/29599092.html)
🎉🎉🎉🎉🎉 重磅福利 🎉🎉🎉🎉🎉
🎉本次送2套书 ,评论区抽2位小伙伴送书
🎉活动时间:截止到 2024-02-1610:00:00
🎉抽奖方式:评论区随机抽奖。
🎉参与方式:关注博主、点赞、收藏,评论。
❗注意:一定要关注博主,不然中奖后将无效!
🎉通知方式:通过私信联系中奖粉丝。
💡提示:有任何疑问请私信公粽号 《机器和智能》
《精通嵌入式Linux编程》本书的每一章都介绍嵌入式Linux的一个主要领域。它描述知识背景,以便你可以了解一般原则,它还包括详细的有效示例来说明这些领域中的操作。
专栏:《Linux内核设计思想与源码分析》
获取内核源码
在Linux内核官方网站即可下载最新Linux源码
点击跳转
我们一般应该下载最新的稳定版本Linux内核源码进行学习。
源码下载后,通过tar命令进行解压即可
tar xvzf linux-X.X.X.tar.gz
解压后源码会存在linux-X.X.X文件夹中。
内核源码一般都安装在 /usr/src/linux 目录下,但我们开发时不要直接对这个源码树进行开发,因为编译C库所用的内核版本就是该源码树。并且一般不要以root身份修改内核,我们应该自己另外建立一个目录,并以root身份在该目录安装新内核,/usr/src/linux 目录应原封不动的继续存在。
一般以补丁的形式发布对代码的修改,并以补丁的形式接收其他人发布的修改。
内核源码树由很多目录组成,其根目录及描述如下
在内核源码树根目录下还有一些文件,COPYING文件是内核许可证,CREDITS是开发者列表并包含了一些内核代码细节,MAINTAINERS是维护内核子系统和驱动程序的维护者列表,Makefile是编译内核的基础。
编译内核
内核源码在编译时可以进行配置和定制,我们可以把自己需要的功能和驱动程序编译进内核。可以配置的选项以CONFIG_FEATURE形式表示,比如,对称多处理器(SMP)的配置选项为CONFIG_SMP,如果设置了该选项,则SMP启用,否则SMP不起作用。配置选项可以用来决定那些文件编译进内核,选项有二选一和三选一,yes/no/module。在三选一的情况下多了一个 module 选项,如果选择 module 选项,表示该配置项已被选定,但编译的时候这部分功能的实现代码是以模块(一种可以动态安装的独立代码段)的形式生成,而 yes 选项表示把代码编译进主内核映像,而不是作为一个模块。
配置选项也可以是字符串或者整数,这些选项不用于控制编译过程,而是用于指定内核源码可以访问的值,一般以预处理宏的形式表示,比如我们可以通过配置选项指定静态分配数组的大小。
内核提供了很多工具来简化内核配置过程,最简单最耗时的是命令行工具,该工具会遍历所有选项并让你选择配置选项 yes/no/module
make config
还有其他更方便的图形工具可供使用
make menuconfig
make xconfig
make gconfig
make defconfig #创建默认配置
也可以查看并修改配置文件来修改这些配置选项,这些选项存放在内核源码树目录的 config 文件中。
通过如下命令验证并更新配置
make oldconfig
配置完成后,就可以编译内核了,使用命令
make
在编译时,往往会打印很多信息并刷屏,如果不想看到这些信息,可以执行下面命令来编译
make > /dev/null
编译好内核后,就到了安装步骤。安装内核与操作系统体系结构和启动引导工具(boot loader)相关,按照要求将内核映像拷贝到适当位置。这个过程可能需要把某个模块拷贝到指定目录,并且需要编译相应的配置文件,建立启动项等等。
我们可以通过命令来把编译好的模块安装到正确的主目录 /lib 下
make modules install
这个命令需要 root 权限。
编译时还会在内核代码树的根目录下创建一个 System.map 文件,这是一个符号对照表,用来将内核符号和它们的起始地址一一对应,调试时可以把内存地址翻译成函数名或变量名以便于理解。
内核开发的特点
内核编程时不能访问C库,即没有 libc 库。
在用户空间编程时,我们可以调用C库函数,但是在内核编程时,内核无法链接标准C函数库,实际上其他一些库也无法使用。但是大部分C库函数都已经在内核中实现了,只要包含相应头文件就可以调用,比如字符串操作函数库 lib/string.c,头文件为<linux/string.h>。
在内核编程时,所用的头文件都是源码树的内核头文件,内核源码文件不能包含外部头文件。
在内核中也有一些C库函数并没有实现,比如 printf() 函数,但是内核中实现了一个叫 printk() 的函数。printk() 函数负责把格式化好的字符串拷贝到内核日志缓冲区,syslog 程序通过读取该缓冲区来获取内核信息。并且 printk() 函数允许通过指定一个标志来设置优先级,syslog 程序根据这个优先级标志来决定在什么地方显示这条系统消息。
内核编程时必须使用 GNU C 。
我们知道,Linux 内核是使用C语言编写的,但是,内核代码并不完全符合 ANSI C 标准,它用到了 gcc 提供的许多语言扩展部分。gcc 是多种 GNU 编译器的集合,它包含的C编译器既可以编译内核,也可以编译 Linux 系统上的其它C源代码。总之,内核开发者使用的C语言包含 ISO C99 标准以及 GNU C 扩展特性。下面列举内核源码中使用到的一些C语言扩展:
① 内联函数(inline)
内联函数顾名思义,就是“在字里行间展开”的意思,内联函数会在它被调用的位置展开,这样做消除了函数调用和返回所带来的开销,比如寄存器的存储和恢复等。而且,编译器会把调用函数的代码和函数本身放在一起进行优化,这就有了代码进一步优化的可能。当然内联函数也有缺点,那就是会使代码变长,会占用更多的内存空间和指令缓存。
我们通常把一些对时间要求高,且本身代码长度较短的函数定义为内联函数。那些对时间要求不高且被反复调用的函数不要定义为内联函数。
再定义一个内联函数时,通常需要使用 static 关键字,并且需要使用 inline 进行限定。
static inline func(){
;
}
内联函数必须在使用之前就定义好,否则的话编译器无法进行函数展开。在编程时,通常在头文件中定义内联函数(如果内联函数仅在某个源文件中使用,也可以在该文件头部定义内联函数)。由于使用了 static 关键字,编译时不会为内联函数单独建一个函数体。在内核编程时,考虑到类型安全因素,应优先使用内联函数而不是宏。
② 内联汇编
gcc 编译器支持在C函数中嵌入汇编指令,Linux 内核就是用了C和汇编混合编程,在偏近体系结构的底层或对执行时间要求严格的地方,一般都是使用汇编语言编写的。
③ 分支声明
对于条件选择语句,gcc 内建了一条指令用于优化,如果一个条件经常出现或者很少出现,编译器就可以根据这条指令对条件分支进行优化。在内核中,这条指令被封装成了宏,比如 likely() 和 unlikely()。例如下面一个条件选择语句
if(flag){
;
}
如果 flag 为0的概率远远大于它为真的概率,也就是说 flag 这条分支大概率不会发生,我们可以标记为
/* 我们认为flag大概率为0 */
if(unlikely(flag)){
;
}
反之
/* 我们认为flag大概率为1 */
if(likely(flag)){
;
}
这种标记大幅提升性能的前提是你的判断是正确的,如果判断错了可能反而会大幅度降低性能。
内核编程时没有像用户空间那样的内存保护机制。
如果是一个用户程序对内存进行非法访问,那么内核会报错,发送 SIGSEGV 并结束进程。但是,如果是内核自身非法访问内存,那么可能它会直接死掉并且不会报错。内核中的内存错误会导致 oops,这也是内核中出现较多的一类错误,并且内核中的内存不会分页,每用掉一个字节,物理内存就减少一个字节。
内核编程时不要轻易使用浮点数。
如果我们在用户空间进行浮点操作,内核会完成从整数操作到浮点操作的转换。但是在内核中使用浮点数会非常麻烦,这需要你人工保存和恢复浮点寄存器,以及其他一些操作都要人工完成,所以在内核编程时不要使用浮点数。
内核只有一个很小且固定的堆栈。
用户空间栈是非常大的,并且可以动态增长,因此我们可以在用户空间编程时分配大量栈内存。但是内核栈的大小是固定的,它和体系结构有关,在 x86 上,栈的大小在编译时配置,可以是4KB或8KB,一般来说,内核栈的大小是两页,在32位机器内核栈大小为8KB,在64位机器内核栈大小为16KB,这是固定的,每个处理器都有自己的栈。
由于内核支持异步中断、抢占和SMP,所以必须时刻注意同步和并发。
内核是很容易产生竞争条件的,内核的许多特性都要求能够并发的访问共享数据,这就要求有同步机制保证不出现竞争条件。
- Linux是抢占多任务操作系统,内核的进程调度程序即兴对进程进行调度和重新调度,内核必须对这些任务同步。
- Linux内核支持多处理器系统,如果没有保护,在多个处理器上运行的代码很可能会同时访问共享的同一资源。
- 中断是异步到来的,不会考虑正在执行的代码,如果不加保护,中断有可能会在代码访问共享资源时到来,而中断处理程序也有可能会访问该共享资源。
- Linux内核可以抢占,如果不加保护,内核中正在执行的代码可能会被另一段代码抢占,并且这几段代码可能同时访问相同资源。
通常使用自旋锁和信号量来解决竞争问题。
需要考虑可移植性。
Linux是一个可移植的操作系统,也就是说大部分C代码应该与体系结构无关,在各种不同体系结构的计算机上都能编译和执行,这就意味着,必须把体系结构相关的代码从内核代码树的特定目录中分离出来。
图书推荐 - 点击购买:链接1 | 链接2
多年来,Linux一直是嵌入式计算的中流砥柱。然而,涵盖该领域所有主题的书籍非常少,本书旨在填补这一空白。“嵌入式Linux”一词的定义并不明确,它可以应用于从恒温器到Wi-Fi路由器,再到工业控制单元的各种设备内的操作系统。但是,它们都建立在相同的基本开源软件之上。这些正是我们在本书中要描述的技术,本书的写作基于我们作为工程师的经验和为培训课程开发的资料。
书名:《精通嵌入式Linux编程》
作者:弗兰克·瓦斯奎兹
出版社:清华大学出版社
内容简介:《精通嵌入式Linux编程》详细阐述了与嵌入式Linux开发相关的基本解决方案,主要包括初识嵌入式Linux开发、关于工具链、引导加载程序详解、配置和构建内核、构建根文件系统、选择构建系统、使用Yocto进行开发、Yocto技术内幕、创建存储策略、现场更新软件、连接设备驱动程序、使用分线板进行原型设计、init程序、使用BusyBox runit启动、管理电源、打包Python程序、了解进程和线程、管理内存、使用GDB进行调试、性能分析和跟踪、实时编程等内容。此外,本书还提供了相应的示例、代码,以帮助读者进一步理解相关方案的实现过程。 本书适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学用书和参考手册。
多年来,Linux一直是嵌入式计算的中流砥柱。然而,涵盖该领域所有主题的书籍非常少,本书旨在填补这一空白。“嵌入式Linux”一词的定义并不明确,它可以应用于从恒温器到Wi-Fi路由器,再到工业控制单元的各种设备内的操作系统。但是,它们都建立在相同的基本开源软件之上。这些正是我们在本书中要描述的技术,本书的写作基于我们作为工程师的经验和为培训课程开发的资料。
技术不会停滞不前。基于嵌入式计算的行业与主流计算一样容易受到摩尔定律的影响。这种指数级的增长意味着自本书第一版出版以来发生了惊人的大量变化。你现在正在阅读的第三版经过全面修订,使用最新版本的主要开源组件,包括Linux 5.4、Yocto Project 3.1 Dunfell和Buildroot 2020.02 LTS。除了Autotools,本书还包含CMake,这是一种新构建系统,近年来得到了越来越多的采用。
本书大致按照你在实际项目中遇到的顺序来涵盖这些主题。第1篇包括第1~8章,涉及项目的早期阶段。本篇涵盖选择工具链、引导加载程序和内核等基础知识。我们以Buildroot和Yocto项目为例介绍嵌入式构建系统的概念。本篇以对Yocto Project的新深入研究结束。
❗❗❗重要❗❗❗☞关注下方公粽号 《机器和智能》 回复关键词 “python项目实战” 即可获取美哆商城视频资源!