SPI 子系统 对比 操作系统
SPI 子系统 ↔ 操作系统
用户态进程的 IO 请求和 SPI 设备驱动的数据传输请求,在抽象层面面对的是同一个问题:如何协调多个请求者,公平高效地访问一份共享资源。
下面从几个维度逐条对比。
1. 调度
操作系统: CPU 只有一个,多个进程想用 →
调度器(CFS/O(1))决定下一个谁跑
SPI 子系统: SPI 总线只有一条,多个 message 想发 →
消息泵(spi_pump_messages)决定下一个谁传
OS 的调度器从 runqueue 里挑进程,SPI 的消息泵从 master->queue 里挑 message。代码里对应:
// spi.c:1428
static int __spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
list_add_tail(&msg->queue, &master->queue); // 入队
kthread_queue_work(&master->kworker, &master->pump_messages); // 唤醒调度
}
注释也很有意思——仲裁算法"未指定",但核心就是 FIFO:
The master’s main job is to process its message queue
If there are multiple spi_device children, the i/o queue arbitration algorithm is unspecified
2. 同步与异步 IO
操作系统: read() 可阻塞(同步)→ 进程睡眠等数据
或 aio_read/epoll(异步)→ 回调通知
SPI 子系统: spi_sync() → completion 等待唤醒
spi_async() → complete 回调通知
// 同步路径 → 类似阻塞 read()
spi_sync(spi, &msg) {
msg.complete = spi_complete; // 内部 completion
msg.context = &done;
spi_async(spi, &msg); // 入队
wait_for_completion(&done); // 睡下去等
return msg.status;
}
// 异步路径 → 类似 aio_read
msg.complete = my_callback;
msg.context = my_data;
spi_async(spi, &msg); // 入队就返回,完成后调 my_callback
同步路径就像调 read() 时进程被 blocking,异步路径就像投递一个 AIO 请求。
3. 中断与下半部
操作系统: 硬件中断 → 中断处理程序(上半个)→ 软中断/tasklet/workqueue(下半个)
上半个快进快出,下半个做重活
SPI 子系统: 控制器 FIFO 中断 → 中断处理器(逐字节搬运)→ spi_finalize_current_message(唤醒等待者)
类似地上半搬运数据,下半唤醒上层
以 spi-imx 为例的中断处理模式:
// 中断上半部:抄 FIFO
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
// 从 FIFO 读一个字节到 rx_buf
// 或往 FIFO 写一个字节从 tx_buf
// 如果传输完成 → spi_finalize_current_transfer
}
4. 锁机制
操作系统: 自旋锁(短临界区,中断上下文可用)
互斥锁(长临界区,可睡眠)
SPI 子系统: queue_lock(自旋锁)→ 保护 message 入队,中断中可用
io_mutex(互斥锁)→ 保护对控制器的长操作
同一段代码中两种锁共存:
struct spi_master {
spinlock_t queue_lock; // 自旋锁 → 入队操作
struct mutex io_mutex; // 互斥锁 → 总线访问
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex; // 互斥锁 → 总线锁定
};
5. 优先级
操作系统: nice 值 / RT 优先级(SCHED_FIFO)
高优先级进程抢占低优先级
SPI 子系统: master->rt 标志 → 消息泵线程 SCHED_FIFO + MAX_RT_PRIO-1
→ 接近硬实时优先级
// spi.c:1268
if (master->rt) {
sched_setscheduler(master->kworker_task, SCHED_FIFO, ¶m);
// param.sched_priority = MAX_RT_PRIO - 1 ≈ 98(Linux 默认 RT 最高 99)
}
如果某个 SPI 设备对延迟敏感(比如音频编解码器),controller 驱动设 master->rt = true,消息泵线程就获得了接近实时的调度优先级。
6. 内存管理
操作系统: malloc/free,mmap
DMA 区域映射
SPI 子系统: spi_alloc_master / spi_master_put
spi_map_msg / spi_unmap_msg(DMA 映射)
全局 buf 就像一个小型内核缓冲区池——给短传输节省一次 kmalloc:
static u8 *buf;
// spi_init 中一次分配,spi_write_then_read 复用
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
7. 设备即文件
操作系统: open() → write() → close()
所有东西都是文件
SPI 子系统: spidev → open → ioctl(SPI_IOC_MESSAGE) → close
SPI 设备通过 spidev 变成了 /dev/spidev0.0
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = spidev_ioctl,
.open = spidev_open,
.release = spidev_release,
};
一张表收尾
| 维度 | 操作系统 | SPI 子系统 |
|---|---|---|
| 资源 | CPU | SPI 总线 |
| 请求 | 进程 | spi_message |
| 调度器 | CFS / SCHED_FIFO | spi_pump_messages + kthread |
| 同步 IO | blocking read/write | spi_sync + completion |
| 异步 IO | aio / epoll | spi_async + complete 回调 |
| 中断处理 | 上半部 + 下半部 | ISR + spi_finalize_current_message |
| 锁 | spinlock / mutex | queue_lock / io_mutex |
| 优先级 | nice / RT | master->rt → SCHED_FIFO |
| 内存管理 | kmalloc / vmalloc | spi_alloc_master / spi_map_msg |
| 用户态接口 | 系统调用 | spidev ioctl |
SPI 子系统就是一个针对单条 SPI 总线做了极简设计的微型操作系统——它有调度器(消息泵)、同步/异步 IO 模型、中断驱动、多种锁机制、甚至支持实时优先级。只不过它不管理 CPU 时间片,而管理 spi_message 在总线上的传输顺序。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)