一、篇头
继续使用qemu调试内核的实验。
二、系列文章
略……
三、实验环境
- 编译服务器+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 KFIFO介绍
Linux内核实现了一个称为KFIFO的环形缓冲区的机制,它可以在一个读者线程和一个写者线程并发执行的场景下,无须使用额外的加锁来保证环形缓冲区的数据安全。同时由此机制来管理了缓冲区的使用情况,省去许多是,特别方便。
KFIFO提供的接口函数定义在include/linux/kfifo.h文件中。
4.1.1 声明及定义:DEFINE_KFIFO(fifo, type, size)
/**
* DEFINE_KFIFO - macro to define and initialize a fifo
* @fifo: name of the declared fifo datatype
* @type: type of the fifo elements
* @size: the number of elements in the fifo, this must be a power of 2
*
* Note: the macro can be used for global and local fifo data type variables.
*/
#define DEFINE_KFIFO(fifo, type, size) \
DECLARE_KFIFO(fifo, type, size) = \
(typeof(fifo)) { \
{ \
{ \
.in = 0, \
.out = 0, \
.mask = __is_kfifo_ptr(&(fifo)) ? \
0 : \
ARRAY_SIZE((fifo).buf) - 1, \
.esize = sizeof(*(fifo).buf), \
.data = __is_kfifo_ptr(&(fifo)) ? \
NULL : \
(fifo).buf, \
} \
} \
}
4.1.2 用户写:kfifo_from_user(fifo, from, len, copied)
/**
* kfifo_from_user - puts some data from user space into the fifo
* @fifo: address of the fifo to be used
* @from: pointer to the data to be added
* @len: the length of the data to be added
* @copied: pointer to output variable to store the number of copied bytes
*
* This macro copies at most @len bytes from the @from into the
* fifo, depending of the available space and returns -EFAULT/0.
*
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macro.
*/
#define kfifo_from_user(fifo, from, len, copied) \
__kfifo_uint_must_check_helper( \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
const void __user *__from = (from); \
unsigned int __len = (len); \
unsigned int *__copied = (copied); \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
__kfifo_from_user_r(__kfifo, __from, __len, __copied, __recsize) : \
__kfifo_from_user(__kfifo, __from, __len, __copied); \
}) \
)
4.1.3 用户读:kfifo_to_user(fifo, to, len, copied)
/**
* kfifo_to_user - copies data from the fifo into user space
* @fifo: address of the fifo to be used
* @to: where the data must be copied
* @len: the size of the destination buffer
* @copied: pointer to output variable to store the number of copied bytes
*
* This macro copies at most @len bytes from the fifo into the
* @to buffer and returns -EFAULT/0.
*
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macro.
*/
#define kfifo_to_user(fifo, to, len, copied) \
__kfifo_int_must_check_helper( \
({ \
typeof((fifo) + 1) __tmp = (fifo); \
void __user *__to = (to); \
unsigned int __len = (len); \
unsigned int *__copied = (copied); \
const size_t __recsize = sizeof(*__tmp->rectype); \
struct __kfifo *__kfifo = &__tmp->kfifo; \
(__recsize) ? \
__kfifo_to_user_r(__kfifo, __to, __len, __copied, __recsize) : \
__kfifo_to_user(__kfifo, __to, __len, __copied); \
}) \
)
4.2 驱动源码
- 文件名:linux-stable\my_kmodules\test_5.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 char kbuf[128];
static int test_5_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_5_release(struct inode *inode, struct file *file)
{
pr_info("%s \n", __func__);
return 0;
}
static ssize_t test_5_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__);
ret = kfifo_to_user(&my_kfifo, buf, count, &copied_count);
if(ret != 0) {
return -EIO;
}
pr_info("%s :%s, count=%d, copied_count=%d\n", __func__, my_kfifo.buf, count, copied_count);
return copied_count;
}
static ssize_t test_5_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__);
ret = kfifo_from_user(&my_kfifo, buf, count, &copied_count);
if(ret != 0) {
return -EIO;
}
pr_info("%s , my_kfifo.buf=%s\n", __func__, my_kfifo.buf);
//注意:使用用户空间的buf,需要先copy_from_user
ret = copy_from_user(kbuf, buf, copied_count);
pr_info("%s :%s, count=%d, copied_count=%d, ret=%d\n", __func__, kbuf, count, copied_count, ret);
return copied_count;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_5_open,
.release = test_5_release,
.read = test_5_read,
.write = test_5_write
};
static struct miscdevice test_5_misc_device ={
.minor = MISC_DYNAMIC_MINOR,
.name = MY_DEV_NAME,
.fops = &test_fops,
};
static int __init test_5_init(void)
{
int ret;
pr_info("test_5_init\n");
ret = misc_register(&test_5_misc_device);
if (ret != 0 ) {
pr_err("failed to misc_register");
return ret;
}
pr_err("Minor number = %d\n", test_5_misc_device.minor);
return 0;
}
static void __exit test_5_exit(void)
{
pr_info("test_5_exit\n");
misc_deregister(&test_5_misc_device);
}
module_init(test_5_init);
module_exit(test_5_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("test_5, 使用misc、KFIFO开发设备驱动");
4.3 APP源码
本次实验,直接使用 echo /cat 负责设备的写/读。
4.4 Makefile
- 文件名:linux-stable\my_kmodules\Makefile
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
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_5.o
MODPOST /home/szhou/works/qemu_linux/linux-stable/my_kmodules/Module.symvers
LD [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_5.ko
make[1]: Leaving directory '/home/szhou/works/qemu_linux/linux-stable'
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_5.ko ~/works/nfs_share/
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$
(2)将KO和APP存放到NFS共享目录
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_5.ko ~/works/nfs_share/
六、运行及测试
(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_5.ko
test_5: loading out-of-tree module taints kernel.
test_5_init
Minor number = 125
/mnt #
/mnt # mdev -s
/mnt #
(4)写入字符串
/mnt # echo "hellow kfifo" > /dev/my_dev
test_5_open: major=10, minor=125
test_5_write
test_5_write , my_kfifo.buf=hellow kfifo
test_5_write :hellow kfifo
, count=13, copied_count=13, ret=0
test_5_release
/mnt #
(5)读取字符串
/mnt # cat /dev/my_dev
test_5_open: major=10, minor=125
test_5_read
test_5_read :hellow kfifo
, count=4096, copied_count=13
hellow kfifo
test_5_read
test_5_read :hellow kfifo
, count=4096, copied_count=0
test_5_release
/mnt #
操作画面如下图: