击左上方蓝色“一口Linux”,选择“设为星标”
第一时间看干货文章 ☞【干货】嵌入式驱动工程师学习路线 ☞【干货】Linux嵌入式知识点-思维导图-免费获取 ☞【就业】一个可以写到简历的基于Linux物联网综合项目 ☞【就业】简历模版
malloc/calloc/realloc/free,C++ 用new/delete,手动分配释放)。指针指向内存:int *p = &var;(变量地址)或 p = (int *)0x40000000;(物理地址映射);
读写内存:*p = 10;(写)、int val = *p;(读);
连续内存:指针加减偏移(如p++访问数组下一个元素)。
strncpy替代strcpy);valgrind),用volatile监控共享内存。4、 栈内是保存什么的?malloc 出来的地址在栈还是堆?**
malloc分配的内存在堆,栈仅存储malloc返回的指针变量本身。哈希函数映射:将键映射到哈希桶下标,快速定位存储位置;
冲突解决:采用链地址法(冲突元素串成链表)或开放地址法;
核心特性:平均查询 / 插入时间复杂度 O (1)。
以下哪种线程同步机制可用于实现 “线程 A 等待线程 B 完成某操作后再执行” 的逻辑?()
A. 互斥锁(Mutex) B. 条件变量(Condition Variable) C. 二进制信号量 D. 计数信号量
答案:B
解析:条件变量通过 “等待 - 通知” 机制实现:线程 A 调用pthread_cond_wait等待,线程 B 完成后调用pthread_cond_signal唤醒 A。
常用数据结构按逻辑分类:
线性结构:
树形结构:
哈希结构:哈希表通过键映射存储,平均 O (1) 操作,用于缓存、字典。
图结构:由节点和边组成,用于网络拓扑、路径规划(如 Dijkstra 算法)。
以下哪种进程间通信方式是最快的?()
A. 管道(Pipe) B. 消息队列 C. 共享内存 D. 套接字(Socket)
答案:C
解析:共享内存通过内核分配物理内存并映射到多进程地址空间,进程直接读写内存,无需内核中转(无数据拷贝),是所有 IPC 中效率最高的。
fork()和vfork()创建子进程的核心区别是什么?
参考答案:
fork()采用写时复制(COW),子进程与父进程共享内存直到修改时复制;vfork()子进程完全共享父进程地址空间,修改会直接影响父进程。fork()父进程不阻塞,与子进程并发执行;vfork()父进程阻塞,直到子进程调用exec或exit才恢复。某程序中,两个线程通过全局变量int data = 0交换数据,线程 A 负责写入data = 100,线程 B 负责读取并打印data。但运行时线程 B 有时打印 0,有时打印 100,原因是什么?如何解决?
参考答案:
data导致数据竞争(线程 B 可能在线程 A 写入前读取)。pthread_mutex_t)保护data的读写,确保同一时间只有一个线程访问。Linux 中,以下哪个指令可用于查看进程占用的 CPU 和内存资源?()
A. ps -ef B. top C. netstat -tuln D. df -h
答案:B
解析:top是动态进程监控工具,实时显示进程的 CPU 使用率、内存占用等;ps -ef仅静态列出进程信息。
TCP/IP 是互联网的核心协议族,采用分层模型(通常简化为 4 层):
链路层:处理物理传输(如以太网、Wi-Fi),封装 MAC 地址,协议有 ARP(地址解析)。
网络层:负责跨网络路由,核心协议是 IP(定义 IP 地址和数据包格式),辅助协议有 ICMP(ping 命令基于此)、IGMP(组播)。
传输层
:提供端到端通信,核心协议:
应用层:直接为应用服务,协议有 HTTP(网页)、FTP(文件传输)、DNS(域名解析)、SSH(远程登录)等。
TCP 四次挥手的过程中,主动关闭方在第四次挥手后进入TIME_WAIT状态,等待2MSL的目的是什么?
参考答案:
TIME_WAIT期间再次回复。2MSL是报文最大生存时间的 2 倍,确保本次连接的所有报文已从网络中消失。工厂模式:创建型设计模式,定义一个工厂类负责创建其他类的实例,隐藏对象创建的细节(如具体类名、初始化参数)。分为简单工厂、工厂方法、抽象工厂。
为什么需要
:
某设备 ping 通目标 IP(如 192.168.1.1),但无法解析域名(如www.baidu.com),可能的原因有哪些?
参考答案:
/etc/resolv.conf中nameserver未配置或指向无效 IP。/etc/hosts中错误映射目标域名,导致解析冲突。systemctl restart systemd-resolved)。libjpeg 是处理 JPEG 图像的开源库,核心操作包括:
jpeg_read_header、jpeg_start_decompress),方便应用快速实现图像编解码,无需关注 JPEG 标准的复杂细节。核心要点:需经过 “硬件初始化→Bootloader→内核启动→根文件系统挂载→用户程序启动” 五个阶段:
硬件上电:CPU 从复位向量(如 0x0 地址)执行,初始化片内 SRAM、时钟等基础硬件。
Bootloader 阶段:
start_kernel)。内核启动:
root=/dev/mmcblk0p2等参数)。用户空间初始化:启动 init 进程(PID=1),解析/etc/inittab或 systemd 配置,启动服务(如网络、日志)。
运行应用程序:init 进程启动用户态应用(如 APP、脚本),系统进入正常运行状态。
核心要点:
定义:是一种描述硬件信息的结构化数据(.dts 文件,编译为.dtb),包含 CPU、内存、外设的型号、地址、引脚等信息。
解决的问题:替代传统 Linux 的 “板级代码(board.c)”,将硬件信息与内核代码分离,实现 “一套内核适配多硬件”(避免为不同板修改内核)。
驱动中获取属性:通过 of 函数族(Open Firmware API)解析设备树节点:
// 示例:获取节点"led@0x12340000"的"gpio"属性structdevice_node *node;int gpio_num;node = of_find_node_by_path("/soc/led@0x12340000"); // 查找节点of_property_read_u32(node, "gpio", &gpio_num); // 读取u32类型属性platform_data),减少内核补丁数量。compatible属性),符合 Linux “设备 - 驱动分离” 模型。核心要点:字符设备驱动通过cdev结构体注册,依赖file_operations实现文件操作接口。
驱动代码框架:
#include<linux/module.h>#include<linux/fs.h>#include<linux/cdev.h>#define DEV_NAME "mychar"#define DEV_MAJOR 0 // 动态分配主设备号staticint major = DEV_MAJOR;staticstructcdevmy_cdev;staticchar data_buf[1024]; // 数据缓冲区// open操作staticintmy_open(struct inode *inode, struct file *filp){ printk("mychar device opened\n");return0;}// read操作:从内核缓冲区读数据到用户空间staticssize_tmy_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){int ret = copy_to_user(buf, data_buf, count); // 内核→用户空间(必须用copy_to_user)return count - ret;}// write操作:从用户空间写数据到内核缓冲区staticssize_tmy_write(struct file *filp, constchar __user *buf, size_t count, loff_t *f_pos){int ret = copy_from_user(data_buf, buf, count); // 用户→内核空间(必须用copy_from_user)return count - ret;}// 文件操作集合staticstructfile_operationsmy_fops = { .owner = THIS_MODULE, .open = my_open, .read = my_read, .write = my_write,};// 模块初始化staticint __init mychar_init(void){dev_t dev;// 分配设备号if (major == 0) { alloc_chrdev_region(&dev, 0, 1, DEV_NAME); // 动态分配 major = MAJOR(dev); } else { dev = MKDEV(major, 0); register_chrdev_region(dev, 1, DEV_NAME); // 静态注册 }// 初始化cdev并添加到内核 cdev_init(&my_cdev, &my_fops); cdev_add(&my_cdev, dev, 1); printk("mychar driver loaded, major=%d\n", major);return0;}// 模块退出staticvoid __exit mychar_exit(void){ cdev_del(&my_cdev); unregister_chrdev_region(MKDEV(major, 0), 1); printk("mychar driver unloaded\n");}module_init(mychar_init);module_exit(mychar_exit);MODULE_LICENSE("GPL");测试方法:
编译驱动为.ko 模块,加载:insmod mychar.ko。
创建设备节点:mknod /dev/mychar c <major> 0(major 为驱动打印的主设备号)。
编写测试程序(用户态):
#include <stdio.h>#include <fcntl.h>int main() { int fd = open("/dev/mychar", O_RDWR); write(fd, "hello", 5); // 写入数据 char buf[10];read(fd, buf, 5); // 读取数据printf("read: %s\n", buf); // 输出"hello" close(fd);return 0;}HAL_NVIC_EnableIRQ()(使能中断)、HAL_GPIO_EXTI_Callback()(中断回调);request_irq()(注册中断服务函数)。volatile修饰共享变量;步骤:
获取中断号:
interrupts = <GIC_SPI 16 IRQ_TYPE_EDGE_FALLING>;(边缘触发);irq = platform_get_irq(pdev, 0);。注册中断处理函数:
int ret = request_irq(irq, irq_handler, IRQF_TRIGGER_FALLING, "my_device", dev);irq_handler:上半部函数(快速处理,如清中断标志),返回IRQ_HANDLED。处理下半部:若需耗时操作(如数据处理),在中断上半部中调度下半部(如 workqueue):
staticirqreturn_tirq_handler(int irq, void *dev_id){ schedule_work(&dev->work); // 调度workqueuereturn IRQ_HANDLED;}注销中断:free_irq(irq, dev);
schedule_work | ||
核心要点:中断处理分为两部分,平衡 “响应速度” 和 “处理效率”:
request_irq注册中断服务函数(ISR),在 ISR 中执行顶半部逻辑。tasklet:基于软中断,适合小任务(原子上下文,不能睡眠);workqueue):基于内核线程,适合大任务(可睡眠,允许阻塞操作);示例:串口接收中断
核心要点:Linux 内核支持多种调度策略,按实时性分为:
CFS(Completely Fair Scheduler):
SCHED_OTHER/SCHED_NORMAL),基于 “虚拟运行时间” 分配 CPU,确保各进程公平执行。实时调度:
SCHED_FIFO(先进先出):高优先级进程一旦获取 CPU,一直运行直到主动放弃或被更高优先级进程抢占。SCHED_RR(时间片轮转):同优先级实时进程按时间片轮流执行。其他策略:
SCHED_IDLE:最低优先级,仅当系统空闲时运行(如后台日志清理)。嵌入式选择原则:
SCHED_FIFO/SCHED_RR,并设置合适优先级。核心要点:
定义:MMU 是 CPU 内的硬件单元,负责虚拟地址到物理地址的转换,提供内存保护。
核心作用:
mmap系统调用)。嵌入式场景:
核心要点:
作用:根文件系统(rootfs)是 Linux 启动后挂载的第一个文件系统,提供用户空间基础环境(命令、库、配置文件、设备节点等),是用户态程序运行的载体。
常用类型及特点:
/tmp)。ext4:
yaffs2:
tmpfs:
squashfs:
核心要点:用户态(应用程序)与内核态(驱动 / 内核模块)隔离,需通过内核提供的接口交互:
系统调用(syscall):
open/read)触发系统调用,陷入内核态,内核处理后返回结果(如操作字符设备)。procfs/sysfs:
procfs(/proc):虚拟文件系统,内核通过文件暴露进程、内存等信息(如/proc/meminfo),应用可读写。sysfs(/sys):按设备层级组织的虚拟文件系统,驱动通过 sysfs 属性(如/sys/class/leds/led0/brightness)暴露设备状态,应用直接读写文件交互。共享内存(mmap):
remap_pfn_range将内核内存(如外设缓冲区)映射到用户虚拟地址,应用通过mmap获取地址后直接读写,适合大数据量交互(如摄像头数据)。netlink 套接字:
核心要点:配置网络需设置 IP 地址、子网掩码、网关、DNS 服务器,常用方式:
临时配置(重启失效):
ifconfig eth0 192.168.1.100 netmask 255.255.255.0route add default gw 192.168.1.1echo "nameserver 8.8.8.8" > /etc/resolv.conf永久配置(基于 systemd):
创建配置文件
/etc/systemd/network/eth0.network[Match]Name=eth0 # 匹配网卡名[Network]Address=192.168.1.100/24 # IP+子网掩码Gateway=192.168.1.1DNS=8.8.8.8DNS=114.114.114.114重启网络服务:systemctl restart systemd-networkd
/etc/init.d/rcS脚本中添加临时配置命令,开机自动执行。核心要点:
dmesg:
dmesg | grep "mychar"查看自定义驱动日志)。strace:
open/read/write)和信号,定位应用程序错误(如 “Permission denied” 是哪个系统调用失败)。strace ./app打印 app 的所有系统调用。gdb + gdbserver:
gdbserver :1234 ./app,开发机arm-linux-gnueabihf-gdb ./app,然后target remote 192.168.1.100:1234。perf:
硬件与设备树:在设备树中定义按键引脚(如key-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>),指定中断触发方式(如下降沿)。
内核驱动实现
:
input_report_key)。驱动加载:编译为.ko 模块,insmod加载,生成设备节点(如/dev/input/event1)。
应用层访问:打开/dev/input/event1,通过read读取input_event结构体,解析type(EV_KEY)、code(按键码)、value(0/1 表示松开 / 按下)。
约定相同波特率、设备地址和帧格式;
通过起始 / 停止位同步通信时序;
从设备以 ACK 应答确认接收,时钟拉伸协调主从速率;
上拉电阻 + 开漏输出实现线与逻辑,避免总线冲突。
Recovery 系统:独立于主系统的小型引导环境,用于系统修复、升级、恢复出厂设置。通常存储在单独的分区(如 recovery 分区),由 bootloader 启动。核心功能:挂载系统分区、执行 OTA 升级脚本、清除数据。
变砖处理
:
pin 复用指一个物理引脚可映射到多个功能(如 GPIO、UART_TX、SPI_CLK),实现步骤:
硬件层面:芯片手册定义引脚的复用功能(如引脚 10 可复用为 GPIO5_3 或 UART2_TX),通过复用寄存器配置。
设备树层面:在设备树中通过pinctrl节点指定引脚功能,例如:
pinctrl_uart2: uart2grp { pins = "pin10"; function = "uart2"; // 复用为UART2_TX};驱动层面:驱动通过devm_pinctrl_get_select绑定设备树的 pinctrl 配置,内核自动解析并配置复用寄存器,完成引脚功能切换。
复位信号有 “高电平有效” 和 “低电平有效”,处理方式:
设备树中声明极性:通过属性指定复位电平,例如:
reset-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; // 高电平有效驱动中适配:解析设备树属性获取极性,复位时输出对应电平:
gpio_set_value(reset_gpio, 1),结束后拉低;gpio_set_value(reset_gpio, 0),结束后拉高。复用通用函数:使用内核 gpio 接口(如gpio_set_active_low)自动处理极性转换,无需关心硬件电平细节。
不同外设复位所需延时不同(如有的需 10ms,有的需 100ms),处理方式:
设备树动态配置:通过自定义属性指定延时,例如:
reset-delay-ms = <50>; // 复位延时50ms驱动中读取延时:驱动解析设备树属性,使用内核延时函数等待:
unsigned int delay;of_property_read_u32(dev->of_node, "reset-delay-ms", &delay);msleep(delay); // 毫秒级延时(进程上下文)// 若在中断上下文,用udelay(微秒级,不睡眠)兼容固定延时:对无设备树配置的场景,设置默认延时(参考外设 datasheet),确保兼容性。
Linux RTC 的时间同步通过 RTC 驱动和用户态工具实现:
内核层面:RTC 驱动(如rtc-ds1307)注册为/dev/rtc0,内核启动时通过rtc_read_time读取 RTC 时间,同步到系统时间(xtime)。
用户层面:
date命令修改系统时间后,用hwclock -w将系统时间写入 RTC;hwclock -w,或驱动中实现周期性同步(如每小时)。自动同步:内核配置CONFIG_RTC_SYSTOHC后,系统会定期将系统时间同步到 RTC。
看门狗(Watchdog)是硬件定时器,核心原理:
初始化时设置超时时间(如 500ms),定时器开始倒计时;
系统需定期 “喂狗”(通过寄存器或驱动接口重置定时器),否则超时后硬件触发系统复位(防止死机)。
分为:
硬件看门狗:独立芯片(如 MAX706)或 SOC 内置模块,可靠性高;
软件看门狗:内核模块模拟(如 softdog),依赖内核正常运行,可靠性低。
喂狗时间需满足:喂狗周期 < 看门狗超时时间 < 系统最大允许无响应时间。
推荐用定时器喂狗,优势:
setitimer或timerfd创建定时事件(如 200ms 一次),定时向/dev/watchdog写入数据(如write(fd, "1", 1)),不占用 CPU,效率高。while(1):会持续占用 CPU(即使加sleep,精度也较低),且无法处理线程异常(如崩溃后无法喂狗)。判断方法:
查看内核配置:zcat /proc/config.gz | grep CONFIG_PREEMPT_RT,若为 y 则打了实时补丁(PREEMPT_RT)。
查看内核版本:
uname -a,实时内核通常含 rt 标识(如 5.10.100-rt50)。
实时 patch 通过 “全抢占内核”“中断线程化” 等优化,将内核响应延时从毫秒级降至微秒级,适合工业控制、自动驾驶等低延迟场景。
SCHED_FIFO 是实时调度策略(优先级 1-99),高优先级线程会抢占 CPU,若一直占用会导致低优先级线程 “饿死”,解决方法:
schedule_yield(),允许同优先级线程运行。work_queue(工作队列)和irq_thread(中断线程)有什么核心区别?参考答案:
work_queue由内核线程(kworker)统一调度,优先级较低;irq_thread是独立线程,可设置高优先级。work_queue需手动调用schedule_work触发;irq_thread由中断自动唤醒(内核关联中断与线程)。work_queue用于通用延迟任务;irq_thread适合中断处理较复杂但需睡眠的场景(如 I2C 通信)。参考答案:
这些题目覆盖了进程 / 线程通信、Linux 指令、网络协议、驱动开发、实时系统等核心考点,可用于检验对基础概念和实际应用的理解。
(结合岗位需求调整,以嵌入式开发岗为例)
“我叫 XX,毕业于 XX 学校 XX 专业,有 X 年嵌入式 Linux 开发经验。主要擅长 Linux 内核驱动开发(如字符设备、I2C/SPI 外设驱动)、设备树配置和应用层编程,熟悉 ARM 架构和常用通信协议(I2C/UART/SPI)。曾参与 XX 项目(如智能硬件传感器模块开发),负责从硬件驱动到应用层接口的全流程实现,解决过 XX 技术问题(如中断冲突、设备树兼容性)。熟练使用 GCC、Makefile、Git 等工具,了解数据结构和操作系统基础。期待加入贵团队,参与底层开发相关工作。”
(结合实际项目举例,突出技术栈和解决的问题)
“主要做过几类驱动开发:
字符设备驱动:如按键、LED、蜂鸣器,基于 GPIO 子系统实现,通过 input 子系统上报按键事件,支持中断触发;
总线设备驱动:I2C 接口的温湿度传感器(SHT30)、SPI 接口的显示屏(OLED),适配设备树,实现设备探测、数据读写逻辑;
外设驱动:UART 串口驱动(调试用)、RTC 实时时钟(DS3231),解决过时钟同步和中断冲突问题;
虚拟设备驱动:模拟一个字符设备用于进程间通信,通过 ioctl 提供控制接口。
开发中涉及设备树配置、中断处理、下半部机制(workqueue)、sysfs 接口封装等。”
end
一口Linux
关注,回复【1024】海量Linux资料赠送
精彩文章合集
文章推荐