嵌入式linux系统开发教程

第四章 交叉开发环境

目标板与主机的连接

目标板与主机的连接方式有:串口、以太网接口、USB接口、JTAG接口

文件传输

(1)串口传输方式

(2)网络传输方式

(3)USB接口传输方式

(4)JTAG接口传输方式

(5)移动存储设备

交叉开发环境的搭建

(1)配置开发主机环境

​ 在开发主机上安装VMware虚拟机,然后在VMware中安装合适的linux系统。

(2)安装和配置交叉编译工具链

​ 从网络社区下载交叉编译工具链,将交叉编译工具链解压出来并将其路径添加到环境变量PATH中,然后重启系统生效。

(3)安装TFTP服务

​ a、登入root用户。
​ b、安装tftpd-hpa服务:apt-get install tftp-hpa
​ c、验证TFTP服务是否安装成功。
​ d、安装成功后修改配置文件 /etc/default/tftpd-hpa,设置好TFTP传输目录。
​ e、重启TFTP服务,使更改生效。
​ f、在根目录下创建/tftpboot目录,用作TFTP的传输目录:mkdir /tftpboot。
​ g、在/tftpboot目录中创建test文件进行测试。
​ h、验证通过TFTP传输的文件和源文件是否内容一致。
​ i、通过自回环获得/tftpboot中的test文件内容保持一致,TFTP搭建成功。

(4)安装NFS服务

​ a、安装NFS服务 apt-get install nfs-kernel-server
​ b、验证NFS服务是否安装成功。
​ c、修改/etc/exports文件,设置NFS使用文件系统所在的位置,并且设置对应的控制权限。
​ d、重启服务使更改生效。
​ e、将NFS文件系统挂载到/mnt目录下,在文件系统中创建文件,然后验证文件系统中 文件与/mnt中镜像的文件内容是否一致。
​ f、修改/mnt中文件内容,查看nfs文件系统中内容,发现仍然保持一致,说明文件共享成功。
​ g、从/mnt卸载下nfs文件系统。

(5)配置串口调试工具

​ 安装USB转串口驱动,配置使用putty等串口控制台工具。

(6)将U-Boot烧写到SD卡上制作启动盘

​ 将U-Boot的镜像文件u-boot.bin同BL1、BL2 合并生成的最终镜像文件烧写到SD卡中。开发板上电,使开发板进入U-Boot模式。

(7)使开发板能ping通虚拟机

​ 设置虚拟机的IP地址,启动开发板进入U-Boot模式,通过设置U-Boot的环境变量设置开发板的IP地址,设置好之后通过ping命令测试是否连通网络。

(8)配置开发板自动运行linux内核

​ 通过TFTP服务将内核镜像uImage和设备树镜像文件下载到/tftpboot目录下,设置自启动命令bootcmd,实现自动运行内核。

(9)挂载根文件系统

​ U-Boot在启动内核时,会通过设置启动参数bootargs,将一些信息传递给内核uImage,其中主要是通过什么方式挂载根文件系统。需要重启开发板通过NFS挂载根文件系统。

(10)运行和调试交叉编译的程序

​ 在开发主机上编写一个程序,将交叉编译之后的文件通过NFS或者TFTP传输到目标板上,然后执行,并且可以通过gdbserver远程交叉调试。

第五章 BootLoader

几种常用的BootLoader

有:LILO(linux磁盘引导程序)、GRUB(GNU的LILO替代程序)、U-Boot(通用引导程序)、RedBoot

BootLoader的启动方式

1、网络启动方式

2、磁盘启动方式

3、Flash启动方式:flash存储的四部分:BootLoader、启动参数、内核镜像、文件系统

U-Boot的常用命令

1、bootm:用于运行内核uImage

2、ping:用于测试网络是否连通

3、movi:用于操作eMMC(类似flash)指令

4、saveenv:用于保存环境变量

5、setenv:用于设置环境变量

6、printenv:用于查看u-boot的环境变量

U-Boot环境变量

1、bootargs:定义传递给Linux内核的命令参数(启动参数)

2、bootcmd:定义启动命令

