嵌入式开发工程师笔试面试指南-面试题目整理(3)
1 linux中内核空间及用户空间的区别
Linux 中内核空间和用户空间主要有以下区别:
功能用途
内核空间:是 Linux 内核运行的区域,负责管理硬件资源、调度进程、提供系统服务等关键任务,像内存管理、进程调度、设备驱动程序都在内核空间运行。
用户空间:是用户进程运行的环境,用户的应用程序如浏览器、文本编辑器等都在此空间执行,主要用于实现用户的各种具体业务逻辑。
访问权限
内核空间:具有最高的权限级别,能直接访问硬件和所有内存空间,可执行特权指令,对系统进行全面控制。
用户空间:权限相对较低,不能直接访问硬件和内核数据结构,只能通过系统调用请求内核提供服务来间接访问硬件等资源。
内存布局
内核空间:通常位于内存的高端地址,占用固定且连续的内存区域,其内存布局相对稳定,由内核开发者进行管理和分配。
用户空间:位于内存的低端地址,每个用户进程都有独立的虚拟地址空间,包含代码段、数据段、堆、栈等,内存布局根据进程的需求动态变化。
2 用户空间与内核通信方式有哪些
在 Linux 中,用户空间与内核空间的通信方式有多种:
系统调用
这是最常见的方式。用户程序通过调用如 open、read、write 等系统调用,陷入内核态,让内核执行特定操作。例如用户程序使用 read 系统调用从文件或设备读取数据,内核负责实际的读取操作并返回结果。
设备文件
内核可以创建字符设备或块设备文件,用户程序通过对这些设备文件进行读写操作来与内核交互。比如 /dev 目录下的设备文件,用户程序向设备文件写入数据,内核中的设备驱动接收并处理;反之,从设备文件读取数据获取内核输出。
信号
内核可向用户进程发送信号,通知特定事件。如 SIGINT(Ctrl+C)用于终止进程,用户进程也能通过 sigaction 等函数处理信号,实现与内核的简单通信。
proc 文件系统和 sysfs
/proc 是虚拟文件系统,内核将系统信息以文件形式呈现,用户程序读取这些文件获取内核数据;sysfs 类似,主要用于管理设备和驱动信息,用户程序可读写其中文件实现与内核交互。
netlink 套接字
用于用户空间和内核空间的网络通信,支持多播,可在用户进程和内核模块间传递消息,常用于网络相关的配置和状态查询。
3 linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化,高端内存概念linux中中断的实现机制,tasklet与workqueue的区别及底层实现区别?
内存划分及使用
Linux 内存分为内核空间和用户空间。用户空间供应用程序使用,通过 malloc、free 等标准库函数管理;内核空间由内核管理,使用 kmalloc、vmalloc 等函数分配内存。
虚拟地址与物理地址
概念:虚拟地址是进程看到的地址空间,每个进程有独立的虚拟地址空间,增强了隔离性和安全性;物理地址是内存芯片上的实际地址。
转化:通过页表实现。MMU(内存管理单元)根据页表将虚拟地址映射到物理地址。
高端内存
Linux 内核将物理内存划分为低端内存和高端内存。低端内存可直接映射到内核地址空间;高端内存无法直接映射,需要通过特定机制(如临时映射)来访问。
中断实现机制
硬件产生中断信号,CPU 检测到后,根据中断向量表找到对应的中断服务程序入口,保存现场,执行中断服务程序,最后恢复现场,继续执行原程序。
tasklet 与 workqueue 区别
tasklet:运行在软中断上下文,执行速度快,不能睡眠,适合处理对时间敏感的小任务。
workqueue:在进程上下文执行,可睡眠,适合处理耗时或可能睡眠的任务。
底层实现:tasklet 基于软中断实现,可在多个 CPU 上并行执行;workqueue 由内核线程处理工作项。
4 为什么要区分上半部和下半部linux中断的响应执行流程
在 Linux 中断处理中,区分上半部和下半部主要有以下原因:
提高响应速度
上半部主要负责快速处理与硬件直接相关的紧急事务,比如接收硬件信号、读取硬件状态等,完成后迅速返回,以便让 CPU 能尽快响应其他中断。
下半部则负责处理可以稍微延迟的任务,如数据处理、更新系统状态等,这样可避免因在中断处理中执行过多复杂任务而导致系统响应变慢。
避免长时间阻塞
如果所有中断处理都在上半部完成,可能会使中断处理时间过长,导致其他中断被长时间阻塞,影响系统的实时性和稳定性。
将部分任务放到下半部,可以在合适的时候异步执行,不会阻塞中断的响应。
优化资源利用
上半部执行时往往会禁用某些中断或抢占,以保证关键操作的原子性和完整性,这可能会影响系统的并发性。
下半部可以在更宽松的环境下执行,能更好地利用系统资源,提高系统的整体性能。
Linux 中断响应执行流程一般是:硬件触发中断后,CPU 跳转到对应的中断向量,执行上半部代码,进行紧急处理后,标记需要执行的下半部任务,然后上半部结束,恢复被中断的程序执行。在合适的时机,如 CPU 空闲或满足特定条件时,会执行下半部任务,完成中断相关的后续处理。
5 谈谈Linux的同步机制
Linux 的同步机制主要用于确保多个进程或线程在访问共享资源时的一致性和正确性,常见的同步机制如下:
原子操作
对单个变量的操作具有原子性,在执行过程中不会被打断,如对整数的自增、自减等操作,可通过硬件指令实现,用于简单的计数或标志位操作。
自旋锁
当一个进程获取自旋锁时,如果锁已被占用,它会一直循环等待,不断检查锁是否可用,适用于锁的持有时间短、不希望进程睡眠的场景。
互斥锁
与自旋锁类似,但当进程无法获取锁时会进入睡眠状态,直到锁被释放,适合锁的持有时间较长的情况,能避免进程空转浪费 CPU 资源。
信号量
可以控制多个进程对共享资源的访问数量,通过一个计数器来实现,进程获取信号量时计数器减一,释放时加一,当计数器为 0 时,其他进程只能等待。
条件变量
通常与互斥锁配合使用,用于实现进程间的条件等待,当某个条件不满足时,进程可以等待在条件变量上,直到其他进程改变条件并唤醒它。
6 /dev/下面的设备文件是怎么创建出来的
在 Linux 系统中,/dev 下面的设备文件主要通过以下几种方式创建:
手动创建
使用 mknod 命令可以手动创建设备文件,需要指定设备类型(字符设备或块设备)、主设备号和次设备号等参数。例如 mknod /dev/mydevice c 100 0 可创建一个主设备号为 100、次设备号为 0 的字符设备文件 /dev/mydevice。
自动创建设备文件
udev 机制:系统启动时,udev 守护进程会扫描系统中的硬件设备,根据设备信息和规则文件在 /dev 目录下自动创建设备文件。它可以根据设备的属性,如设备名称、序列号等,为设备文件设置合适的权限、所有者和组等属性。
sysfs 配合 udev:sysfs 文件系统提供了系统设备的层次化视图,udev 可以从 sysfs 中获取设备的详细信息来决定如何创建设备文件。例如,当插入一个 USB 设备时,sysfs 会新增相应的设备节点,udev 监测到后根据规则在 /dev 下创建对应的设备文件。
模块加载创建
内核模块在加载时,也可以通过调用内核函数来创建设备文件。例如,驱动程序可以使用 class_create 和 device_create 函数来创建字符设备或块设备文件,并将其添加到 /dev 目录中。
7 原子操作该怎么理解insmod一个驱动模块,会执行模块中的哪个函数?mmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
原子操作理解
原子操作指不可被中断的一个或一系列操作,在执行过程中不会被其他进程或线程干扰,确保数据一致性。比如在多线程环境对共享变量自增,若用原子操作,可避免竞态条件。
insmod 与 rmmod 执行函数
insmod加载驱动模块时,执行module_init()指定的初始化函数,负责注册设备、分配资源等初始化工作。
rmmod卸载驱动模块时,执行module_exit()指定的清理函数,用于释放资源、注销设备。
设计注意事项
初始化函数:要做错误检查,若资源分配或设备注册失败,需回滚已分配资源,防止泄漏。
清理函数:要保证释放初始化函数分配的所有资源,避免残留。
卸载异常及原因
常见异常有无法卸载、系统崩溃。原因可能是资源未正确释放,如内存、文件描述符;有进程仍在使用驱动,设备被占用;驱动代码存在死锁或竞态条件。
8 在驱动调试过程中遇到过oops没?你是怎么处理的
在驱动调试过程中,确实可能会遇到 Oops。Oops 是 Linux 内核在检测到严重错误时输出的错误信息,通常表示内核出现了异常情况。以下是处理 Oops 的一般步骤:
记录 Oops 信息:完整记录 Oops 输出的内容,包括错误发生的时间、相关的函数调用栈、寄存器信息等,这些信息对于分析问题至关重要。
分析调用栈:查看 Oops 信息中的函数调用栈,确定错误发生在哪个函数或模块中,这有助于定位问题的大致位置。
检查代码逻辑:根据调用栈信息,检查相应的驱动代码逻辑,查看是否存在指针错误、内存访问越界、资源未正确初始化或释放等问题。
查看相关日志:查看系统日志文件,看是否有其他相关的错误或警告信息,可能会提供更多关于问题的线索。
9 ioctl和unlock ioctl有什么区别
ioctl和unlock_ioctl主要有以下区别:
功能用途
ioctl是输入输出控制函数,用于向设备驱动程序发送控制命令和获取设备信息,能实现设备的配置、状态查询、特殊操作等,如设置串口波特率、获取硬盘分区信息。
unlock_ioctl并非标准的 Linux 系统函数,通常是在特定驱动或应用场景中自定义的函数,从命名看可能用于解锁某种与ioctl相关的操作或资源,或执行与解锁相关的特定设备控制功能,具体功能取决于开发者的设计。
应用场景
ioctl应用广泛,在各种设备驱动与应用程序交互中都可能用到,用于实现设备的常规控制和信息交互。
unlock_ioctl应用场景相对局限,只在特定驱动或应用中有需求,比如当设备被某种机制锁定,需专门函数来解锁以恢复正常操作时使用。
10 驱动中操作物理绝对地址为什么要先ioremap
在驱动中操作物理绝对地址时先使用 ioremap 主要有以下原因:
虚拟地址映射需求
现代操作系统采用虚拟内存管理机制,内核和应用程序只能访问虚拟地址。物理设备的寄存器或内存区域使用的是物理地址,所以需要通过 ioremap 将物理地址映射到内核的虚拟地址空间,这样驱动程序才能通过访问虚拟地址来间接操作物理设备。
内存保护与隔离
ioremap 不仅完成地址映射,还会设置相应的内存访问权限和属性。它确保驱动程序对物理设备的访问是安全和可控的,避免因直接访问物理地址而破坏系统的内存保护机制,防止对其他设备或系统关键区域造成意外影响。
11 设备驱动模型三个重要成员是?platfoem总线的匹配规则是?在具体应用上要不要先注册驱动再注册设备?有先后顺序没
设备驱动模型的三个重要成员是总线(Bus)、设备(Device)和驱动程序(Driver)。以下是 platfoem 总线的匹配规则以及驱动和设备注册顺序的相关内容:
platfoem 总线的匹配规则
基于设备 ID 匹配:平台设备通常会有一个或多个设备 ID,平台驱动也会声明它所支持的设备 ID 列表。当平台总线匹配时,会检查平台设备的 ID 是否与平台驱动支持的 ID 列表中的某个 ID 相匹配。如果找到匹配的 ID,则认为设备和驱动可以匹配。
基于 ACPI 信息匹配:在支持 ACPI(高级配置和电源接口)的系统中,平台设备和驱动可以通过 ACPI 提供的信息进行匹配。例如,设备的 ACPI 路径、设备类型等信息可以与驱动所声明的 ACPI 匹配条件进行比较,以确定是否匹配。
基于 OF 设备树匹配:对于使用设备树的系统,平台设备的设备树节点信息与平台驱动所声明的设备树兼容性字符串等信息进行匹配。如果设备树节点中的兼容性信息与驱动的兼容性字符串相匹配,则认为设备和驱动匹配。
驱动和设备注册顺序
在具体应用中,一般先注册设备,再注册驱动。这样做的原因是,先注册设备可以让系统先识别到硬件设备的存在,为后续驱动的注册和匹配提供基础。当驱动注册时,它可以根据已注册的设备信息来进行匹配和绑定操作。不过,在实际的代码实现中,也有一些情况下可以先注册驱动,然后再注册设备,这主要取决于具体的驱动和设备的特点以及应用场景,但通常需要确保在设备使用之前,驱动已经完成注册和初始化,以保证设备能够正常工作。
12 linux中RCU原理
RCU(Read - Copy - Update)是 Linux 内核中一种高效的同步机制,主要用于读多写少的场景。
基本原理
读操作:读端无需加锁,可直接访问共享数据,不会被写操作阻塞。这是因为读操作在整个过程中不涉及对共享数据的修改,所以可以无锁并发执行,大大提高了读性能。
写操作:写操作会先复制一份共享数据副本,在副本上进行修改。修改完成后,通过原子操作将指向原数据的指针指向新副本。原数据并不会立即删除,而是要等到所有引用原数据的读操作完成,这个等待的时间段称为宽限期。
宽限期:内核会跟踪所有正在进行的读操作,当所有读操作都完成一个完整的读临界区后,宽限期结束。此时,原数据不再被引用,可以安全释放。
13 谈谈Linux软中断
Linux 软中断是一种由软件触发的中断机制,用于在特定条件下中断正常的程序执行流程,以处理一些紧急或重要的任务。以下是关于 Linux 软中断的简要介绍:
特点
异步性:软中断可以在程序执行的任何时刻被触发,与程序的正常执行流程是异步的。
可重入性:软中断处理程序通常被设计为可重入的,以确保在多个软中断同时发生或嵌套时能够正确执行。
低延迟:软中断的处理应该尽可能快速,以减少对系统性能的影响,通常用于处理对时间敏感的任务。
作用
网络数据处理:当网络接口接收到数据时,会触发软中断来通知内核进行数据的接收和处理,确保网络数据的及时处理,提高网络性能。
定时任务:如系统的定时器中断,用于实现系统的时间管理、进程调度等功能,保证系统的时间准确性和任务调度的及时性。
块设备 I/O:在块设备(如硬盘)完成数据读写操作后,通过软中断通知内核,以便内核进行后续的处理,如更新缓存、通知应用程序等。
实现机制
Linux 内核通过一个软中断向量表来管理软中断,每个软中断都有一个对应的编号和处理函数。当软中断被触发时,内核会根据软中断的编号查找对应的处理函数,并执行该函数。为了避免软中断处理时间过长影响系统性能,内核还采用了一些优化措施,如将软中断分为不同的优先级,高优先级的软中断可以抢占低优先级的软中断。
14linux中系统调用过程
在 Linux 中,系统调用是用户空间程序与内核交互的重要方式,其过程如下:
用户空间准备
用户程序调用库函数封装的系统调用接口,将所需参数按约定存于寄存器或栈中,同时确定系统调用号,该号标识具体的系统调用。
触发中断
执行特殊指令(如 x86 架构的 int 0x80 或 syscall),引发从用户态到内核态的切换。CPU 硬件自动保存当前程序状态,包括寄存器值等,然后跳转到内核的中断处理入口。
内核处理
内核根据系统调用号,在系统调用表中查找对应的内核处理函数,接着执行该函数,完成用户请求的操作,如文件读写、进程创建等。
返回结果
内核处理完后,将结果存于特定寄存器,恢复之前保存的程序状态,从内核态切换回用户态,用户程序继续执行,可从相应寄存器获取系统调用结果。
15谈谈Linux调度原理
Linux 调度原理是操作系统中用于决定哪个进程或线程能够使用 CPU 资源以及何时使用的机制,以下是对其的简要介绍:
调度策略
CFS(完全公平调度器):针对普通进程,CFS 为每个进程维护一个虚拟运行时间 vruntime,按照 vruntime 来分配 CPU 时间,尽量让每个进程公平地使用 CPU 资源,vruntime 越小的进程越优先获得 CPU。
实时调度策略:包括 SCHED_FIFO(先入先出调度)和 SCHED_RR(时间片轮转调度),实时进程优先级高于普通进程,SCHED_FIFO 的实时进程一旦获得 CPU 就会一直运行直到主动放弃或被更高优先级的实时进程抢占,SCHED_RR 则为实时进程分配时间片,时间片用完后会被同优先级的其他实时进程抢占。
调度时机
进程主动放弃 CPU:如进程执行 I/O 操作、进入睡眠状态等。
时间片用完:每个进程被分配一定的时间片,时间片耗尽时会触发调度,让其他进程有机会使用 CPU。
有更高优先级的进程进入就绪态:此时会抢占正在运行的低优先级进程的 CPU 资源。
调度算法实现
Linux 内核通过红黑树等数据结构来管理就绪队列中的进程,红黑树能快速地查找和插入进程节点,以高效地选择下一个要运行的进程。
16 谈谈对Linux网络子系统的认识
Linux 网络子系统是 Linux 内核的重要组成部分,负责网络通信的各个方面。
分层架构
遵循 OSI 模型和 TCP/IP 模型,分为网络接口层、网络层、传输层和应用层。网络接口层处理物理设备通信;网络层如 IP 协议负责数据包路由;传输层的 TCP 和 UDP 提供可靠或不可靠传输服务;应用层支持各类网络应用。
功能特性
支持多种网络协议,如 TCP、UDP、IP、ICMP 等,能适应不同网络需求。
具备强大的网络设备驱动,可驱动网卡、调制解调器等多种网络设备。
提供防火墙、网络地址转换(NAT)等网络管理功能,保障网络安全与灵活组网。
17 内核中申请内存有哪几个函数?有什么区别
在 Linux 内核中,常用的申请内存函数及其区别如下:
kmalloc
特点:分配的内存位于物理连续的低端内存区域,返回的内存地址是物理地址和虚拟地址一一映射的,可保证原子性,适合小内存分配。
使用场景:当需要分配较小且对内存连续性要求高的情况,如分配描述符、数据结构等,可使用 kmalloc,它的分配速度较快。
vmalloc
特点:分配的内存是虚拟地址连续,但物理地址不一定连续,会在虚拟地址空间中开辟一块连续区域,通过页表映射到不连续的物理页上。
使用场景:适用于分配大内存块,如驱动程序中需要大量连续虚拟地址空间时,但分配和访问效率相对 kmalloc 较低。
get_free_pages
特点:直接分配物理连续的页,以页为单位,返回的是页的起始虚拟地址。
使用场景:需要整页分配内存时使用,如内核需要大量连续物理内存用于 DMA 操作等。
18 谈谈内核函数mmap的实现机制
Linux 内核函数 mmap 用于将文件或设备等映射到进程的虚拟地址空间,其实现机制如下:
建立映射关系
用户空间调用 mmap 时,内核首先检查参数合法性,如文件描述符有效性、映射长度等。然后在进程的虚拟地址空间中查找一段空闲区域,为映射分配虚拟地址范围,并在内核中创建一个 vm_area_struct 结构体来描述该映射区域,记录映射的相关信息,如映射的起始地址、长度、权限等。
关联物理页面
对于文件映射,若映射的是普通文件,内核会根据文件的偏移量和映射长度,建立虚拟地址与文件数据所在物理页面的映射关系,可能涉及到从磁盘读取数据到物理内存。对于匿名映射,内核会分配新的物理页面并建立映射。
缺页中断处理
当进程首次访问映射区域内的虚拟地址时,若对应的物理页面不在内存中,会触发缺页中断。内核的缺页中断处理程序会根据映射类型和相关信息,将所需的物理页面加载到内存,并更新页表,建立虚拟地址与物理页面的正确映射,使进程能够访问到正确的数据。
#嵌入式#该专栏面向嵌入式开发工程师,包括C语言、C++,操作系统,ARM架构、RTOS、Linux基础、Linux驱动、Linux系统移植、计算机网络、数据结构与算法、5篇面试题目、HR面试常见问题汇总和嵌入式面试简历模板等18篇文章。超全的嵌入式软件工程师笔试面试题目和高频知识点总结!招聘so easy。