笔记 嵌入式C语言自我修养

(一)Git使用(代码版本工具)

Git 教程 | 菜鸟教程 (runoob.com)

*版本穿梭*

git版本穿梭指的是可以在提交了多个版本的文件中自由的切换。

基本语法

git reset --hard 版本号

*代码演示*

#使用命令切换到第一个提交的版本文件中去。

Bear@Xans MINGW64 /d/demo (master)

$ git reflog

c430c84 (HEAD -> master) HEAD@{0}: commit: second commit # 第二次提交的版本

a0f668a HEAD@{1}: commit (initial): my first commit # 第一次提交的文件版本

Bear@Xans MINGW64 /d/demo (master)

$ git reset --hard a0f668a # 穿梭回到第一次提交的版本

HEAD is now at a0f668a my first commit

Bear@Xans MINGW64 /d/demo (master)

$ git reflog

a0f668a (HEAD -> master) HEAD@{0}: reset: moving to a0f668a

c430c84 HEAD@{1}: commit: second commit

a0f668a (HEAD -> master) HEAD@{2}: commit (initial): my first commit # 指针已经指向了第一次提交的版本

Bear@Xans MINGW64 /d/demo (master)

$ cat 1.txt # 此时查看文件内容,发现已经切换到第一个版本文件添加的内容中去了。

Hello China

Hello China

Hello China

Git 切换版本,底层其实是移动的 HEAD 指针,具体原理如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mTTa8Y6I-1691918091377)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps28.jpg)]

(二)CPU的缓存(Cache)机制

*1. 时间局部性:*

如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。

----被引用过一次的存储器位置在未来会被多次引用(通常在循环中)。

*2. 空间局部性:*

一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。

----如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。

Cache 的工作原理:利用空间局部性和时间局部性原理,通过自有的存储空间,缓存一部分内存中的指令和数据,*减少CPU访问内存的次数*,从而提高系统的整体性能

1.为什么有些处理器没有Cache?

①这些处理器低功耗低成本,增加Cache会增加芯片面积和发热量

②工作频率不用高

③Cache无法保证实时性

(三)CPU访问内存或外部设备

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VDkAqHhJ-1691918091379)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps29.jpg)]

(四)地址的本质

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1IPBLh6B-1691918091379)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps30.jpg)]

地址的本质就是由CPU管脚发出的一组地址控制信号

(五)一些定义:

1.MMU

Memory Management Unit的缩写,中文名是内存管理单元,有时称作****分页内存管理单元*(英语:*paged memory management unit*,缩写为*PMMU****)。它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件

2.译码器

译码器(decoder)是一类多输入多输出组合逻辑电路器件,其可以分为:变量译码和显示译码两类。 变量译码器一般是一种较少输入变为较多输出的器件,常见的有n线-2^n线译码和8421BCD码译码两类;显示译码器用来将二进制数转换成对应的七段码,一般其可分为驱动LED和驱动LCD两类。

3.总线

总线是各种数字信号的集合,包含地址信号、数据信号、控制信号等。

不同的总线之间通过桥连接。

(1)两种编址方式:** **统一编址:内存RAM和外部设备共享CPU的可寻址空间

独立编址:内存RAM和外部设备分别占用CPU不同的可寻址空间

4.

桥一般是芯片组电路,用来将总线的电子信号翻译成另一种总线的电子信号

5.指令集

指令集是个虚的东西,是标准规范,就像红绿灯规则

CPU支持的有限个指令的集合

指令集最终的实现就是微架构,就是CPU内部的各种译码和执行电路

指令集作为CPU和编译器的设计规范和参考标准,主要用来定义指令的格式、操作数的类型、寄存器的分配、地址的格式等

几大指令集:x86、ARM、RISC-V

6.微架构

指令集在CPU处理器内部的具体硬件电路的实现,称为微架构

一套相同的指令集,可由不同形式的电路实现,有不同的微架构

微架构一般称为CPU内核

7.EABI

EABI中的E,表示“Embedded”,即嵌入式应用二进制接口,是一种新的ABI。EABI支持软件浮点和硬件实现浮点功能混用,系统调用的效率更高,和今后的工具更兼容,软件浮点的情况下,EABI的软件浮点的效率要比OABI高很多。

(六)ARM 汇编

1.GNU ARM 汇编参考文档

顶部(用作) (sourceware.org)

2.ARM汇编指令

ARM汇编指令集汇总_arm汇编语言指令大全_Saint-000的博客-CSDN博客

ARM指令集【 PUSH \POP】【跳转B\BL\BX\BLX \BXJ】【数据操作LDR\LDRB\LDRH\LDM\STR\STRB\STRH\STM】【移位LSL/LSR/ASL/ASR】_push {r3,lr}_城东的博客-CSDN博客

汇编指令-跳转指令B BEQ BNE BCC_汇编beq_瑞欧莱的博客-CSDN博客

[arm-汇编stmdb、ldmia、stmfd、ldmfd] - 简书 (jianshu.com)

arm汇编指令探究之 ldmia_半瓶醋的历史的博客-CSDN博客

Blo

ARM汇编(2)_arm blo_bleeoom的博客-CSDN博客

Movw和movt

ARM base instruction – movw and movt-CSDN博客

3.ARM 寄存器

寄存器属于计算机的中央处理器(CPU)部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjqjDiIT-1691918091380)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps31.jpg)]

4.objcopy命令

将一个文件的内容复制到另一个目标文件中,对目标文件实行格式转换

img

可执行文件内部结构

img

.symtab 符号表

.debug 用来保存可执行文件中每一条二进制指令对应的源码位置信息(根据这些信息,GDB调试器才可执行源码级调试)

BSS段不占用空间

.init 代码来自C运行库的一些汇编代码。用来初始化C运行所依赖的环境,如堆栈初始化

.text(代码段) .rodata(只读数据段) .bss(放置未初始化的全局变量和静态变量) .data(数据段)

字符串常量放置 .rodata

BSS段设计的初衷是为了减少文件体积,节省磁盘空间。

BSS段和数据段的唯一差异:在可执行文件内不给bss段分配存储空间,在程序运行内存时再分配存储空间和地址

5.编译流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mzPUNeN-1691918091381)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps34.jpg)]

(1)强、弱符号

强:函数名、初始化的全局变量

弱:未初始化的全局变量

各个文件定义的全局变量变量名相同,发生符号冲突,可执行文件到底执行哪一个?

决议规则

一山不容二虎

强弱可以共存 (强弱共存,强覆盖弱)

体积大者胜出

(七)程序的运行

1.操作系统下程序运行

通过加载器将程序的指令加载到内存中,然后CPU到内存中取指令运行

2.一些概念、函数

Fork

Linux系统——fork()函数详解(看这一篇就够了!!!)_fork()用于_代码拌饭饭更香的博客-CSDN博客

用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化;(Process Control Block,进程控制块)

物理地址、虚拟地址

虚拟地址关系到进程的用户空间和内核空间,而物理地址则用来寻址实际可用的内存

虚拟地址到物理地址的转换通常需要以下几个步骤:
\1. 获取页表项:操作系统会通过虚拟地址中的页号去查找相应的页表项。页表项中存储了该虚拟地址对应的物理页号以及其他的信息。
\2. 计算物理地址:通过得到的页表项中的物理页号和虚拟地址中的页内偏移量,就可以计算出实际的物理地址,也就是内存中的实际硬件地址。
\3. 访问物理地址:使用计算出的物理地址可以直接访问内存中的数据,完成读取或者写入操作。
通过上述三个步骤,虚拟地址就被成功转换成了对应的物理地址。在实际情况中,还需要考虑多级页表、虚拟内存的分页、地址空间的映射等复杂情况。而在硬件的实现中,还有诸如TLB等优化设计可以提高虚实地址转换的效率。

相同的虚拟地址经过MMU硬件转换后,会分别映射到物理内存的不同区域

进程管理、调度和运行原理

对于每一个运行的进程,Linux内核会使用一个task_struct结构体来表示,多个结构体通过指针构成链表。操作系统基于链表对进程进行管理、调度和运行。

