Johannes 《Linux内核模块与设备驱动开发:编写Linux驱动程序》 (11)kmalloc 动态内存分配和struct file中的私有数据

复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>

static dev_t dev_nr;
static struct cdev my_cdev;
static struct class *my_class;

#define MEMSIZE 64

static int my_open(struct inode *inode, struct file *filp)
{
	filp->private_data = kmalloc(MEMSIZE, GFP_KERNEL);

	if (!filp->private_data) {
		pr_err("hello_cdev - Out of memory\n");
		return -ENOMEM;
	}

	return 0;
}

static int my_release(struct inode *inode, struct file *filp)
{
	kfree(filp->private_data);
	return 0;
}

static ssize_t my_read(struct file *filp, char __user *user_buf, size_t len, loff_t *off)
{
	char *text = filp->private_data;
	int not_copied, delta, to_copy = (len + *off) < MEMSIZE ? len : (MEMSIZE - *off);

	pr_info("hello_cdev - Read is called, we want to read %ld bytes, but actually only copying %d bytes. The offset is %lld\n", len, to_copy, *off);

	if (*off >= MEMSIZE)
		return 0;

	not_copied = copy_to_user(user_buf, &text[*off], to_copy);
	delta = to_copy - not_copied;
	if (not_copied) 
		pr_warn("hello_cdev - Could only copy %d bytes\n", delta);

	*off += delta;

	return delta;
}

static ssize_t my_write(struct file *filp, const char __user *user_buf, size_t len, loff_t *off)
{
	char *text = filp->private_data;
	int not_copied, delta, to_copy = (len + *off) < MEMSIZE ? len : (MEMSIZE - *off);

	pr_info("hello_cdev - Write is called, we want to write %ld bytes, but actually only copying %d bytes. The offset is %lld\n", len, to_copy, *off);

	if (*off >= MEMSIZE)
		return 0;

	not_copied = copy_from_user(&text[*off], user_buf, to_copy);
	delta = to_copy - not_copied;
	if (not_copied) 
		pr_warn("hello_cdev - Could only copy %d bytes\n", delta);

	*off += delta;
	return delta;
}

static struct file_operations fops = {
	.read = my_read,
	.write = my_write,
	.open = my_open,
	.release = my_release,
	.llseek = default_llseek,
};

static int __init my_init(void)
{
	int status;
#ifdef STATIC_DEVNR
	dev_nr = STATIC_DEVNR;
	status = register_chrdev_region(dev_nr, MINORMASK + 1, "hello_cdev");
#else
	status = alloc_chrdev_region(&dev_nr, 0, MINORMASK + 1, "hello_cdev");
#endif
	if (status) {
		pr_err("hello_cdev - Error reserving the region of device numbers\n");
		return status;
	}

	cdev_init(&my_cdev, &fops);
	my_cdev.owner = THIS_MODULE;

	status = cdev_add(&my_cdev, dev_nr, MINORMASK + 1);
	if (status) {
		pr_err("hello_cdev - Error adding cdev\n");
		goto free_devnr;
	}

	pr_info("hello_cdev - Registered a character device for Major %d starting with Minor %d\n", MAJOR(dev_nr), MINOR(dev_nr));

	my_class = class_create("my_class");
	if (!my_class) {
		pr_err("hello_cdev - Could not create class my_class\n");
		status = ENOMEM;
		goto delete_cdev;
	}

	if (!device_create(my_class, NULL, dev_nr, NULL, "hello%d", 0)) {
		pr_err("hello_cdev - Could not create device hello0\n");
		status = ENOMEM;
		goto delete_class;
	}

	pr_info("hello_cdev - Created device under /sys/class/my_class/hello0\n");

	return 0;

delete_class:
	class_unregister(my_class);
	class_destroy(my_class);
delete_cdev:
	cdev_del(&my_cdev);
free_devnr:
	unregister_chrdev_region(dev_nr, MINORMASK + 1);
	return status;
}

static void __exit my_exit(void)
{
	device_destroy(my_class, dev_nr);
	class_unregister(my_class);
	class_destroy(my_class);
	cdev_del(&my_cdev);
	unregister_chrdev_region(dev_nr, MINORMASK + 1);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4Linux");
MODULE_DESCRIPTION("A sample driver for manually registering a character device");

和lesson10 auto_devfile 相对应的是其添加修改了open/release/read/write 。

malloc/free 在c 里面很常见的成对出现,用于申请内存空间和释放内存空间,这里的kmalloc/free 成对出现在open/release 里面。要实现这对就要在头文件添加<linux/slab.h>.参数

GFP_KERNEL:常规

GFP_ATOMIC:中断上下文调用,原子操作

GFP_DMA: DMA 申请一段连续空间。

程序只是学习使用,JOHANNES 就直接给GFP_KENREL。

然后read/write 函数里面就是copy_to_user()/user_to_copy()。

不看视频,直接看这个程序,能理解这个程序是注册了一个字符设备文件,打开字符设备文件就会申请下一段64字节(MEMSIZE)内存空间,从外部用户层给内存空间读写数据,就会对这段内存空间进行读写。

视频:https://www.bilibili.com/video/BV18SE26cEjn?spm_id_from=333.788.videopod.sections&vd_source=92b7efa4daa5bb4fdcdc9f2db97c307b&p=11

觉得没太多可说的。就是lesson8/lesson10 综合加上了一段内存空间申请读写。

相关推荐
Android系统攻城狮13 分钟前
Linux PipeWire深度解析之pw_init调用流程与实战(八十三)
linux·运维·服务器·音频进阶·pipewire音频进阶
月华路22 分钟前
G1 GC 对数组与大对象(Humongous)的处理
java·jvm·算法
我想走路带风28 分钟前
LRU和最长前缀和(计算机网络算法)
计算机网络·算法
M78佐菲1 小时前
Linux学习笔记:进程
linux·笔记·学习·算法
zbyyd2 小时前
Linux 进程管理详解:从概念到实战
linux·运维·服务器
徐小夕2 小时前
表格、文档、甘特、大屏、表单一站打通:pxcharts超级表格4.0正式上线!
前端·算法·github
薛定猫AI3 小时前
OpenAI Codex本地完整配置教程|(Windows/Mac/Linux全平台,修复鉴权报错)
linux·windows·macos
TechLee3 小时前
跨语言加解密总对不上?这个纯 Go 神库让 AES/RSA 与 PHP、Java 100% 互通
java·后端·算法
躺不平的理查德4 小时前
OpenSSL 的异步 TLS 1.3 加密
linux·运维·服务器