根据面经准备面试-第一期-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)

全部评论

相关推荐

评论
6
16
分享

创作者周榜

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