并发、缓冲、阻塞、轮询与生命周期
1. 执行上下文
字符驱动同时面对:
- process context的open/read/write/ioctl/mmap;
- IRQ/threaded IRQ;
- workqueue/timer;
- PM callback;
- device remove;
- async notification;
- fork/dup后的共享file。
User copy可fault和睡眠,不能在spinlock/hardirq执行。
2. Per-device与Per-file
Per-device:
- MMIO/IRQ;
- hardware queue;
- global configuration;
- remove状态;
- class/cdev。
Per-file:
- read offset;
- pending response;
- mode/filter;
- event cursor;
- ownership;
- references。
把per-file command response放全局buffer会导致用户间串线。
3. 阻塞Read
标准循环:
1 | lock |
条件必须在锁下重查,waitqueue只负责通知,不保存事件。
4. Write Backpressure
Buffer满时:
- nonblock返回
-EAGAIN; - blocking等待space;
- device remove返回
-ENODEV/EPIPE; - signal可能返回short write。
Driver不能无限分配内存吸收用户数据。
5. Poll
先poll_wait()注册waitqueue,再读取当前状态。常见返回:
EPOLLIN|EPOLLRDNORM:有数据;EPOLLOUT|EPOLLWRNORM:有空间;EPOLLERR:硬件/协议错误;EPOLLHUP:对端或设备消失。
Wake时可带poll mask减少无效唤醒。
6. Ring Buffer
IRQ producer与process consumer:
- spinlock保护head/tail;
- release/acquire或barrier保证payload先于index可见;
- 长copy可在锁外进行但要固定buffer ownership;
- wrap和size必须防溢出;
- overflow策略要明确drop/newest/oldest/error。
7. Ioctl
Ioctl配置常与read/write并发。需要:
- mutex串行硬件模式;
- 防止模式切换使pending buffer解释失效;
copy_from_user前检查size;- reserved清零;
- compat转换;
- command超时;
- remove状态检查。
8. Mmap
Mmap建立后close不会自动撤销VMA。Driver要用VMA open/close引用对象,并在remove时:
- 禁止新mmap;
- 停止DMA;
- 必要时zap mapping;
- 等VMA引用;
- 再释放pages/MMIO。
不要把包含其它寄存器的整页映射给用户。
9. Remove
安全状态机:
1 | LIVE |
只调用device_destroy()不会让已有fd失效。
10. Module引用
fops.owner=THIS_MODULE阻止open期间卸载模块代码,但不能解决:
- platform remove释放private data;
- USB/PCI热拔;
- device-managed MMIO失效;
- VMA长于fd;
-异步callback。
需要kref、get_device()或明确disconnect对象。
11. PM
Open/read前runtime get,完成后autosuspend。阻塞read不能永久持电源引用,除非硬件必须持续监听。
Suspend应唤醒或冻结等待者,并保存能够恢复的状态。Resume后硬件queue可能丢失,需向用户报告。
12. 故障注入
测试:
- 多进程并发;
- nonblock/poll;
- signal;
- short user buffer;
- device unplug;
- suspend中I/O;
- mmap后remove;
- command timeout;
- buffer overflow;
- 32位compat应用。
正在加载留言…