技术知识库
扩展:为 KFIFO 邮箱加入 Nonblocking I/O
扩展:为 KFIFO 邮箱加入 Nonblocking I/O
摘要
本扩展在现有 kfifo_mailbox 上检测 file->f_flags 中的 O_NONBLOCK。FIFO 满时,非阻塞写返回 -EAGAIN,用户空间显示 Resource temporarily unavailable;该方向已实际验证。空 FIFO 的非阻塞读使用相同语义,但尚缺最终运行输出,因此本笔记暂为 growing。
环境与路径
目标机:QEMU ARM64,Linux 5.0.0+,Debian 12
源码目录:/home/martin-zou/workspace/gcc-lab/char_mod/kfifo_mailbox
内核构建目录:/home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64
设备节点:/dev/kfifo_mailbox
KFIFO 容量:256 字节
前置主线:使用 KFIFO 实现可读写邮箱设备。
1. 修改驱动源码
cd /home/martin-zou/workspace/gcc-lab/char_mod/kfifo_mailbox
vim kfifo_mailbox.c
完整整理版:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#define KFIFO_MAILBOX_NAME "kfifo_mailbox"
#define KFIFO_MAILBOX_SIZE 256
DEFINE_KFIFO(kfifo_mailbox_fifo, char, KFIFO_MAILBOX_SIZE);
static int kfifo_mailbox_open(struct inode *inode, struct file *file)
{
pr_info("%s: major=%d, minor=%d, nonblock=%d\n",
__func__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
!!(file->f_flags & O_NONBLOCK));
return 0;
}
static ssize_t kfifo_mailbox_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
unsigned int copied;
int ret;
if (kfifo_is_empty(&kfifo_mailbox_fifo) &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
ret = kfifo_to_user(&kfifo_mailbox_fifo, buf, count, &copied);
if (ret)
return ret;
pr_info("kfifo_mailbox: read %u bytes\n", copied);
return copied;
}
static ssize_t kfifo_mailbox_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
unsigned int copied;
int ret;
if (kfifo_is_full(&kfifo_mailbox_fifo) &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
ret = kfifo_from_user(&kfifo_mailbox_fifo, buf, count, &copied);
if (ret)
return ret;
if (count != 0 && copied == 0)
return -ENOSPC;
pr_info("kfifo_mailbox: wrote %u bytes\n", copied);
return copied;
}
static const struct file_operations kfifo_mailbox_ops = {
.owner = THIS_MODULE,
.open = kfifo_mailbox_open,
.read = kfifo_mailbox_read,
.write = kfifo_mailbox_write,
};
static struct miscdevice kfifo_mailbox_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = KFIFO_MAILBOX_NAME,
.fops = &kfifo_mailbox_ops,
};
static int __init kfifo_mailbox_init(void)
{
int ret;
ret = misc_register(&kfifo_mailbox_device);
if (ret)
return ret;
pr_info("kfifo_mailbox: registered, major=%d, minor=%d\n",
MISC_MAJOR, kfifo_mailbox_device.minor);
return 0;
}
static void __exit kfifo_mailbox_exit(void)
{
misc_deregister(&kfifo_mailbox_device);
pr_info("kfifo_mailbox: unregistered\n");
}
module_init(kfifo_mailbox_init);
module_exit(kfifo_mailbox_exit);
MODULE_AUTHOR("martin.zou");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("nonblocking KFIFO misc mailbox");
2. Makefile
vim Makefile
完整内容:
KERNEL_DIR ?= /home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64
ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-
obj-m += kfifo_mailbox.o
.PHONY: all clean
all:
$(MAKE) -C $(KERNEL_DIR) \
M=$(CURDIR) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(CROSS_COMPILE) \
modules
clean:
$(MAKE) -C $(KERNEL_DIR) \
M=$(CURDIR) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(CROSS_COMPILE) \
clean
3. 构建、复制和加载
宿主机:
make clean
make -j"$(nproc)"
scp \
-P 2222 \
kfifo_mailbox.ko \
martin@127.0.0.1:~/
Guest:
sudo rmmod kfifo_mailbox 2>/dev/null || true
sudo insmod /home/martin/kfifo_mailbox.ko
ls -l /dev/kfifo_mailbox
4. 验证非阻塞写
先清空,再用普通方式写满256字节:
cat /dev/kfifo_mailbox >/dev/null
dd \
if=/dev/zero \
of=/dev/kfifo_mailbox \
bs=256 \
count=1 \
status=none
然后把输出设备以 O_NONBLOCK 打开,再写1字节:
printf X |
dd \
of=/dev/kfifo_mailbox \
oflag=nonblock \
bs=1 \
count=1 \
status=none
echo "status=$?"
实际结果:
dd: error writing '/dev/kfifo_mailbox': Resource temporarily unavailable
status=1
这证明满 FIFO 的非阻塞写进入 O_NONBLOCK 分支并返回了 -EAGAIN。
5. 验证非阻塞读
先清空 FIFO:
cat /dev/kfifo_mailbox >/dev/null
把输入设备以 O_NONBLOCK 打开:
dd \
if=/dev/kfifo_mailbox \
of=/dev/null \
iflag=nonblock \
bs=1 \
count=1 \
status=none
echo "status=$?"
预期:
dd: error reading '/dev/kfifo_mailbox': Resource temporarily unavailable
status=1
完成标准
- 驱动能区分普通文件描述符和
O_NONBLOCK文件描述符。 - 满 FIFO 的非阻塞写返回
-EAGAIN。 - 用户空间观察到
Resource temporarily unavailable和退出状态1。 - 空 FIFO 的非阻塞读返回
-EAGAIN,并取得实际输出证据。 - 完成读方向验证后把本笔记状态更新为
evergreen。
关联
待验证
- 运行空 FIFO 的
iflag=nonblock读取测试。 - 后续使用等待队列实现普通打开时真正的阻塞读写。