嵌入式开发工程师笔试面试指南-Linux驱动

1 什么是模块?⭐⭐⭐

在 Linux 驱动中,模块是一种具有特定功能的可动态加载和卸载的代码单元。它能在不重新编译内核的情况下,为内核添加新功能或驱动新硬件。比如网卡驱动模块,可使内核支持特定型号的网卡。模块有独立的代码和数据空间,通过特定的接口与内核其他部分交互,如初始化函数用于在加载时进行资源分配等操作,清理函数用于在卸载时释放资源。常见的模块包括设备驱动模块、文件系统模块、网络协议模块等。

2 驱动类型有几种⭐⭐⭐⭐⭐

Linux 驱动类型主要有以下几种:

字符设备驱动:以字符为单位进行数据传输,像键盘、鼠标、串口设备等,应用程序可通过系统调用对其进行读写等操作,通常按字节流方式处理数据。

块设备驱动:以块为单位传输数据,常见的如硬盘、U 盘等存储设备,数据读写以固定大小的块为单位,支持随机访问,能提高数据传输效率。

网络设备驱动:负责网络设备与内核网络子系统间的通信,如网卡驱动,实现数据包的发送和接收,使设备能接入网络进行数据通信。

3 字符设备驱动框架编程流程?⭐⭐⭐⭐⭐

在 Linux 内核中进行字符设备驱动框架编程,一般可以按照以下流程进行:

1. 准备工作

包含必要的头文件:在驱动代码文件开头,包含 Linux 内核中与字符设备驱动开发相关的头文件,例如 <linux/init.h>、<linux/module.h>、<linux/fs.h> 等,这些头文件提供了驱动开发所需的各种数据结构和函数声明

定义必要的全局变量:定义字符设备的主设备号、次设备号、设备类指针、设备指针等全局变量,方便后续在不同函数中使用。

2. 实现文件操作结构体

文件操作结构体 struct file_operations 定义了用户空间对设备文件进行各种操作时对应的内核函数,是字符设备驱动的核心部分。需要实现以下常见的操作函数:

open 函数:当用户空间程序调用 open() 系统调用打开设备文件时,内核会调用该函数,一般用于对设备进行初始化、检查设备状态等操作。

read 函数:对应 read() 系统调用,用于从设备读取数据到用户空间缓冲区。

write 函数:对应 write() 系统调用,用于将用户空间缓冲区的数据写入设备。

release 函数:当用户空间程序调用 close() 系统调用关闭设备文件时,内核会调用该函数,通常用于释放设备占用的资源。

以下是一个简单的文件操作结构体示例:

static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
};

3.分配和注册设备号

设备号用于唯一标识一个字符设备,由主设备号和次设备号组成。可以通过以下两种方式分配设备号:

静态分配:手动指定一个主设备号,但需要确保该主设备号未被其他设备使用,使用 register_chrdev_region() 函数进行注册。

动态分配:由内核自动分配一个可用的主设备号,使用 alloc_chrdev_region() 函数进行分配和注册。

示例代码如下:

static dev_t dev;
static int major;

// 动态分配设备号
static int __init my_init(void) {
    if (alloc_chrdev_region(&dev, 0, 1, "my_device") < 0) {
        printk(KERN_ERR "Failed to allocate device number\n");
        return -1;
    }
    major = MAJOR(dev);
    printk(KERN_INFO "Allocated major number: %d\n", major);
    return 0;
}

4.创建设备类和设备节点

创建设备类:使用 class_create() 函数创建一个设备类,设备类是一种抽象的表示,用于将相关的设备组织在一起。

创建设备节点:使用 device_create() 函数在 /dev 目录下创建设备节点,用户空间程序可以通过该设备节点来访问字符设备。

示例代码如下:

static struct class *my_class;
static struct device *my_device;

static int __init my_init(void) {
    // 分配设备号代码...

    // 创建设备类
    my_class = class_create(THIS_MODULE, "my_class");
    if (IS_ERR(my_class)) {
        printk(KERN_ERR "Failed to create device class\n");
        unregister_chrdev_region(dev, 1);
        return PTR_ERR(my_class);
    }

    // 创建设备节点
    my_device = device_create(my_class, NULL, dev, NULL, "my_device");
    if (IS_ERR(my_device)) {
        printk(KERN_ERR "Failed to create device node\n");
        class_destroy(my_class);
        unregister_chrdev_region(dev, 1);
        return PTR_ERR(my_device);
    }

    return 0;
}