3、bootdelay:定义执行自动启动的等候秒数

U-Boot启动过程

(1)BL0阶段:关闭看门狗、关闭中断及MMU、时钟设置、检测om决定启动方式和复制BL1代码到RAM中

(2)BL1阶段:初始化环境,搬移BL2代码到RAM中。

(3)BL2阶段:完成基本硬件初始化。

(4)u-boot.bin阶段:将BL1和BL2添加到u-boot.bin前面,打包校验后生成新的u-Boot镜像文件。

第六章 配置编译内核

内核配置方式

1、config: 通过命令行程序更新当前配置

2、menuconfig: 通过菜单程序更新当前配置

3、xconfig:通过Qt图形界面更新当前配置

4、gconfig:通过GTK图形界面更新当前配置

Kconfig

Kconfig是内核配置选项的源文件。多数选项定义为一个菜单选项,其他选项起辅助组织。

菜单选项类型:tristate,string,bool,hex,int

菜单依赖关系:三态逻辑(n,m,y),用来表示模块状态

菜单组织结构:树状结构的两种组织方式。

内核菜单属性说明:

Kconfig菜单关键字:

关键字作用
config定义一个配置符号<symbol>,并且可以配置选项属性
menuconfig这类似于简单的配置选项,但是它暗示:所有的子选项应该作为独立的选项列表显示
choices这定义了一个选择组,并且可以配置选项属性。每个选择项只能是布尔或者三态类型。布尔类型只允许选择单个配置选项,三态类型可以允许把任意多个选项配置成“m”。如果一个硬件设备有多个驱动程序,内核次只能静态链接或者加载一个驱动,但是所有的驱动程序都可以编译为模块选择项还可以接受另外一个选项“ optional”,可以把选择项设置成“n",并且不需要选择什么选项
comment这定义了一个注释,在配置过程中显示在菜单上,也可以回显到输出文件中可能的选项是依赖关系
menu这定义了一个菜单项,在菜单组织结构中有些描述。唯一可能的选项是依赖关系
if这定义了一个if语句块。依赖关系表达式<expr>附加给所有封装好的菜单选项
source读取指定的配置文件。读取的文件也会解析生成菜单

Kbuild Makefile中的变量

obj-y(静态链接目标文件):

​ 将obj-y中的文件链接成一个built-in.o文件链接到顶层目录的vmlinux中去。

obj-m(可加载模块目标文件):

​ 指定要编译成可加载模块的目标文件,需要加载到内核的驱动程序常常使用这种方式。

内核编译结果

(1)vmlinux:是在内核源码顶层目录生成的内核映像,是最原始的未经压缩的内核文件

(2)System.map:是一个特定内核的内核符号表,它包含内核全局变量函数的地址信息

(3)zImage:是一个合成的镜像,可以由引导加载程序加载并启动

(4)uImage:是把zImage加上64个字节的头信息后产生的

内核配置选项

子菜单项选择相应配置的时候,有3种选择,代表的含义是:

y(yes):将该功能编译进内核

n(no):不将该功能编译进内核

m(module):将该功能编译成可以在需要时动态加载到内核中的模块

第七章 内核移植基础

设备树

设备树是一种描述硬件的数据结构,使得目标板和设备变成数据启动的,他们必须基于传递给内核的数据进行初始化,而不是像以前一样采用硬编码的方式,使用它的主要原因是:平台识别、实时配置和设备植入。

设备树文件类型:.dts,.dtsi,.dtb

.dtsi:是被包含的设备树源文件,类似于C语言中头文件。

.dts:是设备树源文件,可以包含其他.dtsi文件,由dtc编译生成.dtb文件。

.dtb:.dts编译成的二进制文件

**设备树语法:**设备树是一个包含节点和属性的简单树状结构,属性就是键值对,而节点可以同时包含属性和子节点。

Linux使用设备树的主要原因:(1)平台识别(2)实时配置(3)设备植入

第九章 内核调试方法

调试方法

1、通过打印函数 printk(和printf的区别 printf是应用程序中的,而printk是在内核中使用的)

2、获取内核信息(系统请求键、/proc接口、/sys接口)

3、处理出错信息(内核的两个基本错误处理机制:oops和panic)

4、内核源码调试(通过gdb调试)

printk与printf的区别

printk:

​ (1)Linux内核标准的系统打印函数,是内核专用的,可以添加打印级别。

​ (2)具有极好的健壮性,不受内核运行条件的限制,在系统运行期间都可使用。

​ (3)printk不直接向控制台设备或串口直接打印信息,而是把打印信息先写到缓冲区里。

​ (4)不支持浮点数。

​ (5)是“行驱动”的,只有收到一个换行符数据才会真正输出到终端。

printf:

​ 运行在用户态。使用了标准的C库函数的时候才能使用。

获取内核信息

通过/proc接口

proc文件系统是一种伪文件系统,是系统运行时在内存中建立的内核状态映射,可以瞬间提供系统的状态信息。通过proc文件系统可以查看运行中的内核,查询和控制运行中的进程和系统资源等状态。在用户空间可使用mount -t proc proc /proc命令挂载到/proc目录下。

通过/sys接口

sysfs也是一种伪文件系统,是在内存中实现的文件系统。可以把内核空间的数据、属性、链接等东西输出到用户空间。sysfs文件系统通常挂载到/sys目录下,提供给用户空间。

使用mount -t sysfs sysfs /sys 命令进行挂载,-t选项是指定文件系统的类型。

处理出错信息

当系统出现错误时,内核有两个基本的错误处理机制:oops 和 panic。

oops消息包含系统错误的详细信息。

panic函数在系统发生严重错误时调用。

可以使用ioctl函数获取出错信息并处理。

第十章 制作linux根文件系统

根文件系统目录结构

目录作用
/bin包含二进制文件形式的可执行程序
/dev存放一些称为设备文件的特殊文件
/etc存放Linux的很多系统配置文件
/home用户主目录的默认位置
/lib必要的共享库和内核模块
/mnt用来为其它的文件系统提供安装点
/proc是虚拟目录,即系统内存的映射,包含一些和系统相关的信息
/rootRoot表示用户主目录
/sbin与bin目录类似,存放系统可执行文件,通常只有root用户才有运行的权限
/syssysfs伪文件系统标准挂载点,类似于proc目录,主要用于设备管理
/tmp公用的临时文件存储点

inittab文件

etc/inittab文件有四个域:id:runlevels:action:process

(1)id:入口标识符,是一个字符串,要求与tty的编号相同

(2)runlevels:指出action和process会在哪些运行级别中被执行(有0~6七个运行级别)

(3)action:init程序在执行相应的process时,对process所采取的动作(比如只执行一次,还是在它退出时重启)

(4)process:具体的执行程序,后面可以跟参数

Busybox init启动过程分析

Busybox具有处理系统初始化过程的能力,可为嵌入式系统提供主要的init功能。

它的init进程主要进行以下工作:

(1)为init进程设置信号处理进程

(2)对控制台进行初始化

(3)解析inittab文件,即/etc/inittab

(4)默认情况下,Busybox会运行系统初始化脚本/etc/init.d/rcS

(5)运行导致init暂停的inttab命令

(6)运行仅执行一次的inittab命令

问答题

1、交叉开发环境的搭建(重要)

(1)配置开发主机环境

​ 在开发主机上安装VMware虚拟机,然后在VMware中安装合适的linux系统。

(2)安装和配置交叉编译工具链

​ 从网络社区下载交叉编译工具链,将交叉编译工具链解压出来并将其路径添加到环境变量PATH中,然后重启系统生效。

(3)安装TFTP服务

​ a、登入root用户。
​ b、安装tftpd-hpa服务:apt-get install tftp-hpa
​ c、验证TFTP服务是否安装成功。
​ d、安装成功后修改配置文件 /etc/default/tftpd-hpa,设置好TFTP传输目录。
​ e、重启TFTP服务,使更改生效。
​ f、在根目录下创建/tftpboot目录,用作TFTP的传输目录:mkdir /tftpboot。
​ g、在/tftpboot目录中创建test文件进行测试。
​ h、验证通过TFTP传输的文件和源文件是否内容一致。
​ i、通过自回环获得/tftpboot中的test文件内容保持一致,TFTP搭建成功。

