1.为什么选择多线程而不使用多进程
 资源共享方便,多线程共享同一个进程的内存空间,数据可以直接读写,多进程要通信(IPC),必须通过 管道、共享内存、消息队列、socket 等方式,复杂且开销大。
 上下文切换开销小,
 内存占用更小,多线程运行在同一进程里,不需要复制整个进程地址空间。多进程内存独立,占用更大。
 多线程更适合并发IO模型
如果使用多线程的话:程序需要 高度稳定/隔离:一个进程崩溃不会影响其他进程
2.IIC\SPI\UART 传输速率、几根线、全双工还是半双工,做一个全面的对比
| 协议 | 线数 | 通信方式 | 速率 | 半/全双工 | 优点 | 缺点 | 
|---|---|---|---|---|---|---|
| I²C (Inter-Integrated Circuit) | 2 根(SCL 时钟,SDA 数据) | 多主多从,总线结构 | 标准模式 100 kbps快速模式 400 kbps高速模式 3.4 Mbps | 串行半双工(同一时刻只能单向传输) | 仅需 2 根线,可挂很多设备(通过地址区分),硬件资源占用少 | 速率较低,线长受限,时序复杂,硬件实现难度大 | 
| SPI (Serial Peripheral Interface) | 至少 4 根(SCLK 时钟,MOSI 主出从入,MISO 主入从出,CS 片选) | 主从结构,一主多从(多从需要多个 CS) | 常见几 Mbps ~ 数十 Mbps,部分可达 100 Mbps | 串行全双工(MISO/MOSI 独立通道,同时收发) | 速度快,协议简单,实现容易 | 线数多(N 个从机需要 N 根 CS),不适合长距离 | 
| UART (Universal Asynchronous Receiver/Transmitter) | 2 根(TX 发送,RX 接收)(可选 GND、RTS/CTS) | 点对点通信(一般一对一) | 常见 9600 ~ 115200 bps最高可到几 Mbps(例如 4~10 Mbps) | 串行全双工(TX 和 RX 独立) | 硬件支持广泛,调试简单,长距离也能用(配合 RS232/RS485) | 不支持多主多从,速率较低,需要精确波特率匹配 | 
速率:SPI > I²C > UART(常规应用)
扩展性:I²C 支持挂多个设备(有地址),SPI 多从需要多个片选,UART 基本就是一对一。
3.uart(ttl\rs232\rs485)
| 特性 | TTL UART | RS232 | RS485(主要是1.组网 2. 抗干扰性好) | 
|---|---|---|---|
| 电平标准 | 逻辑 1: ≈3.3V/5V逻辑 0: 0V | 逻辑 1: -3V ~ -15V逻辑 0: +3V ~ +15V | 差分信号:A-B > +2V → 逻辑 1A-B < -2V → 逻辑 0 | 
| 信号形式 | 单端 | 单端 | 差分 | 
| 线数 | 2~3 根(TX、RX、GND) | 3~9 根(常用 TX、RX、GND,可扩展 RTS、CTS 等) | 2 根 (A、B) + GND(可选),一般需 DE/RE 控制 | 
| 通信方式 | 全双工,点对点 | 全双工,点对点 | 半双工(常见),支持多点总线 | 
| 支持节点数 | 1 对 1 | 1 对 1 | 1 主多从(最多 32 节点,扩展可更多) | 
| 传输距离 | < 1 米 | < 15 米 | ≤ 1200 米 | 
| 典型速率 | 9600 ~ 1 Mbps(常见 115200 bps) | 9600 ~ 115200 bps(少数可到 Mbps) | 300 bps ~ 10 Mbps(距离短时更快) | 
| 抗干扰能力 | 弱 | 一般 | 强(差分传输,工业常用) | 
| 硬件需求 | 直接 MCU GPIO | 需要电平转换芯片(MAX232) | 需要收发器芯片(如 MAX485、SP3485) | 
| 典型应用 | MCU 与模块(GPS、蓝牙、传感器) | 老式 PC 串口、工业设备调试口 | 工业控制、楼宇控制、Modbus RTU、多机通信 | 
4.枚举内存对齐
一般固定为4bytes
            
            
              c
              
              
            
          
          enum Color { RED, GREEN, BLUE }; // 默认 int
enum Color c;                    // 占 4 字节,地址 4 字节对齐
//枚举里放的都是常量 第一个枚举成员的默认值是 0
//如果枚举中放了超过32位的常量,则默认值可能位ul 8 字节 5.数组与链表区别
| 特性 | 数组 | 链表 | 
|---|---|---|
| 内存分配 | 连续 | 不连续,节点可散布在内存任意位置 | 
| 访问方式 | 随机访问,通过下标 O(1) | 顺序访问,需要从头遍历 O(n) | 
| 插入/删除 | 中间位置插入/删除 O(n)(需移动元素) | 已有节点位置插入/删除 O(1)(只需改指针) | 
| 大小 | 固定(静态数组)或可动态扩展(动态数组/Vector) | 可动态增长,大小不固定 | 
| 空间开销 | 元素本身,不需要额外指针 | 每个节点需要额外指针存储链接 | 
| 缓存利用率 | 高,连续内存,CPU cache 友好 | 较低,内存散布,可能导致缓存未命中 | 
| 适合场景 | 随机访问频繁,元素数量已知 | 插入删除频繁,元素数量不确定 | 
| 遍历效率 | 高 | 相对低 | 
| 实现复杂度 | 简单 | 需要维护指针,稍复杂 | 
6.为什么用交叉编译环境工具链
交叉编译(Cross Compilation) :在一种 架构/平台 上编译程序,但生成的可执行文件运行在 另一种架构/平台上。
宿主平台 CPU 架构不同 → 本地编译器不可执行
目标平台库不同 → 需要与目标系统匹配
系统调用不同 → 需要正确的目标头文件和链接规则
7.Makefile 主要包含?
- 变量定义(编译器、选项、路径、文件列表)
- 目标规则(target + dependencies + commands)
- 模式规则(%.o: %.c)
- 伪目标(clean、all 等)
- 条件/包含(条件编译、include 其他 Makefile)
8.关于TCP粘包和UDP丢包
TCP 粘包
- 粘包 :发送方连续发送多条消息,接收方一次 recv却把多条消息"粘"在一起接收了。
- 拆包 :发送方发送的一条大消息被接收方分多次 recv接收。
原因:TCP 是 面向字节流的协议,并不区分消息边界。
解决粘包:自定义协议 ,手动加一个结束标志 ,遇到结束标志,缓冲区刷新
UDP 丢包:发送的数据报在传输过程中丢失,接收方永远收不到
9.0x80800000 -- 0x83000000这个地址是基于什么来说的,是什么上的地址
0x8080_0000 → 内核起始加载地址
0x8300_0000 → 内核结束或内存末尾
这是 物理地址,通常是 DRAM 区域。
数值是 开发板硬件/启动加载策略决定的,不是随机的。
本质就是nandflash启动 -------非可线性寻址
10.dts是怎样配置的
DTS(Device Tree Source) 是一种 硬件描述文件,用于告诉 Linux 内核硬件资源情况。
            
            
              c
              
              
            
          
          / {
    compatible = "vendor,board";
    model = "Board Model";
    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x08000000>; /* 起始地址 + 大小 */
    };
    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "simple-bus";
        uart0: serial@101f1000 {
            compatible = "ns16550a";
            reg = <0x101f1000 0x100>;
            interrupts = <5>;
            clock-frequency = <24000000>;
        };
        led0: gpio-leds {
            compatible = "gpio-leds";
            led0 {
                label = "user_led";
                gpios = <&gpio1 0 0>; /* GPIO 控制 */
            };
        };
    };
};这时候就说用哪些函数,找到节点---找到属性(字符串)-----找到属性里面的设备资源
也可以说 有些属性中 ,有些功能是disabled或者电器属性是默认的, 也是可能要要改变的
11.文件系统启动流程 文件系统启动是用什么控制的,启动方式是什么,怎样修改文件系统的启动流程
1.启动流程
            
            
              c
              
              
            
          
          上电 / 复位
     │
     ▼
