根据面经准备面试-第一期-2026小米嵌入式软开

8.昨天有个问题,I/O复用、select、poll、epoll的区别没解释清楚

I/O 复用就是:你可以同时盯着所有 10 个水龙头,哪个先出水就先处理哪个,其他没出水的继续盯着 —— 这样一个人就能高效处理多个 I/O 操作。

select是最老的复用工具,拿着水龙头编号进行检查,数量限制,最多1024个fd从用户到内核

poll是select升级版,解决数量限制,使用链表数组记录文件描述符fd

epoll先告诉epoll“我要监控这 1000 个水龙头”,epoll会把它们记在一个 “红黑树”(高效的数据结构)里,只注册一次,不用每次,内核会盯着这些水龙头,一旦某个出水(有数据),就把它放进 “就绪链表”(类似 “报警列表”)。

形象点说:select像 “小卖部老板盯几个货架”,poll像 “超市店员盯一排货架”,epoll像 “仓库管理员用监控器盯所有货架,只处理报警的”。

9.linux字符设备驱动程序的设计流程,file_operations 中read write,应用程序怎么读取到字符设备中的数据;

设计流程

1.先定义file_operations结构体,实现open/read/write/release等操作函数

2.注册字符设备:通过cdev_init初始化cdevcdev_add注册到内核,分配设备号(alloc_chrdev_region);