(4)安装NFS服务

​ a、安装NFS服务 apt-get install nfs-kernel-server
​ b、验证NFS服务是否安装成功。
​ c、修改/etc/exports文件,设置NFS使用文件系统所在的位置,并且设置对应的控制权限。
​ d、重启服务使更改生效。
​ e、将NFS文件系统挂载到/mnt目录下,在文件系统中创建文件,然后验证文件系统中 文件与/mnt中镜像的文件内容是否一致。
​ f、修改/mnt中文件内容,查看nfs文件系统中内容,发现仍然保持一致,说明文件共享成功。
​ g、从/mnt卸载下nfs文件系统。

(5)配置串口调试工具

​ 安装USB转串口驱动,配置使用putty等串口控制台工具。

(6)将U-Boot烧写到SD卡上制作启动盘

​ 将U-Boot的镜像文件u-boot.bin同BL1、BL2 合并生成的最终镜像文件烧写到SD卡中。开发板上电,使开发板进入U-Boot模式。

(7)使开发板能ping通虚拟机

​ 设置虚拟机的IP地址,启动开发板进入U-Boot模式,通过设置U-Boot的环境变量设置开发板的IP地址,设置好之后通过ping命令测试是否连通网络。

(8)配置开发板自动运行linux内核

​ 通过TFTP服务将内核镜像uImage和设备树镜像文件下载到/tftpboot目录下,设置自启动命令bootcmd,实现自动运行内核。

(9)挂载根文件系统

​ U-Boot在启动内核时,会通过设置启动参数bootargs,将一些信息传递给内核uImage,其中主要是通过什么方式挂载根文件系统。需要重启开发板通过NFS挂载根文件系统。

(10)运行和调试交叉编译的程序

​ 在开发主机上编写一个程序,将交叉编译之后的文件通过NFS或者TFTP传输到目标板上,然后执行,并且可以通过gdbserver远程交叉调试。

2、如何 下载 编译 配置 移植 启动 调试 U-Boot(重要)

下载

​ 可以使用Git或者从U-Boot的ftp服务器下载U-Boot源码。

编译

​ U-Boot源码通过Makefile组织编译,可以修改U-Boot顶层目录的Makefile,指定编译时的交叉编译工具链。 首先指定产品配置(指定最类似的官方配置),在U-Boot源码的根目录中选择boards.cfg对应的配置即可,配置完之后执行make命令编译生成u-boot.bin文件。(例如:在根目录执行make origen_config命令,然后进行make编译,生成u-boot.bin文件)。

配置

​ 在程序中为开发板定义配置选项或者参数,放置在头文件内。

移植

​ (1)实现串口输出:可以通过J-Link、OpenJTAG或者简单的使用点灯法调试串口。

​ (2)实现网卡移植:配置网卡相关的寄存器的参数,添加网络初始化代码,修改配置文件添加网路相关配置,重新编译U-Boot。

​ (3)Flash移植:初始化EMMC。

​ (4)其他的设备的移植,可以通过网络查询或者是通过电路图、芯片手册和代码总结出移植过程。

启动

​ 对u-boot.bin文件进行加密处理后,将其烧写到SD卡上。U-Boot做为BootLoader启动,在开机上电之后,将系统内核引导起来。u-boot镜像文件的制作分为四个阶段。

(1)BL0阶段:关闭看门狗、关闭中断及MMU、时钟设置、检测om决定启动方式和复制BL1代码到RAM中。

(2)BL1阶段:初始化环境,搬移BL2代码到RAM中。

(3)BL2阶段:完成基本硬件初始化。

(4)u-boot.bin阶段:将BL1和BL2添加到u-boot.bin前面,打包校验后生成新的U-Boot镜像文件。

调试

​ 可以通过J-Link和JTAG等调试器调试或者使用点灯法调试。

3、U-Boot的特点

​ (1)开放源码

​ (2)支持多种嵌入式操作系统内核

​ (3)支持多个处理器系列

​ (4)较高的可靠性和稳定性