BootROM (SoC 内置)
     │
     ▼
U-Boot(Bootloader)
     │
     ├── 加载内核镜像(zImage/uImage)
     ├── 加载设备树(DTB)
     └── 设置启动参数(bootargs)
     │
     ▼
Linux 内核启动
     │
     ├── 初始化硬件驱动
     ├── 挂载根文件系统(rootfs)
     └── 启动 init 进程(PID=1)
     │
     ▼
用户空间(rootfs)启动
     │
     ├── init / systemd / busybox init
     ├── 启动各个服务和应用程序
     └── 进入登录界面或应用程序2.文件系统启动是用什么控制的
启动控制点
	bootargs=root=/dev/mmcblk0p2 rootfstype=ext4 console=ttyS0,115200
内核 init
init 进程3.启动方式-----nandflash-非线程巡回地址
4.修改 U-Boot bootargs
修改 U-Boot bootargs
=> setenv bootargs 'root=/dev/mmcblk0p2 rootfstype=ext4 console=ttyS0,115200'
=> saveenv12.线程邮箱和信号量有什么区别
| 特性 | 信号量 | 邮箱 / 消息队列 | 
|---|---|---|
| 核心作用 | 计数 / 状态标志 | 数据传递 | 
| 存储内容 | 不存数据,仅计数(整型) | 可以存实际数据(结构体、指针、消息) | 
| 同步/互斥 | 是 | 可包含同步功能,但主要用于通信 | 
| 阻塞行为 | wait§ 阻塞直到资源可用 | recv 阻塞直到有消息到来 | 
| 唤醒方式 | 一个任务释放,可能唤醒一个或多个等待任务 | 消息到来即可唤醒阻塞任务 | 
| 典型应用 | 互斥锁、事件标志 | 数据传输、任务通知、生产者-消费者模式 | 
| 粒度 | 通常表示单个资源(1/0 或计数) | 可表示多条数据或复杂结构 | 
| 容量 | 通常一个计数即可 | 可以配置队列长度,支持多条消息缓存 | 
13.为什么选用LVGL LVGL(8.3)和framebuffer有什么关系,为什么不用QT
Framebuffer(帧缓冲区) 是内核中为显示设备(LCD、HDMI屏幕等)提供的一块连续的显存区域,用来存储要显示在屏幕上的每个像素点的数据。
Framebuffer 实际上是内核空间 中的显存,而用户程序(例如你的 fb_show.c)是在用户空间 运行的。
用户空间程序默认不能直接访问内核的物理地址。
mmap()(memory map)系统调用可以将内核空间的物理内存映射到用户进程的虚拟地址空间中。
LVGL(Light and Versatile Graphics Library) 是一个 开源嵌入式 GUI 库
特点:
- 轻量级,适合低资源 MCU/嵌入式 Linux
- 支持 图形控件:按钮、滑条、列表、文本、图表等
- 多平台支持:裸机、RTOS、Linux framebuffer
- 自带图形渲染,可以直接输出到帧缓冲或显示驱动
LVGL 和 Framebuffer 的关系:
- Framebuffer(帧缓冲) :内核提供的一个 内存映射区域,直接对应显示屏像素
- 关系 :
- LVGL 是 GUI 库,生成图形数据(像素缓冲)
- Framebuffer 是 显示接口,把 LVGL 生成的像素渲染到屏幕
 
- 流程示意:LVGL 绘制界面 → 生成像素矩阵 → 写入 Framebuffer → 显示器刷新显示
QT资源占用大,LVGL轻量级,资源占用小
14. mqtt基于什么实现的 tcp在那一层
MQTT(Message Queuing Telemetry Transport) 是一种 轻量级、基于发布/订阅的消息传输协议,适用于物联网(IoT)和嵌入式系统。
- 特点:
- 轻量小巧:协议头只有 2 字节
- 发布/订阅模式:客户端订阅主题,发布数据到主题
- QoS(服务质量) :
- 0:最多一次(消息可能丢失)
- 1:至少一次(消息可能重复)
- 2:正好一次(最可靠,开销大)
 
- 保持会话(Session)和心跳机制(Keep Alive) 一次心跳1.5s
 
mqtt基于TCP协议实现的
为什么 MQTT 要依赖 TCP
- 可靠性要求
- 节省开发成本
- 易于跨网络部署
15.波特率----每秒钟传输的比特数
16.gdb中什么命令,能查看当前进程所有的堆栈信息
            
            
              c
              
              
            
          
          (gdb) bt
#0  foo() at main.c:10
#1  bar() at main.c:20
#2  main() at main.c:3017.GDB如何接管线程,如何进入到程序中去,如何查看某一个函数接口中某接口的值。
            
            
              c
              
              
            
          
          //操作所有线程
查看所有线程堆栈:
	(gdb) thread apply all bt
对所有线程执行命令:
    (gdb) thread apply all info locals18.什么是野指针?如何避免,给空指针赋值,会怎么样?
野指针:指针指向 一个已经释放或未初始化的内存地址
避免方法:
| 方法 | 示例 | 
|---|---|
| 初始化指针 | int *p = NULL; | 
| 释放后置空 | free(p); p = NULL; | 
| 避免返回局部变量地址 | 使用动态分配或全局/静态变量 | 
| 谨慎指针运算 | 不越界访问数组或缓冲区 | 
空指针是 指向 0 的指针,表示"不指向任何有效内存"
            
            
              c
              
              
            
          
          int *p = NULL;
*p = 10;  // 错误:访问空指针 -> 程序崩溃(Segmentation Fault)19.I2C的适配层(Adapter Layer)主要是做什么的?
            
            
              c
              
              
            
          
          应用层/驱动层
       |
   I2C 客户端驱动
       |
  I2C 适配层 (I2C Adapter / Controller Driver)
       |
    硬件总线(SDA/SCL)封装底层硬件操作
