字符设备驱动开发实验

news/2024/10/31 5:34:09/

我们以 chadev 这个虚拟设备为 例,完整的编写一个字符设备驱动模块。chadev 不是实际存在的一个设备,是为了方 便讲解字符设备的开发而引入的一个虚拟设备设备有两个缓冲区,一个为读缓冲 区,一个为写缓冲区,这两个缓冲区的大小都为 100 字节。在应用程序中可以向 chadev 设 备的写缓冲区中写入数据,从读缓冲区中读取数据。

1、编写实验程序

驱动程序chardevirs.c

#include <linux/module.h> //所有模块都需要的头文件
#include <linux/init.h> // init&exit 相关宏
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/fs.h>#define  CHRDEVBASE_MAJOR    200   //主设备号
#define  CHRDEVBASE_NAME     "chadev"  //驱动名字static char readbuf[100];  //读缓存
static char writebuf[100];  //写缓存
static char kerneldata[] = {"kernel data!"};static int  char_open(struct inode *inode, struct file *file)
{// printk("chardevbase_open\n");return 0;
}static ssize_t  char_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{     int ret = 0;  // printk("chardevbase_read\n");memcpy(readbuf,kerneldata,sizeof(kerneldata));//向用户空间发送数据 ret = copy_to_user(buf,readbuf,count);if(ret == 0){}else{}return 0;}static int  char_release(struct inode *inode, struct file *file)
{//printk("chardevbase_release\n");return 0;
}static ssize_t   char_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{int ret = 0;//printk("chardevbase_write\n");/* 接收用户空间传递给内核的数据并且打印出来 */ret = copy_from_user(writebuf,buf,count);if(ret ==0){printk("kernel recevdata:%s\n",writebuf);  }else{}return 0;}//设备操作函数结构体
static  struct   file_operations   chrdebase_fops = {.owner      = THIS_MODULE,.open       = char_open,.release    = char_release,.read       = char_read,.write      = char_write,
};//驱动入口函数
static int __init  hello_init (void)
{       int  ret;printk("Hello module init\n");ret = register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chrdebase_fops);  //注册字符设备驱动if(ret < 0){printk("chardevbase init failed\n");}return 0;
}
//驱动出口函数
static void __exit hello_exit (void)
{printk("Hello module exit\n");unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);  //注销字符设备驱动
}module_init(hello_init);
module_exit(hello_exit);//LICENSE 和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hsj");

char_open 函数,当应用程序调用 open 函数的时候此函数就会调用, 本例程中我们没有做任何工作,只是输出一串字符,用于调试。这里使用了 printk 来输出信息, 而不是 printf!因为在 Linux 内核中没有 printf 这个函数。printk 相当于 printf 的孪生兄妹printf 运行在用户态,printk 运行在内核态。在内核中想要向控制台输出或显示一些内容,必须使用 printk 这个函数。

char_read 函数,应用程序调用 read 函数从设备中读取数据的时候此函 数会执行。参数 buf 是用户空间的内存,读取到的数据存储在 buf 中,参数 cnt 是要读取的字节 数,参数 offt 是相对于文件首地址的偏移。kerneldata 里面保存着用户空间要读取的数据,先将 kerneldata 数组中的数据拷贝到读缓冲区 readbuf 中,通过函数 copy_to_user 将 readbuf 中的数据复制到参数 buf 中。因为内核空间不能直接操作用户空间的内存,因此需要借 助 copy_to_user 函数来完成内核空间的数据到用户空间的复制

static inline long copy_to_user(void __user *to, const void *from, unsigned long n)

参数 to 表示目的,参数 from 表示源,参数 n 表示要复制的数据长度。如果复制成功,返 回值为 0,如果复制失败则返回负数。

char_write 函数,应用程序调用 write 函数向设备写数据的时候此函数 就会执行。参数 buf 就是应用程序要写入设备的数据,也是用户空间的内存,参数 cnt 是要写入 的数据长度,参数 offt 是相对文件首地址的偏移。通过函数 copy_from_user 将 buf 中的 数据复制到写缓冲区 writebuf 中,因为用户空间内存不能直接访问内核空间的内存,所以需要 借助函数 copy_from_user 将用户空间的数据复制到 writebuf 这个内核空间中。

2、编写测试 APP

应用程序charapp.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main(int argc,char *argv[])
{   char  buf[100];char  wbuf[100];char  writebuf[] = "hello kernel";int   ret;int   fd;char *filename;if(argc != 3){printf("Error usage!\r\n");return -1;}filename = argv[1];fd = open(filename,O_RDWR);if(fd < 0){         printf("open failed\n");return -1;}if(atoi(argv[2])==1);  //读{ ret = read(fd, buf, sizeof(buf));if(ret < 0){printf("read failed\n");return -1;}else {printf("APP read data: %s\r\n",buf);}}if(atoi(argv[2]) == 2)   //写{  memcpy(wbuf,writebuf,sizeof(writebuf));ret =  write(fd, wbuf, sizeof(writebuf));if(ret < 0){printf("write failed\n");return -1;}else{}}close(fd); return 0;   
}

判断运行测试 APP 的时候输入的参数是不是为 3 个,main 函数的 argc 参数表示 参数数量,argv[]保存着具体的参数,如果参数不为 3 个的话就表示测试 APP 用法错误。比如, 现在要从 chadev设备中读取数据,需要输入如下命令:

./app /dev/chadev 1

上述命令一共有三个参数“./chrdevbaseApp”、“/dev/chadev”和“1”,这三个参数分别 对应 argv[0]、argv[1]和 argv[2]。第一个参数表示运行 APP 这个软件,第二个参数表 示测试APP要打开/dev/chadev这个设备。第三个参数就是要执行的操作,1表示从chadev 中读取数据,2 表示向 chadev 写数据。

3、运行测试

加载驱动模块

1.输入如下命令加载 chardevirs.ko 驱动文件:

insmod chardevirs.ko 或 modprobe chardevirs.ko

2.输入如下命令查看当前 系统中有没有 chadev 这个设备:

cat /proc/devices

 3.创建设备节点文件

驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操 作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chadev 这个设备节 点文件

mknod /dev/chadev c 200 0

其中“mknod”是创建节点命令,“/dev/chadev”是要创建的节点文件,“c”表示这是个 字符设备,“200”是设备的主设备号,“0”是设备的次设备号。创建完成以后就会存在 /dev/chadev 这个文件,可以使用“ls /dev/chadev -l”命令查看

 

4.chadev设备操作测试

 

 

5.卸载驱动模块

如果不再使用某个设备的话可以将其驱动卸载掉,比如输入如下命令卸载掉
chardevirs 这 个设备

 rmmod chardevirs.ko

卸载以后使用 lsmod 命令查看 chardevirs 这个模块还存不存在

 此时系统已经没有任何模块了,chardevirs 这个模块也不存在了, 说明模块卸载成功。至此,chrdevbase 这个设备的整个驱动就验证完成了,驱动工作正常


http://www.ppmy.cn/news/62794.html

相关文章

(六)【平衡小车制作】位置式PID、直立环与速度环编程

本篇文章我将针对位置式PID算法、直立环、速度环等的编程进行详细的讲解&#xff0c;让每位小伙伴能够对这三个概念的编程逻辑有更加清晰的理解。 一、直立环&#xff08;PD控制器&#xff09; 1.中文公式  直立环输出Kp1角度偏差Kd角度偏差的微分  // 角度偏差真实角度-期…

JMeter开发web及手机APP自动化脚本练习

&#xff08;一&#xff09;开发web自动化脚本练习 一、打开浏览器代理服务器设置 我这里用的是360浏览器&#xff0c;打开浏览器代理服务器设置&#xff0c;端口要与jmeter中的端口设置保持一致哦。 二、JMeter设置代理 JMeter设置代理&#xff08;jmeter中的端口要与360浏览…

【设计模式】抽象工厂模式

【设计模式】抽象工厂模式 参考资料&#xff1a; Java设计模式 - 抽象工厂模式 重学 Java 设计模式&#xff1a;实战抽象工厂模式 文章目录 【设计模式】抽象工厂模式一、抽象工厂模式介绍1.1、什么是工厂方法模式1.2、角色概述 二、案例场景模拟2.1、背景一&#xff1a;Redis…

如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)

如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows) 如何制作 ESXi USB 启动盘 请访问原文链接&#xff1a;https://sysin.org/blog/create-bootable-esxi-usb-installer/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysi…

【SpringCloud】初始微服务

目录 一、单体架构 1、概念 2、优点 3、缺点 二、分布式架构 1、概念 2、优点 3、缺点 三、微服务 1、概念 2、优点 3、缺点 四、微服务技术对比 五、SpringCloud 六、服务拆分 1、注意事项 2、服务远程调用 一、单体架构 1、概念 业务的所有功能都集中到一个…

Triloga 的任务 — Satta 系列来袭!

谁战胜了这些凶兽&#xff0c;谁就获得了力量&#xff0c;让我们通过装备体现出来&#xff01;来自神秘洞穴的疯狂昆虫外壳&#xff0c;黄昏之地燃烧中的部落的骨质盔甲&#xff0c;以及深海的美妙灯光。 Triloga 的任务——Satta 系列已在 The Sandbox 市场平台上架&#xff1…

【Java-05】常用API、正则表达式、Collection集合

主要内容 BigInteger类BigDecimal类Arrays类包装类String类的常用方法正则表达式Collection集合 1 BigInteger类 1.1 概述 概述 : java.math.BigInteger类是一个引用数据类型 , 可以用于计算一些大的整数 , 当超出基本数据类型数据范围的整数运算时就可以使用BigInteger了。…

java中枚举类型使用详解

在Java中&#xff0c;枚举类型&#xff08;Enumeration&#xff09;是一种特殊的数据类型&#xff0c;它允许程序员定义一个有限的、可枚举的数据集。枚举类型的定义通常在类的内部&#xff0c;在类的外部使用。 以下是一些关于Java中枚举类型使用的详细说明&#xff1a; 定义…