不同进程的代码段和数据段分别存在物理内存不同的物理页上。

NAND/NOR分区

NAND分区是指将储存空间划分为块,每个块由多个页组成。这种分区方式适用于存储大量数据和文件的闪存设备中,如固态硬盘和USB闪存驱动器。

NOR分区是将储存空间按字节地址划分为块,每个块包含多个字节。这种分区方式适用于需要频繁更改的应用程序,如嵌入式系统或移动设备。它还可以用于存储引导代码或操作系统映像等特殊用途。

3.裸机环境下的程序运行

裸机平台下,系统商店后,没有程序运行的环境,需要借助第三方工具将程序加载到内存

如(JTAG)

4.程序入口main()函数分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-awiJeBqd-1691918091382)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps35.jpg)]

(八)链接(自制库)

1.链接静态库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZ6a9PhI-1691918091383)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps36.jpg)]

Linux ar命令 | 菜鸟教程 (runoob.com)

链接静态库会出现一个问题,当我们在源文件定义了100个函数,却只用了一个,这样会使可执行文件体积大大增加

img

解决方法:一个源文件定义一个函数

但是,当很多程序调用函数(如printf),会导致大量重复的printf代码指令,浪费内存资源。

这时候,动态链接登场了!

2.动态链接库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STweGszW-1691918091385)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps38.jpg)]

除了默认路径,还可用户指定路径去查找库

用户可以在/etc/ld.so.conf中添加自己的共享库路径,修改后可以使用ldconfig命令生成缓存/etc/ld.so.chche以提高查找效率。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XzHJOHrZ-1691918091386)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps39.jpg)]

动态链接器本身也是一个动态库,即/lib/ld-linux.so文件

它会先给自己重定位,然后在运行。(自举)

将动态库设计成与地址无关的代码,可实现多进程之间对库的共享

与地址无关,利用相对寻址来实现

img

每个应用程序将引用的动态库(绝对地址)符号收集起来,保存在GOT表,记录各符号的地址。GOT表(全局偏移表)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l9uhPt9m-1691918091387)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps41.jpg)]

在使用延迟绑定时,程序不会在程序启动时加载所有的链接库,而是等到需要时才加载链接库。这样可以减少程序启动时的加载时间和程序的内存占用,提高程序的响应速度和执行效率。同时,如果在程序运行时未使用某些链接库,则可以避免浪费资源。

显示加载引用动态库

img

Makefile