5.注册字符设备

使用 cdev_init() 函数将文件操作结构体与字符设备对象关联起来,然后使用 cdev_add() 函数将字符设备对象注册到内核中。

示例代码如下:

static struct cdev my_cdev;

static int __init my_init(void) {
    // 分配设备号、创建设备类和设备节点代码...

    // 初始化字符设备对象
    cdev_init(&my_cdev, &my_fops);
    my_cdev.owner = THIS_MODULE;

    // 注册字符设备
    if (cdev_add(&my_cdev, dev, 1) < 0) {
        printk(KERN_ERR "Failed to add character device\n");
        device_destroy(my_class, dev);
        class_destroy(my_class);
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    return 0;
}

6.实现初始化和退出函数

初始化函数:在驱动模块加载时,内核会调用初始化函数,在该函数中完成设备号分配、设备类和设备节点创建、字符设备注册等操作。

退出函数:在驱动模块卸载时,内核会调用退出函数,在该函数中完成字符设备注销、设备节点和设备类销毁、设备号释放等操作。

示例代码如下:

static void __exit my_exit(void) {
    cdev_del(&my_cdev);
    device_destroy(my_class, dev);
    class_destroy(my_class);
    unregister_chrdev_region(dev, 1);
    printk(KERN_INFO "Character device module unloaded\n");
}

module_init(my_init);
module_exit(my_exit);

7.编译和加载驱动模块

编写 Makefile:编写 Makefile 文件,指定编译驱动模块所需的内核源码路径和编译规则。

编译驱动模块:在终端中执行 make 命令,编译生成 .ko 格式的驱动模块文件

加载和卸载驱动模块:使用 insmod 命令加载驱动模块,使用 rmmod 命令卸载驱动模块。

通过以上步骤,就可以完成一个基本的字符设备驱动框架的编程。不同的字符设备可能还需要根据具体需求实现更多的功能,如中断处理、设备资源管理等。

4 什么是并发,驱动中产生竞态的原因有哪些?⭐⭐⭐⭐

并发是指在同一时间段内,有多个任务或操作在同时进行或交替执行,这些任务可以是不同的进程、线程或内核中的不同执行路径等。

在驱动中产生竞态的原因主要有以下几点:

多进程或多线程访问:多个进程或线程可能同时访问驱动中的共享资源,如全局变量、设备寄存器等,如果没有适当的同步机制,就会导致数据不一致等竞态问题。

中断处理:中断可能在任意时刻发生,若中断处理程序和其他正常执行路径同时访问同一资源,比如中断处理程序修改了某个设备状态变量,而主程序也在对该变量进行操作,就容易引发竞态。

内核抢占:在支持内核抢占的系统中,一个正在执行的驱动程序可能被更高优先级的任务抢占,当它再次恢复执行时,可能与抢占期间执行的其他代码产生对共享资源的竞态。

5 解决竞态的途径有哪些?分别有什么特点?⭐⭐⭐⭐⭐

在驱动开发中,解决竞态的途径及特点如下:

自旋锁

特点:当一个进程获取自旋锁时,如果锁已被占用,它会持续循环等待,直到锁被释放。优点是开销小、响应快,适用于锁被持有的时间短、不希望进程睡眠的场景。缺点是会浪费 CPU 资源,且不能在持有自旋锁时进行可能导致睡眠的操作。

互斥锁

特点:获取互斥锁的进程若发现锁已被占用,会进入睡眠状态,直到锁被释放。它能避免自旋锁的忙等待问题,适合锁被持有的时间较长的情况。不过,由于涉及进程状态切换,开销相对较大,且不能在中断上下文中使用。

信号量

特点:可以允许

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式/C++面试八股文 文章被收录于专栏

#承诺提供免费技术答疑# 本专栏主要是介绍嵌入式开发岗位相关知识和学习攻略。“C/C++软件开发岗位”也可以参考。 该专栏覆盖了嵌入式求职过程中99%常见面试题,详细讲解了嵌入式软件开发岗位、学习攻略、项目经验分享、面试心得,从技术面,HR面,主管面,谈薪一站式服务。订阅即赠送简历模板、内推机会,需要的同学点击我头像私信即可!

全部评论
如果大家觉得可以帮助到自己,麻烦点赞、评论和订阅哦😀
1 回复 分享
发布于 03-10 21:54 安徽
点赞 回复 分享
发布于 03-12 10:29 江苏

相关推荐

星纵物联8.8&nbsp;硬件工程师技术面面试官挺好的,问的基本都是项目相关1.自我介绍2.你的研究方向讲一下,为什么想做硬件3.挑一个你最熟悉的项目讲一下4.你在里面做了什么内容5.为什么用这个拓扑,拓扑原理讲一下,这个拓扑有什么优势吗6.器件选型怎么考虑的7.电路板怎么调试的,考虑到负载了吗,怎么联调的8.功率这么大,做了哪些注意措施9.项目创新点是什么10.产品投入生产使用了吗11.板子之间数据怎么传输的,通讯协议是什么12.为什么用这个主控13.有备选方案吗。反问1.培养方案2.工作强度3.产品方向4.多久出结果星纵物联25届校园招聘进行中&nbsp;以下岗位仍有缺口公司简介:厦门星纵物联科技有限公司是专业的数字感知产品提供商,专注于智能物联网设备的研发,生产与销售招聘岗位:行业产品、海外市场、开发、测试、产品经理、技术支持、项目经理、售前工程师、单片机开发、硬件薪酬福利:极富竞争力的薪酬待遇、一年两次的调薪机会、半年奖、年终奖、项目奖金、团队奖金、营销奖金、职级晋升、多元化激励、员工商业公寓、健康保障、工会福利、五险一金、安居计划、快乐活动、健身俱乐部、人才基金、生活补贴、贴心礼金、年度旅游、落户厦门、五折租房、节日关怀、温馨假期等投递链接:https://milesight.zhiye.com/campus/jobs推荐码:ESVMBS(内推简历优先筛选,加速流程推进)大家投递完可以在评论区打上姓名缩写+岗位,我来确认有没有内推成功喽 #春招#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#内推码#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#秋招#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#提前批#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#星纵物联#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
点赞 评论 收藏
分享
苏州唯理WLEC3医疗级脑电模块是一款专用于脑电采集的模块,本采用苏州唯理科技自研的低功耗高精度的脑电采集芯片&nbsp;WLM128&nbsp;芯片。可以应用于各类型的智能硬件中,实现脑电采集和相关功能的评估。模块内部内嵌了滤波、降噪、脑电信号的识别等常用算法。此外苏州唯理提供了API可以对脑电数据进行更深度的分析,该算法经过已经得到广泛的应用。使用此模块,可以实现对大脑集中度、放松度、alpha(阿尔法)、beta频谱占比、疲劳度等相关分析,用于实现脑电采集及分析等相关智能硬件。本模块模拟部分采用苏州唯理科技自研的低功耗高精度的脑电采集芯片&nbsp;WLM128&nbsp;芯片,并配置硬件&nbsp;RC&nbsp;滤波,具有交流直流信号耦合方式;块内采用&nbsp;32位高性能单片机,内置优秀的数据处理算法。可搭配蓝牙或串口传输数据,电采样的24bit数据,模块使用邮票孔和标准2.54mm插口的方式引出了数据和电源接口,且电极的信号可通过&nbsp;5pin&nbsp;间距1.27mm&nbsp;的排座引出,使用方式灵活集成简单。•使用自研量产芯片,性能优越,响应迅速。•模块功耗达到最优,超低功耗,满足不同场景需求。•模块集成度高,成本大幅降低。•拥有配套算法,高效精准。•提供OEM等各种合作形式。•蓝牙传输速度快,适配广泛无丢包。•监测精度高,附带相关参数分析。•经过市场多家厂商使用认证。•安全指标高,能够通过相关法规认证达到投产目的。•输入工作电压&nbsp;3.3V-5.0V。•系统噪声约5uVpp。•通道数1,2,4,8,16通道。•前置放大倍数6倍。•采样率250~2ksps。•数据分辨率24bit。
点赞 评论 收藏
分享
评论
3
13
分享

创作者周榜

更多
牛客网
牛客企业服务