- 提供标准接口给 I²C 核心和客户端驱动
驱动注册总线
提供读写接口
20. I2C的常见错误码有哪些?
没注意过
21. I2C的上拉电阻是多少k?I2C为什么要求会有上拉电阻?GPIO一般会设置成什么模式呢(用GPIO模拟I2C)?
I²C 上拉电阻值
- 常见范围:4.7kΩ -- 10kΩ
为什么需要上拉电阻
上拉电阻拉高电平(SDA/SCL = 1)
没有上拉电阻:
- 总线无法恢复高电平
- 信号逻辑错误,通信失败
GPIO 模拟 I²C(Bit-banging)模式
- SDA/SCL 用 普通 GPIO 模拟
22.TCP在内核的缓冲区是多少?有什么办法可以改变缓冲区的大小?
| 缓冲区 | 方向 | 作用 | 
|---|---|---|
| 发送缓冲区(send buffer) | 发送端 | 存放待发送但尚未被对方确认的数据 | 
| 接收缓冲区(receive buffer) | 接收端 | 存放已经到达但尚未被应用层读取的数据 | 
在应用层设置
使用 setsockopt() 修改单个 socket 的缓冲区大小:
            
            
              c
              
              
            
          
          int bufsize = 1024*1024; // 1MB
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));23. 介绍一下iic子系统/iic设备树具体这么配置/iic子系统驱动编写流程
            
            
              c
              
              
            
          
          1. I²C 子系统概览
在 Linux 内核中,I²C 子系统包括三层:
应用层/客户端驱动
       |
   I²C 核心 (i2c-core)
       |
   I²C 适配器驱动 (Adapter / Controller Driver)
       |
      硬件总线 (SDA/SCL)
主要组成
I²C 核心 (i2c-core)
提供标准接口给上层驱动(客户端)
处理 I²C 设备注册、地址查找、总线仲裁
维护 i2c_adapter 和 i2c_client 结构体
I²C 适配器 (Adapter)
也称控制器驱动,封装硬件操作
提供标准读写接口给核心:
i2c_master_send()
i2c_master_recv()
i2c_smbus_read_byte_data()
i2c_smbus_write_byte_data()
处理 START/STOP/ACK/NACK 时序
I²C 客户端驱动
具体外设驱动,如 LM75 温度传感器
使用核心接口操作 I²C 总线
2. I²C 设备树配置示例
在设备树中,I²C 控制器和从设备需要分别描述:
(1)I²C 控制器节点
&i2c2 {              // I2C 控制器
    status = "okay";
    clock-frequency = <100000>; // 100kHz
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2_pins>;
};
&i2c2:引用 SOC I²C 控制器
clock-frequency:设置总线速率
pinctrl:引脚复用配置
(2)I²C 从设备节点
lm75@48 {            // 从设备地址 0x48
    compatible = "national,lm75";
    reg = <0x48>;
};
compatible:驱动匹配字符串
reg:I²C 从机地址
放在 I²C 控制器节点下面:
&i2c2 {
    status = "okay";
    lm75@48 {
        compatible = "national,lm75";
        reg = <0x48>;
    };
};24.main 函数里的argv argc 是什么
| 名称 | 类型 | 含义 | 
|---|---|---|
| argc | int | Argument Count,命令行参数的数量 | 
| argv | char*[] | Argument Vector,指向每个参数的字符串数组 | 
| argv[0] | char* | 程序自身的名字或路径 | 
25.在一个函数定义一个char * 指向一个字符串 return char * 能直接在外面能直接获取吗
            
            
              c
              
              
            
          
          char *getString() {
    char *str = "Hello World";
    return str;
}//可以
char *getString() {
    char str[] = "Hello World";  // ❌ 局部数组
    return str;
}//不可以
//定义在函数栈上,函数返回后栈空间被释放,返回的 str 指针成了"野指针";
int main() {
    char *p = getString();
    printf("%s\n", p);
}26.epoll的边缘触发和水平触发的区别
epoll 的触发模式有两种:
| 模式 | 缩写 | 含义 | 
|---|---|---|
| 水平触发 | LT (Level Triggered) | 文件描述符 只要状态满足,就会一直通知 | 
| 边缘触发 | ET (Edge Triggered) | 文件描述符 状态发生变化时才通知一次 | 
2. 水平触发(LT)
特点
- 默认模式
- 只要缓冲区有数据可读,就会不断触发
- 可读事件未处理完,会再次触发
示例
            
            
              c
              
              
            
          
          char buf[1024];
int n = read(fd, buf, sizeof(buf));- 假设 fd 有 100 字节数据:
- 第一次 epoll_wait 通知,read 10 字节 → 缓冲区还有 90 字节
- 下一次 epoll_wait 仍然会通知,因为缓冲区非空
 
优点
- 使用简单,不需要循环读取所有数据
- 安全可靠,不容易漏掉数据
缺点
- 可能产生 重复通知,效率稍低
3. 边缘触发(ET)
特点
- 高效模式
- 只有文件描述符状态 从无到有变化时,才会触发一次
- 需要非阻塞 I/O,一次通知必须把缓冲区数据读完
示例
            
            
              c
              
              
            
          
          int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 设置非阻塞
char buf[1024];
while (1) {
    int n = read(fd, buf, sizeof(buf));
    if (n <= 0) break; // 没有数据就退出循环
    // 处理 n 字节数据
}- 如果缓冲区还有数据,但未全部读完 → 不会再触发
- 所以必须 循环读直到返回 EAGAIN
优点
- 高性能,减少 epoll_wait 调用次数
- 更适合大并发、高负载网络服务器
缺点
- 使用复杂,必须循环读取/写入,否则可能漏掉数据
- 必须配合 非阻塞 I/O
| 特性 | 水平触发 (LT) | 边缘触发 (ET) | 
|---|---|---|
| 默认模式 | ✅ 是默认 | ❌ 需显式设置 EPOLLET 变成非阻塞 | 
| 触发条件 | 缓冲区非空 / 可写 | 缓冲区状态变化 | 
| 通知次数 | 可能重复通知 | 只通知一次,状态变化才触发 | 
| I/O 模式 | 可阻塞或非阻塞 | 必须非阻塞 | 
| 使用难度 | 简单 | 复杂,需要循环读/写 | 
| 性能 | 较低 | 高性能,适合高并发 | 
27.内核申请内存和应用申请的区别。
| 环境 | 函数/方式 | 说明 | 
|---|---|---|
| 内核态 | kmalloc()/vmalloc()/get_free_pages() | 内核直接操作物理页,返回内核虚拟地址,不能被用户态访问 | 
| 用户态 | malloc()/calloc()/mmap() | 调用 C 库或系统调用,返回用户态虚拟地址,可被应用直接访问 | 
- 内存类型与特性
内核态
- 连续内存 :
- kmalloc(size, GFP_KERNEL)→ 分配连续物理内存(小块,通常 <1MB)
- 用于 DMA 或设备访问
 
- 非连续内存 :
- vmalloc(size)→ 分配虚拟连续、物理不连续的内存(大块)
 
- 零初始化 :
- kzalloc()→ kmalloc + memset(0)
 
用户态
- 堆内存 :
- malloc()→ 进程自己的虚拟地址空间
 
- 匿名映射 :
- mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0)
 
- 栈内存 :
- 函数局部变量分配在栈,随函数调用和返回自动分配/释放
 