[ Makefile中.PHONY伪目标的作用(完整)_伪目标 .phony_tilblackout的博客-CSDN博客](#:~:text=Makefile中.PHONY伪目标的作用 (完整) 1 第一种情况 :避免命令与目录下的文件名重复,2 第二种情况 :make的并行和递归执行过程中 3 第三种情况 :生成多个应用程序)

避免命令与当前目录文件重名

(九)Linux内核模块

内核模块的运行不同于应用程序,不依赖C标准库,动态链接和重定位过程需要内核自己来完成。运行于内核空间;模块加载由系统调用init_module完成。

1.内核模块的加载流程

img

2.U-boot重定位分析

(1)一些概念、问题

CPU的SVC模式:

SVC (Supervisor Call) 模式是 ARM 处理器的一个特权级别,可以让操作系统内核执行一些特定的操作,例如进程切换、I/O 操作等。在 SVC 模式下,CPU 可以访问一些特殊的寄存器,包括 SPSR (Saved Program Status Register) 和 LR (Link Register),用于保存当前的程序状态以及调用 SVC 的返回地址。操作系统通常会通过一个特定的指令 (SWI) 将 CPU 切换到 SVC 模式。

SDRAM

SDRAM是同步动态随机存取存储器,是计算机主板上常用的一种内存类型。它是一种具有高速度、大容量、低功耗、高可靠性的存储器,能够提高计算机系统的工作效率和应用性能。与其它存储器相比,SDRAM采用同步技术,即输出数据的时钟与输入数据时钟同步,因此其读写速度更快。同时,它还具有自刷新功能,可以自动刷新存储器芯片中的存储信息,保证数据不会丢失。目前主流的DDR SDRAM已经普及,其速度和容量已经大幅提升。

系统上电复位为什么要关闭Cache和MMU?

在系统上电复位时,部分内存中的数据可能会变得不可用或无效,导致CPU Cache和MMU中缓存的数据与当前内存中的实际数据不匹配。如果不关闭Cache和MMU,CPU将继续访问缓存的数据,而不是主存中的实际数据,从而可能导致不可预期的错误行为,甚至系统崩溃。

系统上电复位程序主要执行下列操作

● 设置CPU为SVC模式。

● 关闭Cache,关闭MMU。

● 设置看门狗、屏蔽中断、设置时钟、初始化SDRAM

因此,在系统上电复位时,需要关闭Cache和MMU,以确保CPU只能访问主存中的实际数据。稍后在系统初始化过程中,可以重新启用Cache和MMU。

零长度数组

int array[0];

零长度数组虽然不占据任何内存空间,但仍然具有一些特定的用途。例如,它可以用于作为函数的返回值,表示返回一个空的集合或序列;或者作为一个占位符,占据某个位置,但不需要提交任何元素。

需要注意的是,访问零长度数组的第0个元素是未定义的行为,因为数组中没有任何元素。因此,如果需要表示一个非空的集合或序列,应当使用数组长度大于0的数组。

(2)LDS文件语法

LD 文件:规则详解_ld文件_申小白的博客-CSDN博客

链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情

(3)boot是如何启动的呢?谁又来引导U-boot运行的呢?

ARM SoC一般会在芯片内部集成一块ROM,在ROM上会固化一段启动代码,如图4-37所示,系统上电后,会首先运行固化在芯片内部的ROMCODE代码。这部分代码的主要工作就是初始化存储接口、建立存储映射,它会根据CPU外部管脚或eFuse值来判断系统的启动方式。

多种启动方式:

NOR Flash、NAND Flash或者从SD卡启动

如果我们设置系统从NOR Flash启动,那么这段代码就会将NOR Flash映射到零地址,然后系统复位,CPU跳到U-boot中断向量表中的第一行代码,即NOR Flash中的第一行代码去执

行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olugx0et-1691918091389)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps44.jpg)]

除了SDRAM和NOR Flash支持随机读写,可以直接运行代码,其他Flash存储器是不支持直接运行代码的,只能将代码复制到内存中执行

系统一般会先将NAND Flash 或SD卡中的一部分代码(前4KB)复制到芯片内部的SRAM中去执行,映射SRAM到零地址,然后在这4KB代码中进行各种初始化、代码复制、重定位等工作,最后PC指针才跳到SDRAM内存中去执行代码。

(十)内存堆栈管理

1.栈的管理

先进后出

满栈SP指向栈顶元素,空栈指向栈顶元素上方的可用空间

C语言函数中的局部变量、传递的实参、返回的结果、编译器生成的临时变量都是保存在栈。

系统上电开始运行的都是汇编代码,在执行第一个C语言函数之前,都要初始化栈空间

计算机运行的程序分操作系统和应用程序。

每个应用程序进程都有4GB(32位)的虚拟地址空间,其中0-3GB用户空间分给应用程序使用,3GB-4GB内核空间分给操作系统使用。

应用程序无权限访问内核,只能通过中断或系统调用访问内核

通过页表和MMU转换虚拟为物理地址,每个进程可独享3GB用户空间

(1)栈的初始化

本质就是栈指针SP的初始化,在系统启动过程中,内存初始化后,将栈指针指向内存中的一段空间,就完成了栈的初始化

ARM处理器使用R13(SP)和R11(FP)来管理堆栈。

新版本Linux栈的起始地址是随机的,防黑客栈溢出攻击(高地址往低地址增长)

栈空间查看、设置

ulimit -s //查看栈大小 单位KB

ulimit -s 4096 //设置栈空间大小 4MB 即4*(2的20次方)

(2)栈溢出

栈内存空间满溢出

编写程序避免栈溢出原则:

①尽量不用大数组,需大内存用堆内存malloc

②函数不要嵌套太深

③递归层数不宜深

(3)FILO栈式操作

FILO是"First In Last Out"的缩写,表示先进后出。栈是一种能够实现FILO操作的数据结构。栈有两种基本操作,一种是push操作,将元素放入栈中,另一种是pop操作,将栈顶的元素弹出。

对于FILO栈式操作,可以使用栈来实现。可以理解为一个放在桌子上的桶或者箱子,我们在桶或者箱子的顶部放入一个个物品,但是当我们需要拿出其中一个物品的时候,只能从顶部开始逐一取出,直到取到目标物品。

在编程语言中,实现FILO栈式操作的相关函数包括push和pop。push函数将元素压入栈中,pop函数将栈顶元素弹出。FILO栈式操作常常用在算法题中,可以帮助我们更快速的解决一些复杂问题。

(4)内存池

内存池是一种数据结构,用于管理分配和释放内存的方法。它是一块预先分配好的内存区域,被分割成固定大小的内存块(或对象),这些内存块可以被动态地分配和释放,同时可以重复利用已经释放的内存块,避免了频繁的内存分配和释放操作,减少了内存碎片的产生,提高了内存使用效率。内存池被广泛地应用于各种高性能计算机系统、网络服务器和游戏引擎等场景。

(5)嵌入式系统堆内存管理框架

img

(6)mmap映射区
缓存页减少系统调用(非mmap)

当我们运行一个程序时,需要从磁盘上将该可执行文件加载到内存。将文件加载到内存有两种常用的操作方法,一种是通过常规的文件I/O操作,如read/write等系统调用接口;一种是使用mmap系统调用将文件映射到进程的虚拟空间,然后直接对这片映射区域读写即可。

为了提高读写效率,减少I/O读盘次数以保护磁盘,Linux内核基于程序的局部原理提供了一种磁盘缓冲机制

img

I/O缓冲区通过减少系统调用的次数来降低系统调用的开销,但也 增加了数据在不同缓冲区复制的次数:一次读写流程要完成两次数据的复制操作。

mmap

通过mmap系统调用将文件直接映射到进程的虚拟地址空间中,地址与文件数据一一对应,对这片内存映射区域进行读写操作 相当于对磁盘上的文件进行读写操作。这种映射方式减少了内存复制和系统调用的次数,可以进一步提高系统性能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EWQodfQG-1691918091391)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps47.jpg)]

img

通过mmap()函数虽然完成了文件和进程虚拟空间的映射,但是需 要注意的是,现在文件还在磁盘上。当用户程序开始读写进程虚拟空 间中的这片映射区域时,发现这片映射区域还没有分配物理内存,就 会产生一个请页异常,Linux内存管理子系统就会给该片映射内存分配 物理内存,将要读写的文件内容读取到这片内存,最后将虚拟地址和 物理地址之间的映射关系写入该进程的页表。文件映射的这片空间分 配物理内存成功后,我们再去读写文件时就不用使用文件的I/O接口函数了,直接对进程空间中的这片映射区域读写即可。

(7)mmap应用

硬盘大文件读写

Framebuffer 屏幕显示

多进程共享动态库

(十一)内存泄漏

当用户使用malloc()申请内存时,内 存分配器ptmalloc将这两个内存块节点从空闲链表中摘除,并把内存 块的地址返给用户使用。如果用户使用后忘了归还,那么空闲链表中就没有了这两个内存块的信息,这两块内存也就无法继续使用了,在内存中就产生了两个“漏洞”,即发生了内存泄漏

1.预防内存泄漏

内存申请后及时地释放,两者要配对使用,

内存释放后要及时将指针设置为NULL

使用内存指针前要进行非空判断。

当函数的调用关系变得复杂时,就很容易产生内存泄漏。为了预防这种错误的发生,在编程时,如果我们在 一个函数内申请了内存,则要在申请处添加注释,说明这块内存应该在哪里释放

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UnyJLsHb-1691918091392)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps49.jpg)]

2.内存泄漏检测:MTrace

