linux app(1)
本笔记基于MX6ULL为板载核心,使用正点原子alpha板。
eMMC
eMMC(embedded MultiMediaCard)是一种嵌入式存储解决方案,广泛应用于智能手机、平板电脑、物联网设备、车载系统等嵌入式设备中。它结合了 NAND Flash 存储芯片 和 控制器,并通过标准接口与主机(如处理器或 SoC)通信。成本低,但通信速率低于UFS,更低于SSD,主要应用在低端设备。
eMMC 的基本组成
eMMC 并不是单纯的存储芯片,而是一个完整的存储模块,主要由以下部分组成:
- NAND Flash 存储芯片:用于实际数据存储。
- Flash 控制器:管理 NAND Flash 的读写、坏块管理、磨损均衡(wear leveling)、错误校验(ECC)等。
- 标准接口(MMC 接口):采用 JEDEC eMMC 标准,兼容性强,简化了主控设计。
使用方式和作用
烧录方法:可通过 SD 卡、USB 工具
Linux 系统和应用程序通常会被烧录到 eMMC 存储中,并通过配置启动模式(如 eMMC 启动)来运行系统。
本质:eMMC = NAND Flash(存储) + 控制器(管理 Flash),相当于一个 “自带管理器的硬盘”。
存储内容:
Bootloader
(如 U-Boot)Linux 内核
(如zImage
)根文件系统
(如rootfs.ext4
)
eMMC 和普通 NAND Flash 的区别?
- eMMC:自带控制器,主控芯片直接通过标准接口读写(简单,适合嵌入式)。
- 原始 NAND Flash:需要主控芯片实现坏块管理、ECC 校验等(复杂,需额外驱动)。
嵌入式设备的核心组件类比(以 i.MX6ULL 为例)
组件 | 电脑类比 | 嵌入式设备(如 i.MX6ULL) | 作用 |
---|---|---|---|
eMMC | 硬盘(SSD/HDD) | 嵌入式存储芯片(如 8GB eMMC) | 存储系统(Linux内核、根文件系统)、应用程序、用户数据,非易失性(断电不丢失)。 |
RAM(如 DDR3) | 内存条 | 动态内存(如 256MB/512MB DDR3) | 临时存储运行时的程序和数据,易失性(断电丢失)。 |
主控芯片 | CPU | 处理器(如 NXP i.MX6ULL) | 执行计算、控制外设(USB、GPIO、网络等),运行操作系统(如 Linux)。 |
Boot ROM | BIOS/UEFI | 芯片内置的 BootROM | 初始化硬件,加载第一级 Bootloader(如 U-Boot)。 |
NFS(Network File System)服务
NFS(Network File System,网络文件系统)是一种分布式文件系统协议,由 Sun Microsystems 开发,允许计算机系统通过网络共享文件和目录。它广泛应用于 Linux/Unix 环境,使客户端可以像访问本地文件一样访问远程服务器上的文件。经过我的尝试,无法在WSL(1)上通过nfs挂载其他linux系统进行嵌入式开发。
通信流程
- 服务器端 启动
nfs-server
并导出(export)共享目录。 - 客户端 请求挂载远程目录,通过 RPC 协议与服务器交互。
- 数据传输:
- 客户端读写文件时,NFS 协议将请求转发到服务器。
- 服务器处理请求并返回数据,客户端无感知(透明访问)。
配置方法
除了不能再WSL上配置可以在虚拟机、或Linux系统下配置:
Ubuntu 使用如下命令安装 NFS 服务:
1 | sudo apt-get install nfs-kernel-server rpcbind |
使用如下命令打开 nfs 配置文件/etc/exports:
1 | sudo vi /etc/exports |
打开/etc/exports 以后在后面添加如下所示内容:
1 | /home/user/linux/nfs *(rw,sync,no_root_squash) |
重启 NFS 服务,使用命令如下:
1 | sudo /etc/init.d/nfs-kernel-server restart |
最后使用如下命令在嵌入端进行挂载:
1 | mount -t nfs -o nolock,vers=3 192.168.5.11:/home/user/nfs_rootfs /mnt |
在开发过程中,在 Ubuntu 中编译好程序后放入/home/book/nfs_rootfs目录,开发板 mount nfs 后就可以直接通过/mnt 访问 Ubuntu 中的文件。
远程访问开发板
远程命令使用开发板的三种方式:
使用串口连接后通过MobaXterm通过指定串口和波特率(115200)后进行访问。
windows端与开发板连接到同一个局域网后,通过MobaXterm的ssh指定IP和端口(22)后访问。
在linux端(ubuntu)无法使用MobaXterm,可以直接在终端中通过ssh进行访问,命令如下:
1
ssh root@192.168.X.XXX
文件传输的两种方法:
- 同一局域网下,使用Filezilla软件指定IP和端口号(22)后进行传输
- 相同的方式,使用WinSCP
GCC
交叉编译工具有OpenWrt,Buildroot,Linaro的,选择:
Linaro出品的交叉编译器:arm-linux-gnueabihf-gcc
编译流程
GCC 的编译过程分为 4 个阶段:
(1)预处理(Preprocessing)
处理
#include
、#define
、#ifdef
等宏指令。命令:
1
gcc -E hello.c -o hello.i # 生成预处理文件
(2)编译(Compilation)
将预处理后的代码转为汇编语言。
命令:
1
gcc -S hello.i -o hello.s # 生成汇编代码
(3)汇编(Assembly)
将汇编代码转为机器码(目标文件
.o
)。命令:
1
gcc -c hello.s -o hello.o # 生成目标文件
(4)链接(Linking)
合并目标文件和库文件(如
libc.so
),生成可执行文件。命令:
1
gcc hello.o -o hello # 生成可执行文件
简化命令:直接生成可执行文件
1 gcc hello.c -o hello
GCC 常用编译选项
基础选项
选项 | 说明 |
---|---|
-o <file> |
指定输出文件名(默认 a.out )。 |
-c |
只编译不链接,生成 .o 目标文件。 |
-g |
添加调试信息(供 GDB 使用)。 |
-Wall |
启用所有警告(推荐始终使用)。 |
-O0/-O1/-O2/-O3 |
优化级别(-O0 不优化,-O3 最高优化)。 |
库和路径选项
选项 | 说明 |
---|---|
-I<dir> |
添加头文件搜索路径(如 -I/usr/local/include )。 |
-L<dir> |
添加库文件搜索路径(如 -L/usr/local/lib )。 |
-l<library> |
链接动态库(如 -lm 链接数学库 libm.so )。 |
-static |
静态编译(将所有库打包进可执行文件)。 |
调试与优化
选项 | 说明 |
---|---|
-D<macro> |
定义宏(如 -DDEBUG 等价于 #define DEBUG )。 |
-fPIC |
生成位置无关代码(用于动态库)。 |
-save-temps |
保留临时文件(.i 、.s 、.o )。 |
操作文件I/O
在 Linux 系统中,文件 I/O(输入/输出)操作可以通过两种主要方式实现:标准 I/O(Standard I/O) 和 系统 I/O(System I/O)。它们的主要区别在于接口层次、缓冲机制、性能以及适用场景。
1. 标准 I/O(Standard I/O)
标准 I/O 是由 C 标准库(libc) 提供的高级文件操作接口,封装了底层系统调用,提供缓冲机制,以提高 I/O 效率。
特点
- 基于流(FILE*):使用
FILE*
结构体表示文件流。 - 带缓冲(Buffered):
- 全缓冲(Fully Buffered):当缓冲区满时才进行实际 I/O(适用于文件)。
- 行缓冲(Line Buffered):遇到换行符
\n
或缓冲区满时刷新(适用于终端)。 - 无缓冲(Unbuffered):立即写入(如
stderr
)。
- 跨平台兼容:由于是 C 标准库的一部分,代码可移植性较好。
- 适用于频繁的小数据读写,减少系统调用次数,提高性能。
常用函数
1 |
|
示例
1 |
|
2. 系统 I/O(System I/O / Unix I/O)
系统 I/O 是 Linux/Unix 提供的 底层文件操作接口,直接调用内核提供的系统调用(syscalls),不提供缓冲机制。
特点
- 基于文件描述符(File Descriptor, fd):使用整数
int
表示文件(如0=stdin, 1=stdout, 2=stderr
)。 - 无缓冲(Unbuffered):每次读写都会触发系统调用,效率较低但更可控。
- 适用于高性能、低延迟场景(如网络编程、设备驱动)。
- 更底层,灵活性更高,可以直接操作设备文件、管道、套接字等。
常用函数
1 |
|
示例
1 |
|
3. 主要区别对比
特性 | 标准 I/O(stdio) | 系统 I/O(syscall) |
---|---|---|
接口级别 | 高级(C 标准库封装) | 低级(直接系统调用) |
缓冲机制 | 有缓冲(可配置) | 无缓冲(直接读写) |
性能 | 较高(减少系统调用) | 较低(每次调用都进内核) |
适用场景 | 普通文件、文本处理 | 设备文件、网络、底层开发 |
文件表示 | FILE* (流) |
int (文件描述符) |
移植性 | 高(跨平台) | 低(依赖 Unix/Linux) |
控制灵活性 | 较低(缓冲不可控) | 高(直接控制) |
4. 如何选择?
- 使用标准 I/O(
fopen/fread/fwrite
):- 需要处理文本文件、格式化输入输出。
- 需要缓冲机制提高性能(如频繁的小数据读写)。
- 希望代码可移植到其他平台(Windows/macOS)。
- 使用系统 I/O(
open/read/write
):- 需要直接操作设备文件(如
/dev
下的设备)。 - 需要非阻塞 I/O 或文件锁(
fcntl
)。 - 进行网络编程(套接字
socket
也是文件描述符)。 - 需要精确控制 I/O 行为(如
O_DIRECT
绕过缓存)。
- 需要直接操作设备文件(如
5. 混合使用
在某些情况下,可以混合使用两者:
使用
fileno()
将FILE*
转换为文件描述符:1
2FILE *fp = fopen("file.txt", "r");
int fd = fileno(fp); // 获取底层文件描述符使用
fdopen()
将文件描述符转换为FILE*
:1
2int fd = open("file.txt", O_RDONLY);
FILE *fp = fdopen(fd, "r"); // 包装成标准 I/O 流
Others
修改DNS配置:
打开/etc/resolv.conf
,添加修改nameserver 8.8.8.8
详细:IP地址与路由