​ (5)高度灵活的功能设置

​ (6)丰富的设备驱动源码

​ (7)较为丰富的开发调试文档与强大的网络技术支持。

4、如何 编译 移植 调试 linux内核 (重要)

编译

​ (1)内核源码下载:可以通过linux官网或者使用Git下载linux内核源码,如果下载的是压缩包则解压获得源码,代码需要进行完整性检查。

​ (2)内核配置:linux内核的源代码包含一个配置系统,内核的配置编译是由Makefile整体管理的,可以修改顶层目录的Makefile。可以选择使用不同的配置工具进行配置。内核中有一个Kconfig文件是内核配置选项的源文件,里面包含了各种选项配置和依赖。我们可以使用菜单之类的图形界面方式对内核进行配置。

​ (3)内核编译:通过Makefile的编译命令对内核进行编译,链接,编译后产生vmlinux、System.map、zImage、uImage内核文件。

移植

​ (1)选定参考板:选定参考板进行linux内核的测试,linux内核启动之后使用配置工具对内核进行配置,裁剪不需要的功能,添加需要的功能。

​ (2)获取内核源码:下载官方或第三方提供的源码,如果是压缩包则解压获得源码,解压后的代码进行完整性检查。

​ (3)清除内核:将内核恢复到初始状态,如果是第三方提供的源码的话最好不要清除。

​ (4)给内核源码打补丁:对内核代码进行相应的修改,内核代码中要添加相应的ARM核支持,相应的SoC的支持,相应硬件单板的支持。

​ (5)配置内核。

​ (6)编译内核源码。

​ (7)生成设备树文件:可以通过参考板的设备树源文件进行修改,然后将其编译成设备树镜像文件。

​ (8)将内核和设备树文件复制到特定目录下。

​ (9)启动内核。

​ (10)基本内核移植完成之后再进行硬件驱动程序的移植:移植需要的网卡、SD卡、eMMC等驱动,挂载根文件系统,需要将要添加的硬件配置到设备树源文件中去,然后重新配置编译内核和设备树。

调试

​ (1)通过打印函数printk(和printf的区别 printf是应用程序中的,而printk是在内核中使用的)。

​ (2)获取内核信息(系统请求键、/proc接口、/sys接口)。

​ (3)处理出错信息(内核的两个基本错误处理机制:oops和panic)。

​ (4)内核源码调试(通过gdb调试)。

5、Linux系统的启动过程

内核引导部分

​ (1)开机启动,BIOS上电自检

​ (2)BootLoader启动初始化堆栈和内存空间,将压缩的内核镜像加载到内存中。

​ (3)内存镜像完成自解压,跳转到解压后的内核代码入口。

内核启动部分

​ (4)linux内核初始化硬件,初始化设备驱动,挂载根文件系统

​ (5)启动init进程,读取/etc/inittab文件,启动本地服务,完成系统初始化

应用程序启动部分

​ (6)建立终端

​ (7)用户完成系统登录

6、设备树的概念 以及 如何 获取 运行 设备树

概念

​ 设备树是一种描述硬件的数据结构,使得目标板和设备变成数据启动的,他们必须基于传递给内核的数据进行初始化,而不是像以前一样采用硬编码的方式,使用它的主要原因是:平台识别,实时配置和设备植入。

设备树文件类型:.dts,.dtsi,.dtb

.dtsi:是被包含的设备树源文件,类似于C语言中头文件。

.dts:是设备树源文件,可以包含其他.dtsi文件,由dtc编译生成.dtb文件。

.dtb:.dts编译成的二进制文件

设备树语法:

​ 设备树是一个包含节点和属性的简单树状结构,属性就是键值对,而节点可以同时包含属性和子节点。

获取

​ 可以通过参考板提供的设备树文件进行对应的修改获取。

运行

​ 将dts文件编译成dtb文件,放到特定目录下,供内核使用。

7、Linux的内核特性

​ (1)可移植性

​ (2)可裁剪性

​ (3)标准化和互用性

​ (4)完善的网络支持

​ (5)安全性

​ (6)稳定性

​ (7)模块化