MTrace是Linux系统自带的一个工具

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qKVaO2G-1691918091393)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps50.jpg)]

img

img

3.常见的内存错误

内存越界、内存踩踏、多次释放、非法指针

段错误:非法访问内存(访问了权限未许可的内存空间)

(1)内存越界
访问内存禁区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9KQztRyn-1691918091395)(C:/Users/86156/AppData/Local/Temp/ksohtml13568/wps53.jpg)]

非法指针

在我们调试链表时,通常通过指针来操作每一个节点。如果指针在遍历链表时已经指向链表的末尾或头部,指针已经指向NULL了,此时再通过该指针去访问节点的成员,就相当于访问零地址了,也会发生一个段错误,这个指针也就变成了非法指针。

大容量数组或局部变量

在Linux环境下,每一个用户进程默认有8MB大小的栈空间,如果你在函数内定义大容量的数组或局部变量,就可能造成栈溢出,也会引发一个段错误。内核中的线程也是如此,每一个内核线程只有8KB的内核栈,在实际使用中也要非常小心,防止堆栈溢出。

多次free

我们使用malloc()申请的堆内存,如果不小心多次使用free()进行释放,通常也会触发一个段错误。

数组越界

在访问数组时,如果超越数组的边界继续访问,也会发生一个段错误。

使用core dump调试段错误

嵌入式C P362

(2)内存践踏

img

内存踩踏监测:mprotect

P366

内存检测神器:Valgrind

P368

不仅可以检测内存泄漏,还可以对程序进行各种性能分析、代码覆盖测试、堆栈分析及CPU的Cache命中率、丢失率分析等

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
宋宝华嵌入式 C/C++语言精华文章集锦 C/C+语言 struct 深层探索 ............................................................................2 C++中 extern "C"含义深层探索........................................................................7 C 语言高效编程的几招...............................................................................11 想成为嵌入式程序员应知道的 0x10 个基本问题 .........................................................15 C 语言嵌入式系统编程修炼...........................................................................22 C 语言嵌入式系统编程修炼之一:背景篇 ............................................................22 C 语言嵌入式系统编程修炼之二:软件架构篇 ........................................................24 C 语言嵌入式系统编程修炼之三:内存操作 ..........................................................30 C 语言嵌入式系统编程修炼之四:屏幕操作 ..........................................................36 C 语言嵌入式系统编程修炼之五:键盘操作 ..........................................................43 C 语言嵌入式系统编程修炼之六:性能优化 ..........................................................46 C/C++语言 void 及 void 指针深层探索 .................................................................50 C/C++语言可变参数表深层探索 .......................................................................54 C/C++数组名与指针区别深层探索 .....................................................................60 C/C++程序员应聘常见面试题深入剖析(1) ..............................................................62 C/C++程序员应聘常见面试题深入剖析(2) ..............................................................67 一道著名外企面试题的抽丝剥茧 ......................................................................74 C/C++结构体的一个高级特性――指定成员的位数 .......................................................78 C/C++中的近指令、远指针和巨指针 ...................................................................80 从两道经典试题谈 C/C++中联合体(union)的使用 ......................................................81 基于 ARM 的嵌入式 Linux 移植真实体验 ................................................................83 基于 ARM 的嵌入式 Linux 移植真实体验(1)――基本概念 ...........................................83 基于 ARM 的嵌入式 Linux 移植真实体验(2)――BootLoader .........................................96 基于 ARM 的嵌入式 Linux 移植真实体验(3)――操作系统 ..........................................111 基于 ARM 的嵌入式 Linux 移植真实体验(4)――设备驱动 ..........................................120 基于 ARM 的嵌入式 Linux 移植真实体验(5)――应用实例 ..........................................135 深入浅出 Linux 设备驱动编程 .......................................................................144 1.Linux 内核模块..............................................................................144 2.字符设备驱动程序 ...........................................................................146 3.设备驱动中的并发控制 .......................................................................151 4.设备的阻塞与非阻塞操作 .....................................................................157
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ka7ia

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

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

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

打赏作者

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

抵扣说明:

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

余额充值