kmalloc、vmalloc、malloc 的区别
| 函数 | 适用空间 | 分配内存性质 | 连续性 | 大小限制 | 睡眠风险 | 使用场景 | 
|---|---|---|---|---|---|---|
| kmalloc | 内核空间 | 物理地址连续 | 物理连续+虚拟连续 | 通常较小(页的倍数以内) | 不可睡眠(GFP_ATOMIC 时可原子上下文) | 需要物理连续的小块内存,如 DMA、设备缓冲区 | 
| vmalloc | 内核空间 | 虚拟地址连续,物理不一定连续 | 虚拟连续,物理不连续 | 较大(可分配多页) | 可能睡眠(因需调整页表) | 大块内存申请,不要求物理连续,如模块加载、大型数据缓冲区 | 
| malloc | 用户空间 | 虚拟地址连续,物理不一定连续(由内核 brk/mmap 等实现) | 虚拟连续 | 受进程虚拟空 | 
28.malloc和calloc的区别?
| 函数 | 功能 | 
|---|---|
| malloc(size_t size) | 分配一块指定大小的内存,但不初始化,内存中的值是随机的(原先的残留数据) | 
| calloc(size_t nmemb, size_t size) | 分配 nmemb 个元素,每个元素大小为 size 的内存,并将所有字节初始化为 0 | 
| void *realloc(void *ptr, size_t new_size); | realloc用于 改变已分配内存块的大小。 | 
            
            
              c
              
              
            
          
          void *realloc(void *ptr, size_t new_size);
`ptr`:原来的内存指针(可以是 `malloc` 或 `calloc` 分配的内存)
`new_size`:新的字节数
返回值:新的内存首地址(可能和原来相同,也可能不同)
如果失败返回 `NULL`,原来的内存块不会释放29.检查内存泄漏--------Valgrind
30. Linux启动流程 uboot启动流程
Linux 启动流程
| 阶段 | 主要工作 | 关键点 | 
|---|---|---|
| 1. 上电 / 复位 (Reset) | CPU 复位,执行预定义的启动地址指令(Boot ROM) | CPU 初始化寄存器,设置堆栈,加载第一条指令 | 
| 2. Boot ROM / Mask ROM | 内部只读存储器中的代码执行,通常查找外部启动介质(NAND/SD/MMC/Serial Flash) | 负责加载 U-Boot 或 SPL(Second Program Loader)到 SRAM | 
| 3. Bootloader(如 U-Boot) | 初始化外设(DDR、时钟、UART、GPIO、网口)、加载内核镜像和设备树到内存 | 包括 SPL(小型初始化)和完整 U-Boot;提供控制台和环境变量;可选择启动方式 | 
| 4. Linux 内核 | 解压内核镜像(zImage/uImage),初始化内核各子系统(内存管理、调度器、文件系统、驱动) | 内核初始化完成后挂载根文件系统(RootFS) | 
| 5. Init / Systemd | 启动用户空间进程,执行 init或systemd,加载系统服务 | 配置网络、启动守护进程、挂载存储等 | 
| 6. 用户程序 | 用户应用程序运行 | 完整系统启动完成,可交互或运行应用任务 | 
U-Boot 启动流程
| 阶段 | 主要工作 | 说明 | 
|---|---|---|
| 1. CPU 上电 / Boot ROM | 复位 CPU,执行 Mask ROM | Boot ROM 从 Flash/NAND/SPI/NAND 读取 SPL(或完整 U-Boot)到 SRAM | 
| 2. SPL(Secondary Program Loader,可选) | 初始化 DDR、堆栈、UART | 主要作用:初始化最低限度硬件,为完整 U-Boot 做准备;大小受 SRAM 限制 | 
| 3. U-Boot 主程序 | 初始化外设(串口、网口、MMC、USB、SPI 等),提供命令行接口 | 可设置环境变量(bootargs、bootcmd),选择启动内核或进入命令行 | 
| 4. 加载内核 / RootFS | 从存储介质加载内核镜像(zImage/uImage)和设备树(.dtb)到内存 | 支持网络启动(TFTP)、Flash 启动等 | 
| 5. 跳转到内核入口地址 | 将控制权交给 Linux 内核 | 设置好 CPU 寄存器(R0、R1、R2 或 ARM64 的 x0~x2)、设备树地址等 | 
先启动U-BOOT 再启动linux
| 阶段 | 内容 | 责任/作用 | 是否属于启动流程 | 
|---|---|---|---|
| 构建内核 | make uImage / zImage | 编译生成内核镜像 | ❌ 构建阶段,不是启动 | 
| Bootloader | U-Boot / SPL | 初始化硬件、加载内核镜像、设置启动参数 | ✅ U-Boot 启动流程 | 
| Linux 内核 | 解压内核、初始化子系统、挂载 rootfs、启动 init | 内核自身初始化 | ✅ Linux 启动流程 | 
| 驱动 / rootfs | 编译驱动、写到 rootfs | 驱动功能 | ❌ 构建阶段,驱动运行属于 Linux 内核启动或用户空间初始化 | 
U-Boot 启动流程 = CPU 上电 → Bootloader 初始化 → 加载内核 → 跳转
Linux 启动流程 = 内核接管 → 初始化子系统 → 挂载 rootfs → 启动 init
make uImage / 编写驱动 只是准备工作,不属于"启动流程",启动流程发生在 Bootloader 和内核执行时
31.解析HTTP报文/GET和POST的区别
一、HTTP 报文解析
HTTP 报文分两大类:
- 请求报文(客户端 → 服务器)
- 响应报文(服务器 → 客户端)
- 
HTTP 请求报文结构 请求行 
 请求头部
 请求体(可选)
举例:
            
            
              c
              
              
            
          
          GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: curl/7.68.0
Accept: */*- 请求行 :GET /index.html HTTP/1.1- 方法(GET/POST/PUT/DELETE...)
- 路径(/index.html)
- 协议版本(HTTP/1.1)
- 换行符
 
- 请求头部 :键值对,如 Host、User-Agent、Content-Length等
- 请求体:GET 一般没有,POST 常有(提交的数据,如 JSON 或表单)。
- 
HTTP 响应报文结构 状态行 
 响应头部
 响应体
举例:
            
            
              c
              
              
            
          
          HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 123
<html> ... </html>- 状态行:协议版本 + 状态码 + 状态描述
- 响应头部 :服务器返回的信息,比如 Content-Type
- 响应体:真正的数据内容(HTML、JSON、图片等)。
- 报文解析思路
假设你写一个 HTTP 解析器
- 读取第一行:区分请求行 / 状态行
- 逐行解析头部,存储到哈希表/字典(直到遇到空行)
- 如果有 Content-Length,继续读取后续字节作为 body
- 应用层根据 body 的格式(JSON、表单、HTML)再进一步解析。
二、GET 和 POST 的区别
| 特性 | GET | POST | 
|---|---|---|
| 参数位置 | URL 中,如 GET /search?q=abc | 请求体中,如 q=abc | 
| 数据长度 | 受 URL 长度限制(通常 2K-8K 左右,浏览器限制) | 理论上无限制(受服务器配置约束,如 nginx 的 client_max_body_size) | 
| 安全性 | 参数暴露在 URL,不安全(容易被缓存/记录) | 参数在请求体中,相对安全(但依然需要 HTTPS 保证传输安全) | 
| 缓存 | 浏览器和代理可缓存 | 默认不缓存 | 
| 使用场景 | 获取资源(查询) | 提交数据(表单、上传文件、登录注册) | 
12、http交互报文的格式?
请求报文:
请求行:方法(get/post)+URL+HTTP版本号+回车换行\r\n
请求头:状态说明信息(键值对)
请求体:请求实体(一般为空)
            
            
              tex
              
              
            
          
          GET:GET请求主要用于从服务器获取数据。它将请求参数附加到URL中
POST:POST请求通常用于向服务器提交数据以进行处理。POST请求将数据包含在请求体中。响应报文:
响应行:HTTP版本+状态码+原因短语
响应头:响应信息(键值对)
响应体:服务器返回的实际内容
32.栈区的内存是如何管理的
- 栈的管理方式
栈采用 连续的内存块 ,遵循 先进后出 (LIFO, Last In First Out) 的原则。
- 栈指针(SP / RSP 在 x86_64 上)记录当前栈顶位置
- 每次 函数调用时,系统会为该函数分配一块栈帧(Stack Frame)
- 每次 函数返回时,栈指针恢复,栈帧销毁
这意味着:栈的分配和释放都是自动完成的,不需要程序员管理。
33.SPI的原理
1. SPI 的定义
SPI(Serial Peripheral Interface,串行外设接口)是一种 全双工、同步、主从结构 的串行通信总线。
- 由 Motorola 提出,广泛应用于 MCU 和外设之间的高速通信(如 Flash、ADC、LCD、传感器)。
- 特点是 简单、速度快、点对点或一主多从。
2. SPI 的硬件组成
典型的 SPI 总线有 4 根信号线:
| 信号线 | 方向 | 作用 | 
|---|---|---|
| SCLK(Serial Clock) | 主机输出 | 时钟信号 | 
| MOSI(Master Out Slave In) | 主机 → 从机 | 主机发送的数据 | 
| MISO(Master In Slave Out) | 从机 → 主机 | 从机返回的数据 | 
| CS/SS(Chip Select/Slave Select) | 主机输出 | 片选信号,低电平有效,决定哪个从机被选中 | 
有些应用中会扩展成 3 线 SPI(省去 MISO/MOSI 的其中一个)或者 2 线 SPI(双向数据线 + 时钟)。
3. SPI 的工作原理
1)主从关系
- 主机(Master):产生时钟 SCLK,控制通信的开始/结束
- 从机(Slave):接收时钟,响应主机
2)通信过程
- 主机拉低对应从机的 CS 脚,表示选中某个外设
- 每个时钟周期,MOSI 和 MISO 各传输一位数据
- 因此 SPI 是 全双工:同一个时钟周期里,主机和从机可以同时发送和接收数据
3)时钟极性和相位(CPOL & CPHA)
SPI 有 4 种模式 (Mode 0~3),由 时钟极性 CPOL 和 时钟相位 CPHA 决定:
- CPOL = 0:空闲时 SCLK 为低电平
- CPOL = 1:空闲时 SCLK 为高电平
- CPHA = 0:在第一个时钟边沿采样
- CPHA = 1:在第二个时钟边沿采样
| 模式 | CPOL | CPHA | 描述 | 
|---|---|---|---|
| Mode 0 | 0 | 0 | 空闲低电平,上升沿采样 | 
| Mode 1 | 0 | 1 | 空闲低电平,下降沿采样 | 
| Mode 2 | 1 | 0 | 空闲高电平,下降沿采样 | 
| Mode 3 | 1 | 1 | 空闲高电平,上升沿采样 | 
不同外设要求的模式可能不同,主机要匹配。
4. SPI 的特点
- 
优点 - 全双工,速度快(MHz 级别,常见 10Mbps~100Mbps)
- 硬件简单(只需 4 根线)
- 支持一主多从(通过片选控制)
 
- 
缺点 - 
没有标准的流控机制 
- 
没有统一的协议层(不像 I²C 有地址机制),需要额外协议或片选管理 
- 
线多,不适合长距离通信(一般几十厘米以内) 
 
- 
35.
34.CAN通信
1. CAN 定义
CAN(Controller Area Network,控制器局域网)是一种 多主机(Multi-Master)、串行通信总线,最早由 Bosch 公司为汽车电子开发,现在广泛用于工业控制、医疗设备等。
特点:
- 多主机竞争,没有固定主从
- 非破坏性仲裁(谁的 ID 优先级高,谁赢)
- 可靠性高(支持 CRC、ACK、错误检测)
- 实时性强(基于优先级仲裁机制)
2. CAN 硬件结构
CAN 总线上只有两根通信线:
- CAN_H(高电平线)
- CAN_L(低电平线)
采用 差分信号传输:
- 逻辑 0(显性位,Dominant):CAN_H≈3.5V,CAN_L≈1.5V,差分电压≈2V
- 逻辑 1(隐性位,Recessive):CAN_H≈2.5V,CAN_L≈2.5V,差分电压≈0V
这种方式能有效抗干扰,适合恶劣环境(汽车发动机舱、工业车间)。
3. CAN 通信机制
1)数据帧结构
标准 CAN 报文主要有以下几类帧:
- 数据帧(Data Frame):真正传输数据
- 远程帧(Remote Frame):请求数据
- 错误帧(Error Frame):错误检测
- 过载帧(Overload Frame):暂缓传输
数据帧格式(标准帧,11 位 ID):
起始位(1bit)
仲裁段(11bit ID + RTR 位)
控制段(6bit)
数据段(0~8 字节)
CRC 段(15bit + 1bit ACK)
结束段(7bit)2)仲裁机制(非破坏性竞争)
- 多个节点同时发送时,比较仲裁段(ID 越小优先级越高)。
- 如果某个节点发送的是隐性位(1),但总线上是显性位(0),说明输了,立刻停止发送。
- 这样不会造成冲突丢包,保证高优先级报文先发送。
3)错误检测机制
CAN 内置多种错误检测:
- 位错误
- 填充错误(连续 5 个相同位时,自动插入补位)
- CRC 错误
- 确认错误
- 格式错误
出错节点会自动发送错误帧,全网知晓,并重发报文,保证数据可靠。
4. CAN 的特点
✅ 优点
- 多主机,优先级仲裁机制 → 实时性强
- 差分信号传输 → 抗干扰强
- 内置错误检测机制 → 高可靠
- 支持 热插拔,方便扩展
❌ 缺点
- 速率相对有限(典型 125kbps~1Mbps)
- 数据帧负载小(最多 8 字节,CAN FD 扩展到 64 字节)
- 通信距离受速率限制(1Mbps ≤ 40m,125kbps ≤ 500m)
36.看门狗是什么?为什么要看门狗?
看门狗(Watchdog Timer) 是一个 定时器 ,
用来"监视"系统是否正常运行。
如果系统长时间没有喂狗(即软件没响应),看门狗就会自动复位系统。
为什么看狗
| 原因 | 说明 | 
|---|---|
| 防止程序跑飞 / 死机 | 当程序陷入死循环或卡死时,系统能自动复位 | 
| 提升系统可靠性 | 保证设备长期无人值守运行(例如工控、路由器、传感器) | 
| 硬件自动恢复机制 | 避免软件逻辑错误导致设备永久失效 | 
| 异常检测机制 | 也可通过看门狗中断记录异常前状态(若启中断模式) | 
37.栈溢出是什么?为什么会?什么情况下会?
**栈溢出:**程序使用的栈空间超过了系统为它分配的范围
为什么:
| 原因 | 说明 | 
|---|---|
| ① 递归调用太深 | 每次函数调用都要分配栈帧,深度太大会耗尽栈 | 
| ② 局部数组太大 | 在栈上定义大数组(如 char buf[100000];)占用太多空间 | 
| ③ 死循环中不断调用函数 | 没有返回的函数调用不断压栈 | 
| ④ 函数嵌套太多 | 多层函数调用层次深(栈帧叠加) | 
| ⑤ 栈空间设置太小 | 在 RTOS 或嵌入式系统中,每个任务的栈手动配置过小 | 
| ⑥ 指针错误 | 错误修改了栈指针(SP),导致越界写入 | 
38.中断函数的规则
中断函数由硬件调用,不是用户代码调用,因此:
不能有参数,也不能有返回值。
            
            
              c
              
              
            
          
          __interrupt double compute_area (double radius) 
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
//1.返回类型 double,参数 double radius
//2.中断服务程序中通常禁止使用 printf() 等阻塞函数,ISR 要求尽快返回,39.Linux启动流程/u-boot传参函数
Linux启动流程
- 
启动U-Boot :(用来触发相关硬件:比如CPU的工作模式,触发内存,时钟,异常向量表...),把内核镜像(uImage)搬移到内存,引导内核启动 
- 
内核跳转到入口点:start_Kenel函数(是初始化函数,控制工作台) 
- 
启动内核,挂载根文件系统,挂载好执行init进程,是用户态的第一个进程,对应去执行rcS文件(第一个脚本文件,会完成内核以外的所有系统准备工作),再执行shell 
U-Boot传参有哪些参数
| 类型 | 内容 | 作用 | 
|---|---|---|
| 机器信息 | 板级 ID、启动方式(NAND/NOR/SD) | 内核识别平台 | 
| 启动参数 | 一系列环境变量(如 bootargs) | 内核解析系统配置 | 
环境变量里面就有:bootargs/bootcmd/serverip/bootfile/ipaddr 还包括用的是什么设备ttySAC0(115200)
U-Boot传参是r0,r1,r2都是传的是什么值
| 寄存器 | 传递内容 | 说明 | 
|---|---|---|
| r0 | 0 | 保留,Linux 内核规定 r0 必须为 0 | 
| r1 | 内核启动模式 | 指示内核的启动类型,通常:0xFFFFFFFF 表示 ARM启动模式 | 
| r2 | 地址(因为是单向传输) | 传递内核启动参数(命令行、内存描述、根文件系统信息) | 
40.线程邮箱主要处理的是什么
1.解耦合:发送消息的线程和接收消息的线程,不需要同时运行,也不需要直接调用对方代码。
2.缓冲区:临时存储消息,平滑速率差异,实现安全同步
41.一起调试gdb?/gdb怎么跳转另一个文件
1.用makefile gcc - g 提供GDN调试入口
            
            
              shell
              
              
            
          
          (gdb) list other.c:142.为什么创建线程池,为什么不用信号量
为了解决多线程的资源消耗问题,这样就不用频繁的pthread_creat和pthread_destory,都是 系统调用 ,资源消耗多,也可以动态调整线程池 通过if-else 判断空闲线程和忙碌线程的比例
43.内存对齐什么意思
内存对齐 指的是 数据在内存中的起始地址必须满足特定规则,通常是"数据大小的整数倍"。
44.动态库 。和静态库,以及他们的后缀是什么
| 类型 | 名称 | 编译方式 | 特点 | 文件后缀 | 
|---|---|---|---|---|
| 静态库 | Static Library | 编译时直接链接进可执行文件 | 独立运行,不依赖外部库 | .a(Linux) | 
| 动态库 | Shared Library | 程序运行时加载 | 可多个程序共享,占空间小 | .so(Linux) | 
45.结构体数据怎么清0
            
            
              c
              
              
            
          
          Sensor s1;
memset(&s1, 0, sizeof(Sensor));46...如果串口中断和定时器中断同时到达,会触发哪个?
| 中断类型 | 控制方式 | 优先级可调 | 备注 | 
|---|---|---|---|
| FIQ (Fast Interrupt) | 快速中断 | 最高优先级 | 一般保留给紧急事件 | 
| IRQ (Normal Interrupt) | 普通中断 | 可在 GIC 里配置 | 一般外设(UART、Timer)都属于这个类型 | 
由于是都是IRQ普通中断,默认是Timer的编号小,先执行
47.怎么看系统有多少内存,怎么看内核是什么版本的
            
            
              c
              
              
            
          
          top
free -h
cat /proc/meminfo
------------------
uname -r48.内存对齐什么意思-内存对齐大小多大
内存对齐(Memory Alignment)
是指在内存中存放数据时,按照一定的边界(字节边界)来存放数据,让 CPU 访问数据的速度更快。
具体对齐看结构体内存对齐
49...内核链表和普通链表区别
| 对比项 | 普通链表 | Linux 内核链表 | 
|---|---|---|
| 数据与节点关系 | 节点中直接包含数据 | 节点只保存指针,数据结构中嵌入链表节点 | 
| 方向 | 单向或双向 | 双向循环链表 | 
| 是否循环 | 一般不是循环链表 | 默认循环链表(head->next = head) | 
| 通用性 | 专用于某个数据类型 | 可嵌入任意结构体,极高复用性 | 
| 内核支持 | 否 | ✅ Linux内核广泛使用(进程、驱动、设备、文件系统) | 
50...中断可能重复吗
可能,比如裸机开发中,源挂起寄存器没有清除标志位,就会导致中断一直重复
51.linux操作系统的认识
标准 Linux 是分时性操作系统
 分为两大部分:内核,用户空间
 特点:多任务,多用户,稳定,可靠,网络功能强大,一切皆文件
52.linux内核有哪些模块,哪个认识深刻
| 模块类别 | 主要功能 | 示例 | 
|---|---|---|
| 1️⃣ 进程管理(Process Management) | 管理进程的创建、调度、切换、终止等 | 调度器(CFS)、 fork()、exec() | 
| 2️⃣ 内存管理(Memory Management) | 管理虚拟内存、页表、缓存、内核空间分配 | Buddy系统、Slab分配器、 mmap | 
| 3️⃣ 文件系统(File System) | 提供文件与目录接口,支持多种文件系统格式 | VFS、ext4、FAT、procfs、sysfs | 
| 4️⃣ 设备驱动(Device Drivers) | 统一访问硬件设备的接口 | 字符设备、块设备、网络设备驱动 | 
| 5️⃣ 网络子系统(Networking Subsystem) | 实现各种网络协议栈 | TCP/IP、UDP、Socket层、Netfilter | 
| 6️⃣ 中断与异常(Interrupt & Exception) | 管理硬件中断和异常处理 | IRQ、软中断、工作队列 | 
| 7️⃣ 内核同步与通信(Kernel Synchronization & IPC) | 实现并发控制与内核通信 | 信号量、互斥锁、自旋锁、wait_queue | 
| 8️⃣ 内核配置与引导(Init & Boot) | 内核启动初始化、模块加载、引导配置 | initcall、 init/main.c | 
| 9️⃣ 安全与权限管理(Security Subsystem) | 用户权限、SELinux、Capability 管理 | LSM、安全钩子机制 | 
| 🔟 系统调用接口(System Call Interface) | 用户空间与内核空间的接口 | sys_call_table、sys_open()等 | 
对设备驱动用的多
53.根文件系统的认识,核心是什么
Linux 系统启动后 第一个被挂载的文件系统,
 根文件系统是整个用户空间的入口:初始化用户空间和挂载其他文件系统
54.s3c2440的cpu参数
| 参数 | 说明 | 
|---|---|
| CPU 核心 | ARM920T (ARM9) | 
| CPU 架构 | ARMv4T | 
| 工作频率 | 400 MHz(S3C2440A/B 常见频率可达 400MHz) | 
| 指令集 | ARM 指令 + Thumb 指令集 | 
| 流水线 | 5 级流水线 | 
| 缓存 | 16 KB 指令缓存 + 8 KB 数据缓存(片上) | 
| MMU | 支持内存管理单元(虚拟地址映射) | 
| 中断控制器 | 内嵌 VIC(Vector Interrupt Controller) | 
| 总线接口 | AMBA 2.0(AHB + APB) | 
55.内核进线程区别
| 类型 | 定义 | 执行空间 | 
|---|---|---|
| 内核线程(Kernel Thread) | 运行在内核空间,由内核调度,不依赖用户空间程序 | 内核空间 | 
| 用户线程 / 进程线程(User Thread) | 运行在用户空间,依赖进程的虚拟地址空间,由内核调度 | 用户空间(+内核态切换) | 
 内核线程是内核空间的线程,用于处理系统级任务;用户线程是用户空间的线程,用于执行应用程序。
 内核线程权限高、无需系统调用切换,用户线程安全但需要系统调用。
56.标准IO/文件IO区别
| 区别 | 标准 I/O ( stdio) | 文件 I/O(系统调用) | 
|---|---|---|
| 接口 | fopen(),fclose(),fread(),fwrite(),fprintf(),fscanf() | open(),close(),read(),write() | 
| 缓冲 | 有缓冲区(默认 4KB / 8KB,可行设置) | 无缓冲,直接调用内核 | 
| 效率 | 对小数据读写效率高(可减少系统调用次数) | 大量小数据读写频繁系统调用,效率低 | 
| 灵活性 | 功能丰富(格式化、文件定位、行缓冲等) | 灵活低,但可精确控制文件偏移和权限 | 
| 错误处理 | 返回 EOF或ferror(),易用 | 返回 -1 并设置 errno,需要手动判断 | 
| 适用场景 | 应用程序开发、文本处理、跨平台 | 嵌入式开发、底层驱动、对性能要求高的二进制文件操作 | 
| 可移植性 | 高(标准库) | 依赖操作系统,移植性低 | 
57...驱动程序出问题的时候你是怎么调试的
            
            
              c
              
              
            
          
          1.使用 printk() 输出日志
2.查看内核日志	dmesg
3.内核的gdb  需要移植
4.用debug宏58.说一下互斥锁和信号量的区别和使用场景,说一下信号量的底层原理。
 互斥锁 是用于 保护共享资源 的锁,每次 只能有一个线程/进程持有锁 ,如果锁已经被占用,其他线程 阻塞等待
 信号量是一个 计数器,用于控制对共享资源的访问(PV操作)
 二值信号量
 数信号量:控制多个相同资源的访问数量
| 特性 | 互斥锁 mutex | 信号量 semaphore | 
|---|---|---|
| 类型 | 二值锁 | 计数器锁(可>1) | 
| 目的 | 互斥访问资源 | 互斥或同步、计数控制 | 
| 是否可睡眠 | 可阻塞(睡眠等待) | 可阻塞(睡眠等待) | 
| 使用场景 | 单个资源保护 | 多资源控制、同步 | 
| 是否可递归 | 支持递归 mutex | 不支持递归 | 
信号量 既可用于互斥 ,也可用于同步和资源计数
59.公司规范不允许使用 static 的情况
        
            
            
              c
              
              
            
          
          //使用全局变量或结构体保存状态
//使用命名空间前缀,int mymodule_g_value = 100;	//每次都要判断
//函数封装访问   //不要用错函数60.介绍socket套接字
| 类型 | 描述 | 协议 | 使用场景 | 
|---|---|---|---|
| 流式套接字(SOCK_STREAM) | 面向连接,可靠传输 | TCP | 文件传输、HTTP、远程登录 | 
| 数据报套接字(SOCK_DGRAM) | 无连接,不保证可靠性 | UDP | 视频/音频实时传输、DNS | 
| 原始套接字(SOCK_RAW) | 直接访问 IP 层 | IP | 网络工具(ping、traceroute)、自定义协议 | 
61.共享内存什么作用,共享内存的特点?
共享内存 是 进程间通信(IPC)的一种方式,允许多个进程直接访问同一块物理内存区域,实现数据交换
| 特性 | 描述 | 
|---|---|
| 高速 | 进程直接访问物理内存,不经过内核数据拷贝 | 
| 需要同步 | 多进程同时访问时,需要使用 信号量/互斥锁 避免竞态条件 | 
| 固定大小 | 内存段需要提前申请固定大小 | 
| 跨进程 | 只要映射成功,任意进程都可以访问该内存区域 | 
| 生命周期 | 受内核管理 ,使用 shmget()/shmat()分配和映射,最后shmdt()解除映射 | 
| 速度快但安全性低 | 共享数据可被任意进程修改,需要额外同步控制 | 
62.举例链表和队列的应用场景
链表是一种动态存储结构 ,每个节点包含数据和指向下一个节点的指针。,链表适用于:频繁插入/删除、不确定数量、不要求随机访问 的场景。
eg:自定义容器结构,内核链表(Linux 内核)
队列是一种 先进先出(FIFO) 的数据结构。队列适用于:按顺序处理数据、异步通信、缓冲数据流 的场景。
eg:生产者-消费者模型,多线程通信,消息队列
63.const和define的区别,这两个关键字那个更适合修饰跨文件全局常量-const和define那个关键字会分配内存
| 对比点 | const | #define | 
|---|---|---|
| 类型检查 | 有类型检查(编译器能检查类型是否匹配) | 无类型检查(仅做文本替换) | 
| 作用域 | 遵守作用域规则(如局部变量、全局变量) | 无作用域概念,预处理阶段全局有效 | 
| 存储方式 | 在内存中分配空间(常量区或栈中) | 仅作宏替换,不占内存 | 
| 调试支持 | 可以在调试器中查看其值 | 无法在调试器中直接看到宏值 | 
| 定义阶段 | 编译阶段生效 | 预处理阶段生效 | 
| 语法安全 | 支持类型限定、const修饰符 | 无类型安全,容易出错 | 
| 适用场景 | 程序内的真正常量(如 const int a = 10;) | 条件编译、简单宏定义等(如 #define MAX 100) | 
const 更合适
| 项目 | 是否分配内存 | 说明 | 
|---|---|---|
| const | ✅ 可能分配 | 如果编译器不能优化(如被取地址),会在常量区或栈区分配内存 | 
| #define | ❌ 不分配 | 宏展开仅是文本替换,不产生内存实体 | 
64.11.查看磁盘的使用情况用哪个
            
            
              shell
              
              
            
          
          df  -h65.你项目中的Linux内核编译出来是什么格式
| 文件名 | 含义 | 用途 | 
|---|---|---|
| vmlinux | 内核的原始 ELF 可执行文件 | 用于调试(gdb) | 
| zImage | 压缩后的内核镜像 | 用于 Bootloader 启动 | 
| uImage | 带有 U-Boot 头的 zImage | U-Boot 识别并加载的镜像 | 
| Image | 未压缩的内核映像 | 一般不用直接烧录 | 
我们用的是uImage---tftp
66.U-Image是压缩的还是非压缩的
本身不是压缩算法的结果 ,它是 在 zImage 基础上增加了 U-Boot 头部(header) 的内核镜像。
67.内核解压是在启动流程的什么时候、由谁完成的
启动u-boot后,进入内核入口后,进入start_kernel之前
 zlamge本身就包含了解压代码,解压是内核自身完成的,不依赖 U-Boot 做解压
68.U-Boot中的 boot command 和 boot args 这两个概念是什么意思?
 bootcmd 是 U-Boot 的 自动执行命令 ,主要作用是 加载内核镜像、设备树、根文件系统 到内存,并启动内核。
 bootargs ------ 内核启动参数,U-Boot 传给 Linux 内核的启动参数
69.bootz作用
bootz 是 U-Boot 中用于启动压缩格式为 zImage 的 Linux 内核 的命令。
 :把内核映像(zImage)从内存加载地址启动起来,并传递设备树和 ramdisk 给内核。
            
            
              shell
              
              
            
          
          bootz 0x80800000 - 0x8300000070.ping命令通信过程
            
            
              c
              
              
            
          
          1.构造 ICMP 报文
2.发送 ICMP 包
3.目标主机接收并响应
4.源主机接收 ICMP 回复71.tcp udp可以用同一个端口嘛
可以
 TCP 和 UDP 是两种不同的传输层协议 ,操作系统在区分连接时,会把 协议类型 也作为识别的一部分。
72.逻辑分析仪主要分析什么
采集并分析数字电路中多路信号的时序变化(高低电平变化),帮助开发者验证逻辑关系、通信协议、时序同步等问题。
73.probe函数里面主要干些什么
驱动与设备匹配成功后 被内核自动调用的核心函数。可以说,它是驱动的"入口函数",主要负责初始化设备和驱动的绑定关系。
74.如何加载到probe函数?如果设备没有probe到你会考虑从哪些方面排查?如果设备prob到了,但屏幕没有亮应该怎么做?
            
            
              tex
              
              
            
          
          probe() 函数不会被手动调用,而是由 Linux 内核自动触发
	前提:驱动匹配到了对应的设备
	
1.驱动没匹配上
	设备树 compatible 不匹配
	名字不对,注册表不对,已经被注册了,总线被占用了
2.驱动匹配了但是probe 未触发
	看日志		dmesg | tail
	
初始化工作没有做好,从头来75.怎么查看进程
            
            
              c
              
              
            
          
          ps -ef
ps aux76.Ip、网关、子网掩码介绍一下
IP:网络中 唯一标识一台主机 的地址
子网掩码:决定 IP 地址的哪一部分是 网络号 ,哪一部分是 主机号
网关:网络出口,通常是路由器的 IP 地址。
77.memset的用法和接口定义
            
            
              c
              
              
            
          
          void *memset(void *s, int c, size_t n);| 参数 | 类型 | 作用 | 
|---|---|---|
| s | void * | 指向要填充的内存起始地址 | 
| c | int | 要填充的值,会被转为 unsigned char | 
| n | size_t | 要填充的字节数 | 
78...中断服务函数为什么不能传参,为什么不能返回值
| 特性 | 原因 | 
|---|---|
| 不能传参 | 硬件直接跳转到中断向量,调用方无法提供参数 | 
| 不能返回值 | CPU 不关心 ISR 返回值,没有接收者 | 
79.死锁
 在多线程/多进程系统中,两个或多个线程/进程因争夺资源而互相等待,导致 都无法继续执行 的状态。
80.为什么互斥锁比信号量要好一些
在多数操作系统中,互斥锁比信号量更轻量
内核和线程库对互斥锁做了特殊优化
81. IIC和SPI那个快,为什么快
时钟频率更高
🔹SPI: 推挽输出,电平切换快,全双工传输
🔹I²C: 开漏输出,上拉恢复慢(RC延迟大),单线半双工,同一条 SDA 线上要轮流发送和接收数据,效率自然低。
82.UART的数据包是什么
| 内容 | 含义 | 
|---|---|
| 帧头(Header) | 同步、标识数据起始,如 0xAA 0x55 | 
| 长度字段 | 表示数据长度 | 
| 数据字段 | 实际传输内容 | 
| 校验字段 | CRC、异或校验等 | 
83.uart的校验位怎么设置的
| 校验方式 | 说明 | 示例(数据 0x5A = 01011010) | 
|---|---|---|
| 无校验(None) | 不检测错误 | 不加校验位 | 
| 偶校验(Even) | 让"1"的总个数为偶数 | 01011010 → 有 4 个 1(偶数),校验位=0 | 
| 奇校验(Odd) | 让"1"的总个数为奇数 | 01011010 → 有 4 个 1(偶数),校验位=1 | 
| 标记校验(Mark) | 校验位始终为 1 | 永远输出 1 | 
| 空白校验(Space) | 校验位始终为 0 | 永远输出 0 | 
84.S3C2440主频最高能到多少?项目中设置到多少?存储空间有多少?
| 参数 | 描述 | 
|---|---|
| 核心 | ARM920T(ARM9 架构) | 
| 最高主频 | 400 MHz(根据芯片版本和晶振不同可能略有差异) | 
| 项目常用设置 | 203 MHz ~ 266 MHz 较常见,视功耗与稳定性而定 | 
(1) 内部 SRAM / SDRAM
| 类型 | 容量 | 说明 | 
|---|---|---|
| 内部 SRAM | 16 KB(SRAM on-chip) | 用于启动或寄存器缓存 | 
| 外部 SDRAM | 64 MB ~ 128 MB | 主系统运行内存,Linux 可用 RAM | 
(2) 闪存 / ROM
| 类型 | 容量 | 说明 | 
|---|---|---|
| NOR Flash | 512 KB ~ 2 MB | 可执行启动代码(Bootloader) | 
| NAND Flash | 32 MB ~ 128 MB | 存储文件系统、应用程序 | 
85...volitale作用,在哪里使用,举个例子;比如a=1,a=2,a=3,这样我该怎么优化?
            
            
              c
              
              
            
          
          a = 1;
a = 2;
a = 3;
编译器会发现前两次赋值没有被使用,可能直接优化成:如果 a 是普通变量,编译器优化是合法的。
如果 a 是 硬件寄存器或者中断共享变量 ,你希望每次赋值都生效,就应该加上 volatile
            
            
              c
              
              
            
          
          volatile int a;
a = 1;
a = 2;
a = 3;