当前位置: 首页 > news >正文

驱动:字符设备驱动注册、读写实操

准备基础的文件

Makefile

#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
# KERN_VER = $(shell uname -r)
# KERN_DIR = /lib/modules/$(KERN_VER)/build	# 开发板的linux内核的源码树目录
KERN_DIR = /home/aston/workspace/driver/kernelobj-m	+= module_test.o #要将module_test.c文件编译成一个模块	all:make -C $(KERN_DIR) M=`pwd` modules cp:cp *.ko /home/aston/workspace/nfsdir.PHONY: clean	
clean:make -C $(KERN_DIR) M=`pwd` modules clean

module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit// 模块安装函数
static int __init chrdev_init(void)
{	printk(KERN_INFO "chrdev_init helloworld init\n");return 0;
}// 模块下载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

添加注册

module_test.c文件当中添加file_operations结构体实体

// 定义file_operations结构体实例
static const  struct file_operations my_fops = {.owner = THIS_MODULE,//全部一样,都这样写.open = my_open,.release = my_release,
};

补全需要注册的函数

// 打开设备函数
static int my_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device opened\n");return 0;
}// 释放设备函数
static int my_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device released\n");return 0;
}

在chrdev_init里面调用register_chrdev注册

	int ret = 0;//注册字符设备驱动ret = register_chrdev(MYMAJOR, MYNAME, &my_fops);if(ret){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "chrdev_init helloworld successs...\n");

修改后的module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>#define MYMAJOR 200
#define MYNAME "mychartest"// 打开设备函数
static int my_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device opened\n");return 0;
}// 释放设备函数
static int my_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device released\n");return 0;
}// 定义file_operations结构体实例
static const  struct file_operations my_fops = {.owner = THIS_MODULE,//全部一样,都这样写.open = my_open,.release = my_release,};// 模块安装函数
static int __init chrdev_init(void)
{	int ret = 0;printk(KERN_INFO "chrdev_init helloworld init\n");//注册字符设备驱动ret = register_chrdev(MYMAJOR, MYNAME, &my_fops);if(ret){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "chrdev_init helloworld successs...\n");return 0;
}// 模块下载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

编译后安装测试

执行make 进行编译
在这里插入图片描述
将文件cp到nfs目录
在这里插入图片描述
设备里面进行安装测试,查看日志,检测是否安装正确
在这里插入图片描述

注销测试

在chrdev_exit 注销字符设备驱动

static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");//注销字符设备驱动unregister_chrdev(MYMAJOR, MYNAME);
}

然后重新编译安装
在这里插入图片描述
先进行了卸载,但是安装失败
查看cat /proc/devices,发现还是存在

cat /proc/devices

在这里插入图片描述
设备重启,就恢复正常,因为我们进行了注册没有进行注销,所有释放是失败的
重启设备后,200没有了
在这里插入图片描述

重新安装,重新卸载就可以了

在这里插入图片描述

内核自动分配主设备号


int mymajor;//全局变量static int __init chrdev_init(void)
{	printk(KERN_INFO "chrdev_init helloworld init\n");//注册字符设备驱动//将第一个参数填写0 成功:返回主设备号 失败:返回负数mymajor = register_chrdev(0, MYNAME, &my_fops);if(mymajor < 0){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "chrdev_init helloworld successs... mymajor = %d\n",mymajor);return 0;
}

在这里插入图片描述

应用程序调用驱动

驱动设备文件的创建

使用ls -l命令查看设备文件的设备号

ls -l /dev/ttyS0

使用 mknod 创建设备文件

mknod /dev/设备名 [c|b] 主设备号 次设备号

在这里插入图片描述

编写应用程序调用驱动

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FILE "/dev/mychartest"int main(void)
{int fd = -1;fd = open(FILE, O_RDWR);if(fd < 0){printf("open %s error.\n", FILE);return -1;}printf("open %s seccess...]n", FILE);close(fd);return 0;
}

在这里插入图片描述
在这里插入图片描述

添加读写接口

复习一下
先安装驱动

insmod module_test.ko

查看

cat /proc/devices

再创建mknod 创建设备文件

mknod /dev/mychartest c 250 0

最后应用层调用

apptest.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FILE "/dev/mychartest"int main(void)
{int fd = -1;int i = 1000;char buf[100];fd = open(FILE, O_RDWR);if(fd < 0){printf("open %s error.\n", FILE);return -1;}printf("open %s seccess...]\n", FILE);// 写入数据ssize_t written = write(fd, "123456", 6);if (written != 6) {perror("write failed");close(fd);return -1;}lseek(fd, 0, SEEK_SET);ssize_t read_bytes = read(fd, buf, sizeof(buf) - 1);  // 预留1字节给终止符if (read_bytes < 0) {   perror("read failed");close(fd);return -1;}buf[read_bytes] = '\0';  // 手动添加字符串终止符printf("buf: %s\n", buf);while(i--)close(fd);return 0;
}

module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <linux/uaccess.h>  // copy_to_user, copy_from_user
#include <linux/kernel.h>#define BUFFER_SIZE 100
#define MYMAJOR 200
#define MYNAME "mychartest"int mymajor;
static char buffer[BUFFER_SIZE];// 打开设备函数
static int my_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device opened\n");return 0;
}// 释放设备函数
static int my_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device released\n");return 0;
}// 读取设备函数
static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {printk(KERN_INFO "my_read\n");size_t bytes_to_read = min(count, (size_t)(BUFFER_SIZE - *f_pos));if (copy_to_user(buf, buffer + *f_pos, bytes_to_read)) {return -EFAULT;}*f_pos += bytes_to_read;return bytes_to_read;
}// 写入设备函数
static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {printk(KERN_INFO "my_write\n");size_t bytes_to_write = min(count, (size_t)(BUFFER_SIZE - *f_pos));if (copy_from_user(buffer + *f_pos, buf, bytes_to_write)) {return -EFAULT;}*f_pos += bytes_to_write;return bytes_to_write;
}// 定义file_operations结构体实例
static const  struct file_operations my_fops = {.owner = THIS_MODULE,//全部一样,都这样写.open = my_open,.release = my_release,.write = my_write,.read = my_read,};// 模块安装函数
static int __init chrdev_init(void)
{	printk(KERN_INFO "chrdev_init helloworld init\n");//注册字符设备驱动//将第一个参数填写0 成功:返回主设备号 失败:返回负数mymajor = register_chrdev(0, MYNAME, &my_fops);if(mymajor < 0){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "chrdev_init helloworld successs... mymajor = %d\n",mymajor);return 0;
}// 模块下载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");//注销字符设备驱动unregister_chrdev(MYMAJOR, MYNAME);
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

copy_from_user 和 copy_to_user 是 Linux 内核编程中用于在内核空间和用户空间之间安全传输数据的关键函数

copy_from_user

unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);

作用:从用户空间(from)复制数据到内核空间(to)。
参数:
to:内核空间目标地址(指针)。
from:用户空间源地址(需用 __user 标记)。
n:要复制的字节数。
返回值:
0:成功复制全部数据。
非零值:未成功复制的字节数(表示部分或全部复制失败)。

copy_to_user

作用:从内核空间(from)复制数据到用户空间(to)。
参数:
to:用户空间目标地址(需用 __user 标记)。
from:内核空间源地址(指针)。
n:要复制的字节数。
返回值:
0:成功复制全部数据。
非零值:未成功复制的字节数。

为什么需要专用函数?

内核与用户空间隔离:
内核空间和用户空间有独立的内存地址空间,不能直接通过指针访问。
用户空间指针可能无效(如空指针、已释放的内存),直接访问会导致内核崩溃。
安全机制:
copy_*_user 会检查用户空间指针的有效性,并在访问前将进程切换到用户模式。
若用户空间指针无效,函数会返回错误而非崩溃。

相关文章:

  • [Harmony]颜色初始化
  • [Harmony]网络状态监听
  • 5.29-6.4解决问题归纳
  • ‘pnpm‘ 不是内部或外部命令,也不是可运行的程序
  • Linux系统iptables防火墙实验拓补
  • 亚马逊站内信规则2025年重大更新:避坑指南与合规策略
  • 制造业数智化:R²AIN SUITE 如何打通提效闭环
  • 使用 useSearchParams 的一个没有触发控制台报错的错误用法
  • 某校体育场馆结构自动化监测
  • LeetCode 2297. 跳跃游戏 VIII(中等)
  • 【电赛培训课程】电子设计竞赛工程基础知识
  • Git常用命令完全指南:从入门到精通
  • 【Redis实战:缓存与消息队列的应用】
  • 30 C 语言递归算法详解:基准条件、递归逻辑、循环对比、经典案例(斐波那契、猴子吃桃、汉诺塔、二分查找等)
  • 防火墙iptables项目实战
  • golang常用库之-go-feature-flag库(特性开关(Feature Flags))
  • 关于面试找工作的总结(四)
  • Linux容器篇、第一章_02Rocky9.5 系统下 Docker 的持久化操作与 Dockerfile 指令详解
  • 电子电路:共集电极放大器原理与作用解析
  • MySQL JSON 查询中的对象与数组技巧
  • wordpress通过微信投稿/如何推广seo
  • 网站没有在工信部备案/seo网站建站
  • 博彩网站怎么做代理/在线网页生成器
  • 网站制作售后/注册域名费用一般多少钱
  • 温州网站建站/国内做网站比较好的公司
  • 长沙建站模板大全/线上推广是什么意思