8、如何制作根文件系统(重要)

​ (1)配置并编译Busybox

​ 下载busybox后解压源码,进入源码目录,运行make menuconfig命令进入图形界面配置busybox(配置包括三类,第一类是包括编译调试安装的基本配置,第二类是工具集的选择,第三类是配置文件的加载和保存的路径设置,一般可以只改第一类)配置完成之后保存退出。退出之后执行makemake install命令编译安装busybox。

​ (2)添加共享库文件和内核模块

​ 创建lib目录存放库相关文件。busybox运行还需要共享库支持,需要将共享库添加进根文件系统。有一些内核功能是以模块的形式进行编译的,需要编译并安装内核模块。

​ (3)创建其他的目录

​ 使用mkdir命令创建其他的基本目录。

​ (4)创建基本的设备结点

​ (5)添加启动配置文件和脚本程序

​ 添加/etc/inittab、/etc/init.d/rcS等文件

​ (6)测试根文件系统

​ 通过NFS挂载根文件系统进行测试

​ (7)制作根文件系统镜像

​ 首先制作一个ramdisk镜像,将镜像文件格式化为ext2格式。然后将该镜像文件挂载到/mnt目录下。把rootfs下的所有内容复制到/mnt下,然后取消挂载。压缩镜像文件,生成U-Boot可以识别的ramdisk镜像。通过tftp将镜像直接下载到指定的目录/tftpboot下,然后再在U-Boot命令行设置参数,启动镜像。

​ (8)固化根文件系统镜像

​ 需要将根文件系统镜像固化到eMMC上,这样就不需要每次都要重新下载了。从emmc启动开发板进入U-Boot交互模式,通过tftp将U-Boot、内核uImage镜像和设备树镜像文件下载下来并且烧写到eMMC上,然后设置相应的环境变量。通过NFS挂载根文件系统,系统启动后对eMMC进行分区和格式化,之后将根文件系统中的内容复制到新建的分区上,重启开发板,进入U-Boot交互模式,重新设置bootargs环境变量,设置之后启动系统完成固化。

9、Linux有哪些文件系统,各有什么优缺点

​ (1)jffs2

​ 可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃/掉电安全保护,提供“写平衡”支持等。缺点主要是当文件系统已满或接近满时,因为垃圾收集的关系而使jffs2的运行速度大大放慢。

​ (2)Yaffs

​ 速度更快,挂载时间很短,对内存的占用较小。

​ (3)ext2

​ 没有日志功能,启用的时间通常需要很久。目前有许多日志型文件系统可以以更快的速度及更高的效率完成系统启用和检查。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本篇主要讲述进行嵌入式 本篇主要讲述进行嵌入式 本篇主要讲述进行嵌入式 本篇主要讲述进行嵌入式 本篇主要讲述进行嵌入式 本篇主要讲述进行嵌入式 Linux 开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍开发所必备的基础知识, 以实用和够为标准进行介绍与嵌入式嵌入式 LinuxLinux 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 开发不相关的知识都在讲述之列。,特别是 LinuxLinux 命令部分,并没有介绍 命令部分,并没有介绍 命令部分,并没有介绍 命令部分,并没有介绍 命令部分,并没有介绍 全部 的 Linux Linux 命令, 命令, 而仅精选嵌入式 仅精选嵌入式 仅精选嵌入式 仅精选嵌入式 LinuxLinux 开发中的常用命令进行介绍。 开发中的常用命令进行介绍。 开发中的常用命令进行介绍。 开发中的常用命令进行介绍。 开发中的常用命令进行介绍。本篇一共分为 本篇一共分为 本篇一共分为 6章,从 章,从 章,从 LinuxLinux Linux 操作系统开始, 操作系统开始, 操作系统开始, 操作系统开始, 循序渐进 循序渐进 循序渐进 地介绍, 到最后 讲述 嵌入式 嵌入式 Linux Linux 开发环境的构建, 开发环境的构建, 开发环境的构建, 开发环境的构建, 为嵌入式嵌入式嵌入式 Linux Linux 开发做准备。各章 开发做准备。各章 开发做准备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值