五月份嵌入式面试总结

目录

1、札记

[1.1、芯片的bring up 主要做哪些工作:](#1.1、芯片的bring up 主要做哪些工作:)

2、Linux驱动八股文

中断与同步互斥

[2.1.1 内核同步互斥的几种方式](#2.1.1 内核同步互斥的几种方式)

[2.1.2 互斥锁和自旋锁的区别](#2.1.2 互斥锁和自旋锁的区别)

[2.1.3 spin_lock 和 spin_lock_irqsave 的区别](#2.1.3 spin_lock 和 spin_lock_irqsave 的区别)

[2.1.4 进程上下文和中断上下文有什么区别](#2.1.4 进程上下文和中断上下文有什么区别)

[2.1.5 进行上下文用什么锁](#2.1.5 进行上下文用什么锁)

[2.1.6 中断上下文用什么锁](#2.1.6 中断上下文用什么锁)

[2.1.8 中断下半部的三种方式 以及有什么区别](#2.1.8 中断下半部的三种方式 以及有什么区别)

[2.1.9 tasklet 和工作队列能否休眠?运行在中断上下文还是进程上下文](#2.1.9 tasklet 和工作队列能否休眠?运行在中断上下文还是进程上下文)

Linux驱动基础问题

[2.2.1 驱动分类](#2.2.1 驱动分类)

[2.2.2 驱动模块基本结构](#2.2.2 驱动模块基本结构)

[2.2.3 驱动的加载方式](#2.2.3 驱动的加载方式)

[2.2.4 字符驱动设备](#2.2.4 字符驱动设备)

[2.2.5 文件操作结构体](#2.2.5 文件操作结构体)

[2.2.6 常见面试问题](#2.2.6 常见面试问题)


1、札记

1.1、芯片的bring up 主要做哪些工作:

1、sdk 编译 烧录 启动 调试串口

2、屏幕驱动正常工作 demo正常启动

2、Linux驱动八股文

中断与同步互斥

2.1.1 内核同步互斥的几种方式

互斥锁、自旋锁、原子操作、禁止抢占、内存屏障

信号量、读写锁、顺序锁

2.1.2 互斥锁和自旋锁的区别

自旋锁:忙等、不可休眠、持有时间短、适合中断上下文

互斥锁:睡眠等,持有时间长

2.1.3 spin_lock 和 spin_lock_irqsave 的区别

区别在于中断开关,通常在中断上下文,需要 对寄存器进行操作,寄存器操作需要用 spin_lock_irqsave ,而 spin_lock 只是禁止内核抢占,适用于没有中断处理的场景,确保临界区资源不被中断程序访问

2.1.4 进程上下文和中断上下文有什么区别

进程上下文:用户态进程的执行环境,例如系统调用,内核线程,可休眠(允许调用可休眠函数,如果kmalloc msleep)

中断上下文: 硬中断、软中断触发的执行条件,不可休眠

2.1.5 进行上下文用什么锁

看进程能否休眠,可以休眠的话用互斥锁,比如系统调用,内核线程等场景都是可以休眠的

不可休眠:自旋锁,比如中断处理程序的上半部,持有自旋锁、原子操作的领域

2.1.6 中断上下文用什么锁

自旋锁

2.1.8 中断下半部的三种方式 以及有什么区别

软中断 tasklet 工作队列

tasklet 基于软中断,动态注册,而软中断是静态注册的

工作队列运行在进程上下文,可休眠 ;tasklet 和软中断是在中断上下文,不可休眠

2.1.9 tasklet 和工作队列能否休眠?运行在中断上下文还是进程上下文

tasklet : 中断上下文,禁止休眠

工作队列: 进程上下文,允许休眠

Linux驱动基础问题

2.2.1 驱动分类

  • 字符设备驱动:按字节访问 如串口 按键
  • 块设备驱动:按块访问 如硬盘 SD卡
  • 网络设别驱动:网络接口设备

2.2.2 驱动模块基本结构

复制代码
#include <linux/module.h>
#include <linux/init.h>

static int __init my_driver_init(void)
{
    printk(KERN_INFO "Driver initialized\n");
    return 0;
}

static void __exit my_driver_exit(void)
{
    printk(KERN_INFO "Driver exited\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Driver");

2.2.3 驱动的加载方式

  • 静态加载: 编译进内核镜像
  • 动态加载:编译为模块 (.ko)文件,使用 insmod/ modprobe 加载
  • 对应模块的静态加载和动态加载可以通过menuconfig 界面进行选择

    config EXAMPLE_DRIVER

tristate "Example Driver Support"

depends on NETDEVICES

help

This is an example driver for Linux.

  • tristate 是支持动态加载(<M>)的关键字。

  • 通过 menuconfig 界面按 Y/M/N 切换编译方式。

  • 依赖项(depends on)和默认值(default)会影响最终行为。

2.2.4 字符驱动设备

复制代码
// 分配设备号
dev_t dev;
alloc_chrdev_region(&dev, 0, 1, "my_device");

// 初始化cdev结构
struct cdev *my_cdev = cdev_alloc();
cdev_init(my_cdev, &fops);
my_cdev->owner = THIS_MODULE;

// 添加字符设备
cdev_add(my_cdev, dev, 1);

// 创建设备节点
struct class *my_class = class_create(THIS_MODULE, "my_class");
device_create(my_class, NULL, dev, NULL, "my_device");

2.2.5 文件操作结构体

复制代码
static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
    .unlocked_ioctl = my_ioctl,
};

2.2.6 常见面试问题

  1. 字符设备驱动的主设备号和次设备号有什么作用
  • 主设备号 标识设备驱动程序
  • 此设备号 标识使用同一驱动的不同设备通过 MAJOR() 和 MINOR ()宏获取

2.如何实现设备的并发访问控制

  • 使用自旋锁、互斥锁等同步机制

3.copy_to_user 和 copy_from_user 的作用是什么

  • 安全在内核空间和用户空间之间复制数据

2.3 中断处理

2.3.1 中断注册流程

复制代码
// 注册中断处理函数
int ret = request_irq(irq_num, my_interrupt_handler, 
                      IRQF_SHARED, "my_device", dev_id);

// 中断处理函数
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 处理中断
    // ...
    return IRQ_HANDLED;
}

// 释放中断
free_irq(irq_num, dev_id);
  • 先 请求中断 -> 在写中断函数 -> 释放中断

2.3.2 中断注册流程

  • 上半部 中断处理函数,快速响应
  • 下半部 延迟处理 可调度
复制代码
// 工作队列实现下半部
static struct work_struct my_work;

static void my_work_handler(struct work_struct *work)
{
    // 耗时操作
}

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 快速处理
    schedule_work(&my_work);
    return IRQ_HANDLED;
}

// 初始化
INIT_WORK(&my_work, my_work_handler);

2.3.4 常见面试问题

1、Linux 中断下半部有哪几种机制

  • 软中断 : 静态分配,优先级高
  • tasklet : 基于软中断,动态创建
  • 工作队列:在进程上下文中执行,可睡眠

2、中断上下文有什么限制

  • 不能睡眠
  • 不能使用可能睡眠的函数 (互斥锁)
  • 尽量减少处理时间

3、如何处理共享中断

共享中断是指多个设备共享一个硬件中断线,当中断触发,内核需要调用所有注册到这个irq 的设备处理函数处,处理函数中回去 检查中断源 和 返回处理结果 、

2.4 设备树与平台驱动

2.4.1 设备树基础

复制代码
/* 设备树节点示例 */
my_device: my_device@50000000 {
    compatible = "vendor,my-device";
    reg = <0x50000000 0x1000>;
    interrupts = <0 29 4>;
    clocks = <&clk 1>;
    status = "okay";
};

2.4.2 平台驱动模型

复制代码
// 平台驱动结构体
static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe,
    .remove = my_platform_remove,
    .driver = {
        .name = "my-device",
        .of_match_table = my_of_match,
        .pm = &my_pm_ops,
    },
};

// 设备树匹配表
static const struct of_device_id my_of_match[] = {
    { .compatible = "vendor,my-device" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);

// 注册平台驱动
module_platform_driver(my_platform_driver);

2.4.3 常见面试问题

1.设备树的作用是什么

  • 描述硬件设备,实现硬件与驱动分离支持运行适合

2、如何在驱动中获得设备树属性

  • 通过设备树匹配节点(compatible)

  • 提取常用属性(of函数)

    #include <linux/of.h>
    #include <linux/platform_device.h>

    static int my_probe(struct platform_device *pdev)
    {
    struct device_node *node = pdev->dev.of_node;
    struct resource *res;
    void __iomem *regs;
    int irq, ret;
    u32 freq;

    复制代码
      /* 1. 获取寄存器地址(通过 reg 属性) */
      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
      regs = devm_ioremap_resource(&pdev->dev, res);
      if (IS_ERR(regs))
          return PTR_ERR(regs);
    
      /* 2. 获取中断号 */
      irq = platform_get_irq(pdev, 0);
      if (irq < 0)
          return irq;
    
      /* 3. 读取自定义整数属性 */
      ret = of_property_read_u32(node, "clock-frequency", &freq);
      if (ret) {
          dev_warn(&pdev->dev, "clock-frequency not specified, using default\n");
          freq = 25000000; // 默认值
      }
    
      /* 4. 检查布尔属性 */
      if (of_property_read_bool(node, "dma-capable")) {
          setup_dma();
      }
    
      /* 注册中断处理函数 */
      ret = devm_request_irq(&pdev->dev, irq, my_irq_handler, 0, "my-device", NULL);
      if (ret)
          return ret;
    
      dev_info(&pdev->dev, "Device probed, freq=%d Hz\n", freq);
      return 0;

    }

    static const struct of_device_id my_device_ids[] = {
    { .compatible = "vendor,my-device" },
    { }
    };
    MODULE_DEVICE_TABLE(of, my_device_ids);

    static struct platform_driver my_driver = {
    .driver = {
    .name = "my-device",
    .of_match_table = my_device_ids,
    },
    .probe = my_probe,
    };
    module_platform_driver(my_driver);

3、platform_device 和 platform_driver 关系

  • platform_device 描述设备资源
  • platform_driver 实现设别驱动通过总线和模型绑定
相关推荐
小羊在奋斗43 分钟前
【LeetCode 热题 100】搜索插入位置 / 搜索旋转排序数组 / 寻找旋转排序数组中的最小值
算法·leetcode·职场和发展
林下清风~3 小时前
力扣hot100——347.前K个高频元素(cpp手撕堆)
算法·leetcode·职场和发展
Swift社区5 小时前
涂色不踩雷:如何优雅解决 LeetCode 栅栏涂色问题
算法·leetcode·职场和发展
真的没有脑袋5 小时前
概率相关问题
算法·面试
跟我一起学测试呀6 小时前
软件测试—接口测试面试题及jmeter面试题
软件测试·jmeter·面试
{⌐■_■}7 小时前
【计算机网络】HTTP/1.0,HTTP/1.1,HTTP/2,HTTP/3汇总讲解,清晰表格整理面试重点对比
计算机网络·http·面试
前端小巷子8 小时前
CSS面试题汇总
前端·css·面试
蓝婷儿10 小时前
前端面试每日三题 - Day 34
前端·面试·职场和发展
S01d13r14 小时前
LeetCode 解题思路 48(编辑距离、只出现一次的数字)
算法·leetcode·职场和发展