一、篇头
继续使用qemu调试内核的实验。本章复习阻塞与非阻塞IO的概念和机制,然后对之前实验(七)的代码做少许修改,来实现非阻塞的IO读写。
二、系列文章
略……
三、实验环境
- 编译服务器+NFS:ubuntu 22.04
- Qemu 虚拟机:Linux version 5.15.102 + Buysbox 1.3.36 + ARM_32bit
Qemu 启动命令:qemu-system-arm -nographic -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage -initrd …/busybox/rootfs.ext4.img.gz -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb
四、源码
4.1 非阻塞IO与阻塞IO
非阻塞:进程发起I/O系统调用后,如果设备驱动的缓冲区没有数据,那么进程返回一个错误而不会被阻塞。如果驱动缓冲区中有数据,那么设备驱动把数据直接返回给用户进程。
阻塞:进程发起I/O系统调用后,如果设备的缓冲区没有数据,那么需要到硬件I/O中重新获取新数据,进程会被阻塞,也就是睡眠等待。直到数据准备好,进程才会被唤醒,并重新把数据返回给用户空间。
4.2 驱动源码
- 文件名:linux-stable\my_kmodules\test_7.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 MY_DEV_NAME "my_dev"
DEFINE_KFIFO(my_kfifo, char, 128);
static int test_6_open(struct inode *inode, struct file *file)
{
int major = MAJOR(inode->i_rdev);
int minor = MINOR(inode->i_rdev);
pr_info("%s: major=%d, minor=%d\n", __func__, major, minor);
return 0;
}
static int test_6_release(struct inode *inode, struct file *file)
{
pr_info("%s \n", __func__);
return 0;
}
static ssize_t test_6_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned int copied_count=0;
pr_info("%s \n", __func__);
/*
* (1)add for O_NONBLOCK reading
*/
if(kfifo_is_empty(&my_kfifo)){
if(file->f_flags & O_NONBLOCK){
return -EAGAIN;
}
}
ret = kfifo_to_user(&my_kfifo, buf, count, &copied_count);
if(ret != 0) {
return -EFAULT;
}
//pr_info("%s :%s, count=%d, copied_count=%d\n", __func__, my_kfifo.buf, count, copied_count);
return copied_count;
}
static ssize_t test_6_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
int ret;
unsigned int copied_count=0;
pr_info("%s \n", __func__);
/*
* (1)add for O_NONBLOCK writing
*/
if(kfifo_is_full(&my_kfifo)){
if(file->f_flags & O_NONBLOCK){
return -EAGAIN;
}
}
ret = kfifo_from_user(&my_kfifo, buf, count, &copied_count);
if(ret != 0) {
return -EFAULT;
}
//pr_info("%s , my_kfifo.buf=%s\n", __func__, my_kfifo.buf);
return copied_count;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_6_open,
.release = test_6_release,
.read = test_6_read,
.write = test_6_write
};
static struct miscdevice test_6_misc_device ={
.minor = MISC_DYNAMIC_MINOR,
.name = MY_DEV_NAME,
.fops = &test_fops,
};
static int __init test_6_init(void)
{
int ret;
pr_info("test_6_init\n");
ret = misc_register(&test_6_misc_device);
if (ret != 0 ) {
pr_err("failed to misc_register");
return ret;
}
pr_err("Minor number = %d\n", test_6_misc_device.minor);
return 0;
}
static void __exit test_6_exit(void)
{
pr_info("test_6_exit\n");
misc_deregister(&test_6_misc_device);
}
module_init(test_6_init);
module_exit(test_6_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("test_7, 使用misc、KFIFO开发设备驱动非阻塞设备驱动。");
4.3 APP源码
- 文件名:linux-stable\my_kmodules\app_test_7.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> //消除 warning: implicit declaration of function ‘malloc’ [-Wimplicit-function-declaration]
#include <string.h> //消除 warning: implicit declaration of function ‘memset’ [-Wimplicit-function-declaration]
#define MY_DEV_NAME "/dev/my_dev"
int main(int argc, void * argv[])
{
int fd;
int ret;
size_t len;
char message[80] = "To test reading and writing FIFO device with O_NONBLOCK.";
char *read_buffer;
len = sizeof(message);
read_buffer = (char *)malloc((size_t)2*len);
memset((void *)read_buffer, 0, 2*len);
fd = open(MY_DEV_NAME, O_RDWR | O_NONBLOCK);
if (fd < 0) {
printf("open device %s failded\n", MY_DEV_NAME);
return -1;
}
/*1: 先读一次,但这个时候KFIFO为空,所以会直接返回 */
ret = read(fd, read_buffer, (size_t)2*len);
printf("read %d bytes\n", ret);
printf("read buffer=%s\n", read_buffer);
/*2. 写一次数据 */
ret = write(fd, message, len);
if (ret != len)
printf("have write %d bytes\n", ret);
/*3. 再写一次数据 */
ret = write(fd, message, len);
if (ret != len)
printf("have write %d bytes\n", ret);
/*4. 读取数据,这次因为KFIFO非空,所以可以获得数据 */
ret = read(fd, read_buffer, 2*len);
printf("read %d bytes\n", ret);
printf("read buffer=%s\n", read_buffer);
close(fd);
}
4.4 Makefile
- 文件名:linux-stable\my_kmodules\Makefile
- 如下,继续追加 test_7.o 即可编译出test_7.ko
KDIR := /home/szhou/works/qemu_linux/linux-stable
obj-m := test_1.o test_2.o test_3.o test_4.o test_5.o test_6.o test_7.o
all :
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
rm -f *.ko
五、编译及部署
(1)执行驱动KO编译
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ make
make -C /home/szhou/works/qemu_linux/linux-stable M=/home/szhou/works/qemu_linux/linux-stable/my_kmodules modules
make[1]: Entering directory '/home/szhou/works/qemu_linux/linux-stable'
CC [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_7.o
MODPOST /home/szhou/works/qemu_linux/linux-stable/my_kmodules/Module.symvers
CC [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_7.mod.o
LD [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_7.ko
make[1]: Leaving directory '/home/szhou/works/qemu_linux/linux-stable'
(2)执行app编译
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ arm-linux-gnueabi-gcc app_test_7.c -o app_test_7 --static
(3)将KO和APP存放到NFS共享目录
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_7.ko app_test_7 ~/works/nfs_share/
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$
六、运行及测试
(1)启动之前编译组建的QEMU虚拟机
----------------------------------------
Welcome to szhou's tiny Linux
----------------------------------------
Please press Enter to activate this console.
(2)挂载NFS共享目录
~ # mount -t nfs -o nolock 192.168.3.67:/home/szhou/works/nfs_share /mnt
(3) 加载ko
~ # cd /mnt/
/mnt # insmod test_7.ko
test_7: loading out-of-tree module taints kernel.
test_6_init
Minor number = 125
/mnt # mdev -s
(4) 运行app测试
/mnt # ./app_test_7
test_6_open: major=10, minor=125
test_6_read
read -1 bytes
read buffer=
test_6_write
test_6_write
have write 48 bytes
test_6_read
read 128 bytes
read buffer=To test reading and writing FIFO device with O_NONBLOCK.
test_6_release
/mnt #
操作画面如下图: