1.环境
ubuntu 18.04.5
gcc 7.5.0
内核版本:5.4.0-66-generic
2.文件介绍
Makefile - 内核模块Makefile
mem_dev.c - 内核模块代码
mem_dev.h - 内核模块代码
mem_dev_test.c - 内核模块用户空间测试程序代码
mem_dev_test.sh - 内核模块用户空间测试脚本
3.实现功能
(1)实现了内核模块字符设备驱动,向用户空间提供了open、read、write、close函数。
(2)提供了阻塞和非阻塞模式。在阻塞模式下,写入的数据长度大于缓冲区的空闲长度,则睡眠,读出的数据长度大于填充的数据长度,则睡眠,数据满足要求时被唤醒 。非阻塞模式下,无论是否操作成功都立刻返回。
(3)内核模块使用环形缓冲区保存数据。
(4)导出调试属性,可设置调试等级,属性文件路径为:/sys/devices/virtual/mem_dev_class/mem_dev/mem_dev_dbg。
(5)导出设置属性,可设置环形缓冲区的大小,属性文件路为:/sys/devices/virtual/mem_dev_class/mem_dev/mem_dev_set。
4.字符设备驱动源码
/*===========================mem_dev.h================================*/
#ifndef MEM_DEV_H
#define MEM_DEV_H
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#define DEFAULT_SIZE (4 * 1024)
#define MAX_SIZE (0x7FFFFFFF)
#define DBG_LVL_ERROR (1)
#define DBG_LVL_INFO (10)
struct bytes_ring
{
char* buf; // 存储数据的缓冲区
unsigned int size; // 缓冲区的字节数
unsigned int empty_size; // 缓冲区空闲的字节数
unsigned int full_size; // 缓冲区已填充的字节数
unsigned int r_pos; // 缓冲区的写位置
unsigned int w_pos; // 缓冲区的读位置
};
struct mem_dev
{
struct cdev cdev; // 字符设备结构体
struct bytes_ring ring;
struct mutex mutex; // 用于同步的互斥体
wait_queue_head_t r_wq; // 读等待队列头
wait_queue_head_t w_wq; // 写等待队列头
dev_t devno; // 设备号
struct class* mem_dev_class;
struct device* mem_dev_device;
unsigned int dbg_lvl;
};
#endif // MEM_DEV_H
/*===========================mem_dev.c================================*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include <linux/vmalloc.h>
#include <linux/sched/signal.h>
#include "mem_dev.h"
static struct mem_dev* mem_dev = NULL;
static int bytes_ring_init(struct bytes_ring* ring, unsigned int size)
{
ring->buf = vzalloc(size);
if (NULL == ring->buf)
return -ENOMEM;
ring->size = size;
ring->empty_size = size;
ring->full_size = 0;
ring->r_pos = ring->w_pos = 0;
return 0;
}
static void bytes_ring_destroy(struct bytes_ring* ring)
{
if (ring->buf) {
vfree(ring->buf);
ring->buf = NULL;
}
ring->size = 0;
ring->empty_size = 0;
ring->full_size = 0;
ring->r_pos = ring->w_pos = 0;
}
static inline unsigned int bytes_ring_empty_size(const struct bytes_ring* ring)
{
return ring->empty_size;
}
static inline unsigned int bytes_ring_full_size(const struct bytes_ring* ring)
{
return ring->full_size;
}
/*=========================================================
* 函数名称:bytes_ring_write
* 函数功能:向环形缓冲区中写入数据
* 参数:ring-环形缓冲区,buf-数据缓冲区,len-写入数据的字节数
* 返回值:大于等于0-写入数据的字节数,小于0-出错
*=========================================================*/
static int bytes_ring_write(struct bytes_ring* ring, const char* buf, unsigned int len)
{
int ret = 0;
unsigned int w_num, w_tmp_num1, w_tmp_num2;
unsigned int empty_size = bytes_ring_empty_size(ring);
w_num = ((len <= empty_size) ? len : empty_size);
if (0 == w_num)
return 0;
if ((ring->w_pos + w_num) >= ring->size) {
w_tmp_num1 = ring->size - ring->w_pos;
ret = copy_from_user(&ring->buf[ring->w_pos], &buf[0], w_tmp_num1);
if (0 != ret)
return -EFAULT;
w_tmp_num2 = w_num - w_tmp_num1;
ret = copy_from_user(&ring->buf[0], &buf[w_tmp_num1], w_tmp_num2);
if (0 != ret) {
ring->w_pos = 0;
ring->full_size += w_tmp_num1;
ring->empty_size -= w_tmp_num1;
return -EFAULT;
}
ring->w_pos = w_tmp_num2;
} else {
ret = copy_from_user(&ring->buf[ring->w_pos], buf, w_num);
if (0 != ret)
return -EFAULT;
ring->w_pos += w_num;
}
ring->full_size += w_num;
ring->empty_size -= w_num;
if (DBG_LVL_INFO == mem_dev->dbg_lvl) {
pr_info("ring buffer: size %u, empty_size %u, full_size %u, "
"r_pos %u, w_pos %u\n", ring->size, ring->empty_size, ring->full_size,
ring->r_pos, ring->w_pos);
}
return w_num;
}
/*=========================================================
* 函数名称:bytes_ring_read
* 函数功能:从环形缓冲区中读出数据
* 参数:ring-环形缓冲区,buf-数据缓冲区,len-读出数据的字节数
* 返回值:大于等于0-读出数据的字节数,小于0-出错
*=========================================================*/
static int bytes_ring_read(struct bytes_ring* ring, char* buf, unsigned int len)
{
int ret = 0;
unsigned int r_num, r_tmp_num1, r_tmp_num2;
unsigned int full_size = bytes_ring_full_size(ring);
r_num = ((len <= full_size) ? len : full_size);
if (0 == r_num)
return 0;
if ((ring->r_pos + r_num) >= ring->size) {
r_tmp_num1 = ring->size - ring->r_pos;
ret = copy_to_user(&buf[0], &ring->buf[ring->r_pos], r_tmp_num1);
if (0 != ret)
return -EFAULT;
r_tmp_num2 = r_num - r_tmp_num1;
ret = copy_to_user(&buf[r_tmp_num1], &ring->buf[0], r_tmp_num2);
if (0 != ret) {
ring->r_pos = 0;
ring->full_size -= r_tmp_num1;
ring->empty_size += r_tmp_num1;
return -EFAULT;
}
ring->r_pos = r_tmp_num2;
} else {
ret = copy_to_user(&buf[0], &ring->buf[ring->r_pos], r_num);
if (0 != ret)
return -EFAULT;
ring->r_pos += r_num;
}
ring->full_size -= r_num;
ring->empty_size += r_num;
if (DBG_LVL_INFO == mem_dev->dbg_lvl) {
pr_info("ring buffer: size %u, empty_size %u, full_size %u, "
"r_pos %u, w_pos %u\n", ring->size, ring->empty_size, ring->full_size,
ring->r_pos, ring->w_pos);
}
return r_num;
}
static int mem_dev_open(struct inode* inode, struct file* filp)
{
struct mem_dev* dev = container_of(inode->i_cdev, struct mem_dev, cdev);
filp->private_data = dev;
pr_info("mem_dev module open\n");
return 0;
}
static int mem_dev_release(struct inode* inode, struct file* filp)
{
pr_info("mem_dev module close\n");
return 0;
}
static ssize_t mem_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
int ret = 0;
struct mem_dev* dev = filp->private_data;
if (0 == size) {
return 0;
}
// 非阻塞读
if (filp->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&dev->mutex))
return -EBUSY; // 非阻塞模式下无法获取锁,返回错误
ret = bytes_ring_read(&dev->ring, buf, size);
mutex_unlock(&dev->mutex);
} else { // 阻塞读
mutex_lock(&dev->mutex);
if (bytes_ring_full_size(&dev->ring) < size) {
do {
mutex_unlock(&dev->mutex);
ret = wait_event_interruptible(dev->r_wq,
(bytes_ring_full_size(&dev->ring) >= size));
if (signal_pending(current)) { // 判断是否是信号引起的唤醒
ret = -ERESTARTSYS;
goto out;
}
mutex_lock(&dev->mutex);
} while (bytes_ring_full_size(&dev->ring) < size);
}
ret = bytes_ring_read(&dev->ring, buf, size);
if (0 != waitqueue_active(&dev->w_wq))
wake_up_interruptible_all(&dev->w_wq);
mutex_unlock(&dev->mutex);
}
out:
return ret;
}
static ssize_t mem_dev_write(struct file* filp,
const char __user* buf, size_t size, loff_t* ppos)
{
int ret = 0;
struct mem_dev* dev = filp->private_data;
if (0 == size) {
return 0;
}
// 非阻塞写
if (filp->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&dev->mutex))
return -EBUSY; // 非阻塞模式下无法获取锁,返回错误
ret = bytes_ring_write(&dev->ring, buf, size);
mutex_unlock(&dev->mutex);
} else { // 阻塞写
mutex_lock(&dev->mutex);
if (bytes_ring_empty_size(&dev->ring) < size) {
do {
mutex_unlock(&dev->mutex);
ret = wait_event_interruptible(dev->w_wq,
(bytes_ring_empty_size(&dev->ring) >= size));
if (signal_pending(current)) { // 判断是否是信号引起的唤醒
ret = -ERESTARTSYS;
goto out;
}
mutex_lock(&dev->mutex);
} while (bytes_ring_empty_size(&dev->ring) < size);
}
ret = bytes_ring_write(&dev->ring, buf, size);
if (0 != waitqueue_active(&dev->r_wq))
wake_up_interruptible_all(&dev->r_wq);
mutex_unlock(&dev->mutex);
}
out:
return ret;
}
// 文件操作函数结构体
static const struct file_operations mem_dev_fops = {
.owner = THIS_MODULE,
.write = mem_dev_write,
.read = mem_dev_read,
.open = mem_dev_open,
.release = mem_dev_release,
};
static ssize_t mem_dev_dbg_show(struct device *dev, struct device_attribute *attr,char *buf)
{
return sprintf(buf, "mem_dev_dbg level %u\n", mem_dev->dbg_lvl);
}
static ssize_t mem_dev_dbg_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{ int err;
unsigned int lvl;
err = kstrtouint(buf, 10, &lvl);
if (err)
return err;
mem_dev->dbg_lvl = lvl;
return count;
}
// 设置属性,生成的属性结构体名称为dev_attr_mem_dev_dbg,类型为struct device_attribute
static DEVICE_ATTR(mem_dev_dbg, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH,
mem_dev_dbg_show, mem_dev_dbg_store);
static ssize_t mem_dev_set_show(struct device *dev, struct device_attribute *attr,char *buf)
{
return sprintf(buf, "ring buf size %u\n", mem_dev->ring.size);
}
static ssize_t mem_dev_set_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned int size;
err = kstrtouint(buf, 10, &size);
if (err)
return err;
if (size > MAX_SIZE)
return -EINVAL;
mutex_lock(&mem_dev->mutex);
bytes_ring_destroy(&mem_dev->ring);
bytes_ring_init(&mem_dev->ring, size); // 重新设置环形缓冲区大小
mutex_unlock(&mem_dev->mutex);
return count;
}
// 设置属性,生成的属性结构体名称为dev_attr_mem_dev_set,类型为struct device_attribute
static DEVICE_ATTR(mem_dev_set, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH,
mem_dev_set_show, mem_dev_set_store);
static struct attribute* mem_dev_attrs[] = {
&dev_attr_mem_dev_dbg.attr, // 调试属性
&dev_attr_mem_dev_set.attr, // 设置属性
NULL,
};
// 创建mem_dev属性组
static struct attribute_group mem_dev_attr_group = {
.attrs = mem_dev_attrs,
};
// 初始化和注册cdev结构体
static int set_up_mem_dev(struct mem_dev* dev, int cnt)
{
int ret;
cdev_init(&dev->cdev, &mem_dev_fops);
dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->cdev, dev->devno, cnt); // 出错返回负值
if (ret < 0)
pr_err("adding mem_dev %d error, errno %d\n", cnt, ret);
return ret;
}
// 模块初始化
static int __init mem_dev_init(void)
{
int ret = 0;
dev_t devno = 0;
// 动态分配设备号,传入的devno参数为0
ret = alloc_chrdev_region(&devno, 0, 1, "my_mem_dev");
if (ret < 0) {
pr_err("alloc_chrdev_region failed, errno %d\n", ret);
return ret;
}
// 分配设备结构体内存并将分配的内存清0
mem_dev = kzalloc(sizeof(struct mem_dev), GFP_KERNEL);
if (NULL == mem_dev) {
ret = -ENOMEM;
pr_err("kzalloc failed, errno %d\n", ret);
goto unreg_chrdev;
}
mem_dev->devno = devno;
ret = bytes_ring_init(&mem_dev->ring, DEFAULT_SIZE);
if (ret < 0) {
pr_err("bytes_ring_init failed, errno %d\n", ret);
goto free_dev;
}
ret = set_up_mem_dev(mem_dev, 1);
if (ret < 0)
goto destroy_ring;
// 创建类和设备,当模块加载后会自动在/dev目录下生成设备节点
mem_dev->mem_dev_class = class_create(THIS_MODULE, "mem_dev_class");
if (IS_ERR(mem_dev->mem_dev_class)) {
ret = PTR_ERR(mem_dev->mem_dev_class);
pr_err("class_create failed, errno %d\n", ret);
goto del_cdev;
}
mem_dev->mem_dev_device = device_create(mem_dev->mem_dev_class,
NULL, devno, NULL, "mem_dev");
if (IS_ERR(mem_dev->mem_dev_device)) {
ret = PTR_ERR(mem_dev->mem_dev_device);
pr_err("device_create failed, errno %d\n", ret);
goto clean_class;
}
/*
// 创建属性文件
ret = device_create_file(mem_dev->mem_dev_device, &dev_attr_mem_dev);
if (ret != 0) {
pr_err("device_create_file failed, errno %d\n", ret);
goto clean_device;
}
*/
// 创建属性组
ret = sysfs_create_group(&mem_dev->mem_dev_device->kobj, &mem_dev_attr_group);
if (ret != 0) {
pr_err("sysfs_create_group failed, errno %d\n", ret);
goto clean_device;
}
mutex_init(&mem_dev->mutex);
init_waitqueue_head(&mem_dev->r_wq);
init_waitqueue_head(&mem_dev->w_wq);
pr_info("mem_dev init OK, major %u, minor %u\n", MAJOR(devno), MINOR(devno));
return 0;
clean_device:
device_destroy(mem_dev->mem_dev_class, devno);
clean_class:
class_destroy(mem_dev->mem_dev_class);
del_cdev:
cdev_del(&mem_dev->cdev);
destroy_ring:
bytes_ring_destroy(&mem_dev->ring);
free_dev:
kfree(mem_dev);
mem_dev = NULL;
unreg_chrdev:
unregister_chrdev_region(devno, 1);
return ret;
}
// 模块注销
static void __exit mem_dev_exit(void)
{
sysfs_remove_group(&mem_dev->mem_dev_device->kobj, &mem_dev_attr_group);
//device_remove_file(mem_dev->mem_dev_device, &dev_attr_mem_dev);
device_destroy(mem_dev->mem_dev_class, mem_dev->devno);
class_destroy(mem_dev->mem_dev_class);
cdev_del(&mem_dev->cdev);
// 注销动态分配的设备号
unregister_chrdev_region(mem_dev->devno, 1);
bytes_ring_destroy(&mem_dev->ring);
kfree(mem_dev);
mem_dev = NULL;
pr_info("mem_dev module exit\n");
}
module_init(mem_dev_init);
module_exit(mem_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyang.plus@foxmail.com");
MODULE_VERSION("v1.00");
# ==============================Makefile================================
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
obj-m += mem_dev.o
all:
$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURDIR) modules
clean:
$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURDIR) modules clean
rm -rf modules.order
5.测试程序源码
/*===========================mem_dev_test.c================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEFAULT_PATH_LEN (100)
#define DEFAULT_PATH "/dev/mem_dev"
#define DEFAULT_WRITE_LEN (100)
#define DEFAULT_READ_LEN (100)
#define DEFAULT_TEST_CNT (100)
struct argument
{
int write_len;
int read_len;
int test_cnt;
char path[DEFAULT_PATH_LEN];
};
static void default_argument(struct argument* arg)
{
arg->write_len = DEFAULT_WRITE_LEN;
arg->read_len = DEFAULT_READ_LEN;
arg->test_cnt = DEFAULT_TEST_CNT;
strcpy(arg->path, DEFAULT_PATH);
}
#define OPT_STRING "w:r:n:p:" // 选项字符串
extern char* optarg; // 指向参数值
extern int optind; // 记录getopt函数处理argv[]数组的位置,一般不需要设置
extern int opterr; // 保存出错信息
extern int optopt;
static void parse_option(int argc, char* argv[], struct argument* arg)
{
int opt, val;
opterr = 0; // 关闭getopt输出的错误信息
while (-1 != (opt = getopt(argc, argv, OPT_STRING))) {
switch (opt) {
case 'w': // 每次写入的数据长度
val = atoi(optarg);
if (val < 0) {
printf("parse_option: input -w option error\n");
exit(1);
}
arg->write_len = val;
break;
case 'r': // 每次读取的数据长度
val = atoi(optarg);
if (val < 0) {
printf("parse_option: input -r option error\n");
exit(1);
}
arg->read_len = val;
break;
case 'n': // 测试次数
val = atoi(optarg);
if (val <= 0) {
printf("parse_option: input -n option error\n");
exit(1);
}
arg->test_cnt = val;
break;
case 'p': // 打开设备的路径
val = strlen(optarg);
if ((val <= 0) || (val >= DEFAULT_PATH_LEN)) {
printf("parse_option: input -p option error\n");
exit(1);
}
strcpy(arg->path, optarg);
break;
case ':':
case '?':
default:
printf("parse_option: input option error\n");
exit(1);
break;
}
}
if ((0 == arg->write_len) && (0 == arg->read_len)) {
printf("parse_option: input -w and -r option error\n");
exit(1);
}
}
// 获取随机数
static void get_random_data(char* buf, int size)
{
int i;
srand((unsigned)time(NULL)); // 初始化随机数种子
for (i = 0; i < size; i++) {
buf[i] = rand() % (0xFF + 1); // 产生0-0xFF的十六进制的随机数
}
}
int test(struct argument* arg)
{
char* buf = NULL;
int cnt = arg->test_cnt;
int ret = 0, fd = 0;
int len = (arg->write_len > arg->read_len) ? arg->write_len : arg->read_len;
fd = open(arg->path, O_RDWR);
if (fd < 0) {
perror("open failed");
ret = fd;
goto out;
}
buf = (char*)malloc(2 * len);
if (NULL == buf) {
perror("malloc buf failed");
ret = -1;
goto fd_close;
}
while (cnt--) {
if (arg->write_len > 0) {
get_random_data(buf, arg->write_len);
ret = write(fd, buf, arg->write_len);
if (ret < 0) {
perror("write error");
break;
}
}
if (arg->read_len > 0) {
ret = read(fd, &buf[arg->write_len], arg->read_len);
if (ret < 0) {
perror("read error");
break;
}
}
if (arg->read_len == arg->write_len) {
ret = memcmp(buf, &buf[arg->write_len], arg->write_len);
if (ret != 0)
break;
}
memset(buf, 0, 2 * len);
usleep(1000 * 200);
}
free(buf);
buf = NULL;
fd_close:
close(fd);
out:
return ret;
}
int main(int argc, char* argv[])
{
struct argument arg;
default_argument(&arg);
parse_option(argc, argv, &arg);
return test(&arg);
}
# ===========================mem_dev_test.sh================================
#!/bin/bash
test_cnt=10
write_len=("56" "127" "287" "365" "467" "563")
read_len=("56" "127" "287" "365" "467" "563")
path="/dev/mem_dev"
echo "test count: $test_cnt"
echo "write length: ${write_len[*]}"
echo "read length: ${read_len[*]}"
echo "device path $path"
for i in ${write_len[@]}
do
./mem_dev_test -w $i -r $i -n $test_cnt -p $path
# $?获取上一条命令的执行状态
if [ $? -eq 0 ]; then
echo "read_len $i, write_len $i test passed"
else
echo "read_len $i, write_len $i test FAILED"
fi
sleep 1
done