C/C++ 应用开发入门
1. 概述
1.1 功能简介
C/C++ 是嵌入式 Linux 系统中最常用的开发语言,具有执行效率高、资源占用少、可直接操作硬件等优点。在 RK3576 平台上,C/C++ 应用开发基于交叉编译工具链,开发者在 x86 主机上编写代码,编译生成 ARM64 可执行文件,然后部署到 RK3576 目标板上运行。本文档介绍 C/C++ 交叉编译环境搭建、基本开发流程、常用库的使用以及调试方法。
1.2 应用场景
- 系统服务:守护进程、系统管理程序
- 驱动程序:内核模块和用户空间驱动
- 音视频处理:多媒体应用、流媒体服务
- 网络应用:TCP/UDP 服务器、HTTP 服务
- 算法加速:NPU/GPU 加速的计算密集型应用
1.3 工作原理
RK3576 C/C++ 开发流程:
源代码 (.c/.cpp)
↓
交叉编译 (aarch64-linux-gnu-gcc)
↓
可执行文件 (ELF, ARM64)
↓
传输到目标板 (scp/tftp/USB)
↓
在 RK3576 上运行RK3576 Linux SDK 使用 GCC 10.3 交叉编译工具链,支持 C99/C11/C++11/C++14/C++17 标准。
2. 硬件准备
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3576BQ 核心板 + M4-R2 底板 | 1 | |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| 网线 | 标准 RJ45 | 1 | 用于网络传输文件 |
| 电源适配器 | 5V/2A DC(Type-C 或 6pin 供电) | 1 | 空载最小 1000mA |
2.2 硬件确认
# 确认目标板系统架构
uname -m
# 输出:aarch64
# 确认 GCC 版本(在目标板上)
gcc --version3. 软件准备
3.1 交叉编译工具链安装
RK3576 Linux SDK 自带交叉编译工具链,位于 SDK 目录下:
# SDK 工具链路径
SDK_DIR/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/
# 交叉编译器
aarch64-linux-gnu-gcc
# 添加到 PATH
export PATH=$SDK_DIR/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
# 验证
aarch64-linux-gnu-gcc --version
# 输出:aarch64-linux-gnu-gcc (GCC) 10.3.1 20210621RK3576 SDK 使用 GCC 10.3,相比前代方案的 GCC 8.3 提供了更好的 C++17 支持和编译优化。
3.2 也可以使用系统安装的交叉编译器
# Ubuntu/Debian
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# 验证
aarch64-linux-gnu-gcc --version3.3 基本编译命令
# 编译 C 程序
aarch64-linux-gnu-gcc -o hello hello.c
# 编译 C++ 程序
aarch64-linux-gnu-g++ -o hello hello.cpp
# 指定 C++ 标准
aarch64-linux-gnu-g++ -std=c++17 -o hello hello.cpp
# 带优化编译
aarch64-linux-gnu-g++ -O2 -std=c++17 -o hello hello.cpp
# 带调试信息
aarch64-linux-gnu-g++ -g -O0 -std=c++17 -o hello hello.cpp
# 链接静态库
aarch64-linux-gnu-g++ -static -o hello hello.cpp4. 操作步骤
4.1 第一个 C 程序
编写源代码
// hello.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/utsname.h>
int main(void)
{
struct utsname buf;
printf("Hello from RK3576!\n");
if (uname(&buf) == 0) {
printf("System: %s %s\n", buf.sysname, buf.release);
printf("Machine: %s\n", buf.machine);
}
return 0;
}交叉编译并传输到目标板
# 交叉编译
aarch64-linux-gnu-gcc -o hello hello.c
# 传输到目标板(假设目标板 IP 为 192.168.1.100)
scp hello root@192.168.1.100:/root/
# 在目标板上运行
ssh root@192.168.1.100
./hello
# 输出:Hello from RK3576!
# 输出:System: Linux 6.1.xx
# 输出:Machine: aarch644.2 第一个 C++ 程序
// hello.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main(void)
{
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8};
// 使用 C++17 结构化绑定
auto [min_it, max_it] = std::minmax_element(nums.begin(), nums.end());
int sum = std::accumulate(nums.begin(), nums.end(), 0);
std::cout << "RK3576 C++17 Test" << std::endl;
std::cout << "Min: " << *min_it << std::endl;
std::cout << "Max: " << *max_it << std::endl;
std::cout << "Sum: " << sum << std::endl;
return 0;
}# 编译(使用 C++17 标准)
aarch64-linux-gnu-g++ -std=c++17 -O2 -o hello_cpp hello_cpp.cpp
# 传输并运行
scp hello_cpp root@192.168.1.100:/root/4.3 Makefile 工程管理
# Makefile
CROSS_COMPILE ?= aarch64-linux-gnu-
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
CFLAGS = -Wall -O2 -g
CXXFLAGS = -Wall -O2 -g -std=c++17
LDFLAGS =
# 源文件
C_SRCS = main.c utils.c
CXX_SRCS = app.cpp logger.cpp
# 目标文件
C_OBJS = $(C_SRCS:.c=.o)
CXX_OBJS = $(CXX_SRCS:.cpp=.o)
OBJS = $(C_OBJS) $(CXX_OBJS)
TARGET = myapp
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) $(TARGET)
# 传输到目标板
deploy: $(TARGET)
scp $(TARGET) root@192.168.1.100:/root/# 编译
make
# 清理
make clean
# 编译并部署
make deploy4.4 动态库开发
创建动态库
// mylib.c — 动态库源文件
#include <stdio.h>
void greet(const char *name)
{
printf("Hello, %s! Welcome to RK3576.\n", name);
}
int add(int a, int b)
{
return a + b;
}// mylib.h — 头文件
#ifndef MYLIB_H
#define MYLIB_H
void greet(const char *name);
int add(int a, int b);
#endif编译动态库
# 编译为位置无关代码(-fPIC)
aarch64-linux-gnu-gcc -fPIC -shared -o libmylib.so mylib.c
# 编译使用动态库的程序
aarch64-linux-gnu-gcc -o use_lib use_lib.c -L. -lmylib
# 传输到目标板
scp libmylib.so use_lib root@192.168.1.100:/root/
# 在目标板上运行(指定库路径)
export LD_LIBRARY_PATH=/root:$LD_LIBRARY_PATH
./use_lib静态库
# 编译为 .o 文件
aarch64-linux-gnu-gcc -c -o mylib.o mylib.c
# 创建静态库
aarch64-linux-gnu-ar rcs libmylib.a mylib.o
# 使用静态库编译
aarch64-linux-gnu-gcc -o use_lib use_lib.c -L. -lmylib4.5 多线程编程
// pthread_example.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_THREADS 8 // RK3576 有 8 个核心
typedef struct {
int thread_id;
int result;
} thread_data_t;
void *worker(void *arg)
{
thread_data_t *data = (thread_data_t *)arg;
printf("Thread %d running on CPU %d\n", data->thread_id, sched_getcpu());
data->result = data->thread_id * data->thread_id;
return NULL;
}
int main(void)
{
pthread_t threads[NUM_THREADS];
thread_data_t thread_data[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
thread_data[i].thread_id = i;
thread_data[i].result = 0;
pthread_create(&threads[i], NULL, worker, &thread_data[i]);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
printf("Thread %d result: %d\n", i, thread_data[i].result);
}
return 0;
}# 编译(链接 pthread 库)
aarch64-linux-gnu-gcc -o pthread_example pthread_example.c -lpthread
# 传输到目标板运行
scp pthread_example root@192.168.1.100:/root/4.6 CMake 工程管理
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(myapp C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加编译选项
add_compile_options(-Wall -O2)
# 添加源文件
add_executable(myapp
src/main.cpp
src/utils.cpp
src/logger.cpp
)
# 链接库
target_link_libraries(myapp pthread)# 创建交叉编译工具链文件
cat > toolchain-aarch64.cmake << 'EOF'
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
EOF
# 使用 CMake 构建
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-aarch64.cmake ..
make5. 代码详解
5.1 交叉编译原理
x86 主机
├── 编译器 (GCC) — 在 x86 上运行,生成 ARM64 代码
├── 链接器 — 链接 ARM64 目标文件和 ARM64 库
├── C 库 (glibc) — ARM64 版本
└── 头文件 — ARM64 平台的内核头文件
RK3576 目标板
├── Linux 内核 — 6.1
├── glibc — ARM64 动态链接库
└── 用户空间程序 — ELF ARM64 格式5.2 ELF 文件分析
# 查看可执行文件架构
file myapp
# 输出:ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked
# 查看依赖的动态库
aarch64-linux-gnu-readelf -d myapp | grep NEEDED
# 查看符号表
aarch64-linux-gnu-nm myapp | grep " T "6. 常见问题
6.1 找不到交叉编译器
现象: aarch64-linux-gnu-gcc: command not found
解决方案:
# 检查 SDK 工具链路径
ls $SDK_DIR/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-linux-gnu-gcc
# 设置 PATH
export PATH=$SDK_DIR/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
# 或安装系统工具链
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu6.2 运行时报 "cannot find ld-linux-aarch64.so.1"
现象: 目标板上运行报动态链接器缺失
解决方案:
# 使用静态编译
aarch64-linux-gnu-gcc -static -o hello hello.c
# 或检查目标板 glibc 是否存在
ls /lib/ld-linux-aarch64.so.16.3 目标板上段错误(Segmentation Fault)
现象: 程序在目标板上运行时崩溃
解决方案:
# 带调试信息编译
aarch64-linux-gnu-gcc -g -O0 -o hello hello.c
# 使用 GDB 远程调试
# 目标板上
gdbserver :2345 ./hello
# 主机上
aarch64-linux-gnu-gdb ./hello
(gdb) target remote 192.168.1.100:2345
(gdb) run相关文档
- Python应用开发 — Python 应用开发
- Qt嵌入式开发实战 — Qt GUI 开发
- 网络编程与应用 — 网络编程
- 设备树详解与实践 — 设备树配置