3.创建设备文件:通过class_createdevice_create自动生成/dev/xxx(替代手动mknod

read/write

  • read:从设备读取数据到用户空间,用copy_to_user(dst, src, len)(内核→用户);
  • write:从用户空间写入数据到设备,用copy_from_user(dst, src, len)(用户→内核)

应用程序访问方式

  1. int fd = open("/dev/xxx", O_RDWR):打开设备,获取文件描述符;
  2. read(fd, buf, sizeof(buf)):调用驱动的read函数,数据通过copy_to_user传递到buf
  3. write(fd, buf, len):调用驱动的write函数,数据通过copy_from_userbuf传入内核

10.系统调用过程、platform 总线驱动设计、mmap 函数

系统调用1.用户态调用库函数,触发软中断

2.陷入内核态,保存用户态上下文,寄存器,栈

3.内核通过系统调用号查sys_call_table,执行对应内核函数(如sys_read

执行完毕,恢复上下文,返回用户态

platform总线驱动

1.定义platform-device描述设备资源,如地址、中断,或由设备树组成

2.定义platform-driver实现probe初始化设备,注册字符设备,remove清理函数

3.通过platform-driver-register注册驱动,内核自动匹配设备

mmap函数

作用是将内核空间内存(如设备缓冲区)映射到用户空间,用户程序可直接访问,避免read/write的数据拷贝,提高效率。驱动中需实现file_operationsmmap函数,通过remap_pfn_range建立虚拟地址映射

(11)UART串口有时钟线吗,他是怎么保证数据发完之后对方知道你发完的,UART怎么保证数据的准确性;

uart无专用时钟线,双方通过预设波特率115200同步数据

uart帧格式包含停止位,1,2个bit,高电平,表示一帧数据结束,例如一个起始位,低+8个数据位+1个停止位(高)

准确性保证

硬件上通过奇偶校验位(奇校验、偶校验)检测单bit错误

软件上可添加CRC校验或超时重传机制如modbus协议

(12)多个进程访问同一个文件时,文件描述符是一样的吗?

不同,文件描述符fd,是进程私有表的索引,每个进程打开同一文件,会分配不同pd,但这些fd最终指向内核中同一个struct file结构体,记录文件偏移量、权限

(13)uboot的启动流程了解吗;stage1 和 stage2 。uboot源码看过吗,其中的数组?(uboot代码必须理解)

uboot相当于bootloader的一种,

1.初始化硬件

2.引导内核启动把存在 Flash 里的内核文件(比如zImage)读到内存,然后告诉 CPU“跳到内核的起始地址,开始运行内核”。

  • stage1(汇编实现,如start.S):初始化 CPU(禁用中断、设置工作模式);初始化关键外设(时钟、SDRAM);将 uboot 代码从 Flash(如 NAND)搬移到 SDRAM;跳转到 stage2 的 C 入口函数(board_init_r)。
  • stage2(C 语言实现,如board_init_r):初始化更多外设(网卡、SD 卡、LCD);解析环境变量(如bootcmd、bootargs);加载内核镜像到 SDRAM(从 Flash 或网络);跳转到内核入口(如kernel_entry)。
  • 源码中的数组

  • 命令表cmd_tbl_t:存储 uboot 支持的命令(如bootmtftp),通过U_BOOT_CMD宏注册;
  • 设备驱动表:如driver_lists,存储各类外设驱动(如 SPI、I2C 驱动)。

补充:uboot相当于设备上电后第一个运行程序、负责唤醒硬件、如初始化内存、cpu时钟、串口、硬盘

硬件准备好后,把kernel从硬盘加载到内存,内核管理所有硬件资源(CPU、内存、摄像头、传感器等)

(14)添加过uboot中的命令吗?linux文件系统的格式.

定义命令结构体和实现函数并且加入makefile,重新编译uboot

Linux 文件系统格式

  • 磁盘类:ext2/3/4(通用)、xfs(高性能)、btrfs(支持快照);
  • 嵌入式专用:jffs2(闪存,支持擦除均衡)、yaffs2(NAND 闪存);
  • 虚拟文件系统:proc(内核信息)、sysfs(设备信息)、tmpfs(内存文件系统)。

(15)多线程通信

多线程共享进程地址空间,通信方式主要依赖同步机制:

1.互斥锁,保护临界资源,同一时间仅允许一个线程访问

2.条件变量,线程件同步,如生产者-消费者模型

3.信号量,控制并发线程树

4.读写锁,读多写少场景优化,允许多个读线程同时访问,写线程独占

(16)共享内存,进程间通信方式, 共享内存如果发生泄漏,怎么查看?ipcs -m?

共享内存:是最快的 IPC(也就是进程通信) 方式,通过内核创建一块物理内存,映射到多个进程的虚拟地址空间,直接访问无需拷贝。

其他 IPC 方式:管道(匿名 / 命名)、消息队列、信号量、信号、Socket

共享内存泄漏查看

  • ipcs -m查看系统中所有共享内存(含shmid、权限、大小);
  • 若进程退出未删除共享内存,会残留,可通过ipcrm -m shmid手动删除。

(17)创建的设备文件 /dev/下的,读取时文件描述符一样吗?

不同。文件描述符是进程私有资源,每个进程打开/dev下的同一设备文件时,内核会分配不同的 fd(如进程 A 的 fd=3,进程 B 的 fd=4),但它们指向内核中同一个struct file结构体(关联设备驱动的file_operations)。

(18)算法相关:反转链表,链表的头插和尾插法;

这个不太好解释,网上很多

(19)画过原理图吗,用什么软件画的,AD? 那在AD软件中在如果根据原理图上的器件快速选中PCB图上的器件

在 AD 中根据原理图器件快速选中 PCB 器件的方法:

  1. 打开原理图和 PCB 文件,确保两者同步(执行 “Design→Update PCB Document”);
  2. 在原理图中选中目标器件,按Ctrl+F,或通过 “Tools→Cross Select Mode” 开启交叉选择模式,PCB 中对应器件会自动高亮选中。

(20)了解Makefile吗,Makefile语法格式?

target: dependencies # 目标: 依赖文件 command # 生成目标的命令(必须以Tab开头)

关键要素

  • 变量:CC = gcc(编译器)、CFLAGS = -Wall -O2(编译选项),引用时用$(CC)
  • 自动变量:$@(目标名)、$^(所有依赖)、$<(第一个依赖);

(21)struct结构体的大小,字节对齐、数组 sizeof()大小;

  1. 结构体成员对齐到自身大小的整数倍(如int占 4 字节,对齐到 4 的倍数);
  2. 结构体总大小是最大成员大小的整数倍(添加填充字节)。这里可以看下b站陈子青
  3. 数组 sizeof:返回数组总字节数 = 元素个数 × 单个元素大小,与是否初始化无关。

char arr[10]sizeof(arr)=10

int arr[5]sizeof(arr)=20(5×4)

全部评论

相关推荐

1.I2C协议时序I2C协议是两线制串行通信协议,物理层包括SCL时钟线、SDA数据线,支持一主多从架构核心时序包括起始信号:scl为高电平,sda从高到低跳变,标志通信开始scl为高电平,sda从低到高跳变,标志通信结束数据传输时:scl高电平时,sda必须稳定,低电平时sda可变化,准备下一位,每个字节八位,高位在前接收方在第&nbsp;9&nbsp;个时钟周期拉低&nbsp;SDA&nbsp;表示应答,不拉低为非应答主设备通过控制scl和sda生成起始信号,发送从设备地址(7位地址+1位读写位),等待应答后传输数据,最后停止信号结束2.I2C&nbsp;协议的应用场景和实现我在项目中用&nbsp;I2C&nbsp;连接过温湿度传感器(如&nbsp;SHT30)和&nbsp;EEPROM,传感器通过&nbsp;I2C&nbsp;将采集的数据传给&nbsp;MCU,EEPROM&nbsp;通过&nbsp;I2C&nbsp;存储配置参数,3.Linux&nbsp;下&nbsp;I2C&nbsp;驱动编程的三层结构回答:linux下I2C驱动为核心层、总线层、设备驱动层核心层:提供通用api,管理总线和设备的匹配关系,是连接总线层和设备驱动层的桥梁总线层(控制器驱动):对应具体I2C控制器,实现硬件时序,负责将核心层的抽象请求转化为实际的&nbsp;SCL/SDA&nbsp;电平操作设备驱动层(外设驱动):针对具体&nbsp;I2C&nbsp;设备(如传感器),定义i2c_driver结构体,实现probe(初始化设备)、remove(清理)等函数,通过核心层&nbsp;API&nbsp;与设备通信。4.platform&nbsp;与设备树的区别platform总线:是Linux内核为无物理总线的片上外设(GPIO、定时器)设计的虚拟总线设备树:是一种文本文件,用树形结构描述硬件信息(外设、引脚、中断),替代传统板级C代码,驱动通过属性与设备树节点匹配核心区别:platform&nbsp;是&nbsp;“驱动&nbsp;-&nbsp;设备”&nbsp;匹配机制,设备树是&nbsp;“硬件信息描述工具”;现代内核中,platform&nbsp;设备通常由设备树自动生成5.Linux&nbsp;下如何获取设备树中的硬件信息内核通过of函数解析设备树,查找节点,6.TCP&nbsp;与&nbsp;UDP&nbsp;的区别,Socket&nbsp;编程步骤及差异TCP服务器编程步骤:socket创建TCP套接字bind绑定ip和端口listen监听连接accept阻塞等待客户端连接,返回新套接字recv收发数据close关闭连接UDP无需listen、accept、connect收发用sendto,需指定目标地址,UDP无连接概念,一个套接字可与多个地址通信7.IO复用:select、poll、epoll区别三者均用于单线程管理多个I/O流,8.Linux&nbsp;字符设备驱动设计流程,read/write&nbsp;实现及应用访问方式定义file_operations结构体,实现open/read/write/release等操作函数,注册字符设备:通过cdev_init初始化cdev,cdev_add注册到内核,分配设备号(alloc_chrdev_region),创建设备文件:通过class_create和device_create自动生成/dev/xxx(替代手动mknod。read从设备读取数据到用户空间,内核到用户write是从用户空间写入到设备,从用户到内核应用程序访问方式&nbsp;1打开设备,获取文件描述符2.调用驱动的read函数,数据通过copy_to_user传递到buf3.调用驱动的write函数,数据通过copy_from_user从buf传入内核
站队站对牛:这个技术面相当强
查看8道真题和解析
点赞 评论 收藏
分享
评论
6
17
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务