8、Linux驱动开发:驱动-读写接口实现(readwrite)

目录

🍅点击这里查看所有博文

  随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

  想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

  很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

  同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

  既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

  本系列博客所述资料均来自互联网,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

系统调用

  在前面讲到的基础上,我们继续学习如何实现读写接口。

  在Linux中,应用程序在打开字符设备文件之后。可通过readwrite对字符设备进行读写。应用程序的readwrite通常定义为系统调用函数。

  对于read操作,应用程序通常会调用read()函数来读取数据。该函数的原型如下:

ssize_t read(int fd, void *buf, size_t count);

  其中,fd是文件描述符,buf是用户空间缓冲区指针,count是要读取的字节数。该函数会将数据从文件中读取到缓冲区中,并返回实际读取的字节数。如果读取失败,则会返回-1,并设置相应的错误码。

  对于write操作,应用程序通常会调用write()函数来写入数据。该函数的原型如下:

ssize_t write(int fd, const void *buf, size_t count);

  其中,fd是文件描述符,buf是用户空间缓冲区指针,count是要写入的字节数。该函数会将数据从用户空间缓冲区中写入到文件中,并返回实际写入的字节数。如果写入失败,则会返回-1,并设置相应的错误码。

  除了read()write()函数外,还有其他一些系统调用函数可以用于文件读写操作,例如pread()pwrite()等。这些函数与read()write()函数类似,但提供了更多的选项和控制。

  这些系统调用函数,最终会通过对应驱动的file_operations 结构体调用。这在5.设备-设备注册章节有详细的描述。file_operations结构体如下,本章节的内容就是实现其中的readwrite方法。

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*iterate) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

数据隔离

  Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据。因为Linux使用的虚拟内存机制,用户空间的数据可能被换出。当内核空间使用用户空间指针时,对应的数据可能不在内存中。

  正确的做法是在内核空间开辟自己可以操作的内存,将用户空间的数据拷贝到内核空间之后,内核操作自己的这块内存即可,即使进程意外结束,内核也不会崩溃,这种做法的安全性是要比前者的高的。

  我们在驱动中常用copy_from_usercopy_to_user来完成内核空间和用户空间之间的数据交换。

copy_from_user&write

  函数copy_from_user的定义如下,其作用是将数据从用户空间拷贝到内核空间。

/*
功能:将数据从用户空间拷贝到内核空间 (write)
参数:@to:内核空间的首地址@from:用户空间的首地址@n:大小(单位是字节)
返回值:成功返回0,失败返回未拷贝字节的个数
*/
int copy_from_user(void *to, const void __user volatile *from,unsigned long n);

  对应的操作也就是系统调用的的write接口。用户程序调用write接口时,通过系统调用,驱动模块中会根据文件的fd数据,最后调用到file_operations 中的*write函数指针。

/*
参数:filp:待操作的设备文件file结构体指针buf:待写入所读取数据的用户空间缓冲区指针count:待读取数据字节数f_pos:待读取数据文件位置,写入完成后根据实际写入字节数重新定位
返回:成功实际写入的字节数,失败返回负值
*/
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

输入图片说明

copy_to_user&read

  函数copy_to_user的定义如下,其作用是将数据从内核空间拷贝到用户空间。

/*
功能:将数据从内核空间拷贝到用户空间 (read)
参数:@to:用户空间的首地址@from:内核空间的首地址@n:大小(单位是字节)
返回值:成功返回0,失败返回未拷贝字节的个数     
*/   
int copy_to_user(void __user volatile *to, const void *from, unsigned long n);

  对应的操作也就是系统调用的的read接口。用户程序调用read接口时,通过系统调用,驱动模块中会根据文件的fd数据,最后调用到file_operations 中的*read函数指针。

/*
参数:filp: 待操作的设备文件file结构体指针buf: 待写入所读取数据的用户空间缓冲区指针count:待读取数据字节数f_pos:待读取数据文件位置,读取完成后根据实际读取字节数重新定位__user :是一个空的宏,主要用来显示的告诉程序员它修饰的指针变量存放的是用户空间的地址。
返回值:成功实际读取的字节数,失败返回负值
*/   
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

输入图片说明

驱动编写

  节省篇幅,这里的代码是在上一篇文章设计内容的基础上修改的。驱动代码中首先补充file_operationsreadwrite函数。

static struct file_operations hello_ops = 
{.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write,
};

  读取函数的实现就只是读取一个全局变量的内容,该全局变量的默认值为kernel。使用copy_to_user将内核空间的数据拷贝到用户空间的内存中。

#define KMAX_LEN 32
char kbuf[KMAX_LEN + 1] = "kernel";
//read(fd,buff,40);
static ssize_t hello_read (struct file *filep, char __user *buf, size_t size, loff_t *pos)
{int error;if(size > KMAX_LEN)size = KMAX_LEN;if(copy_to_user(buf, kbuf, size)){error = -EFAULT;return error;}return size;
}

  写入函数的实现修改全局变量的内容。使用copy_from_user将用户空间的数据拷贝到内核空间的内存中。

//write(fd,buff,40);
static ssize_t hello_write (struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{int error;if(size > KMAX_LEN)size = KMAX_LEN;memset(kbuf,0,sizeof(kbuf));if(copy_from_user(kbuf, buf, size)){error = -EFAULT;return error;}printk("%s\n",kbuf);return size;
}

实验结果

  编写用户空间的读写程序。打开驱动文件后,首先读取一遍驱动中的默认数据。然后写入数据device write test,最后在读取一遍驱动的数据。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h> //close
void main(void)
{int len = 0;int fd = open("/dev/hellodev",O_RDWR);if(fd < 0) {perror("open fail\n");return;}char buf[64 + 1] = {0};len = read(fd, buf, sizeof(buf) - 1);buf[len] = '\0';printf("read:%s,len = %d\n", buf, len);char buf2[64 + 1] = "device write test";len = write(fd, buf2, strlen(buf2));printf("write ok,len=%d\n", len);len = read(fd, buf, sizeof(buf) - 1);buf[len] = '\0';printf("read:%s,len=%d\n", buf, len);close(fd);return;
}

  从日志中可以看到,读写的数据都是符合预期的。

root@ubuntu:# insmod ./hello.ko 
root@ubuntu:# gcc ./test.c 
root@ubuntu:# ./a.out 
read:kernel,len = 32
write ok,len=17
read:device write test,len=32
root@ubuntu:# dmesg
[236852.445548] hello_init 
[236866.611393] hello_open()
[236866.611482] device write test
[236866.611495] hello_release()

  那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/726545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络协议学习DAY1

1.网络协议模型: OSI协议模型 应用层 实际发送的数据 表示层 发送的数据是否加密 会话层 是否建立会话连接 传输层 数据传输的方式&#xff08;数据报、流式&#xff09; 网…

Seata 2.x 系列【4】产品简介

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot 版本 3.1.0 本系列Seata 版本 2.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 概述2. 发展历史3. 核心术语3.1 TC3.2 TM3.3 RM 4.…

Unity3D 冯高光与布林冯高光经验模型详解

前言 Unity3D是一款非常流行的游戏开发引擎&#xff0c;它为开发者提供了丰富的功能和工具&#xff0c;使得开发游戏变得更加简单和高效。在Unity3D中&#xff0c;冯高光与布林冯高光经验模型是一种常用的光照模型&#xff0c;用来模拟光线在表面上的反射和折射效果。本文将详…

国创证券|存钱有窍门,掌握这五个窍门利息高出不少?

大部分居民会把手中闲置的资金存入银行&#xff0c;享用安稳的收益&#xff0c;其间存在以下五个诀窍&#xff1a; 1、比照不同银行的利率 储户在去银行存钱时&#xff0c;能够比照不同银行的利率&#xff0c;尽量地挑选利率较高的银行存&#xff0c;来获取较多的收益&#x…

MYSQL筛选字段是否为手机号码

1、需要判断导出的手机号正确性 2、观察到数据中包含“1”、“86”、“86”开头的手机号 3、手机号验证规则&#xff0c;1&#xff08;861&#xff0c;861&#xff09;开头的&#xff0c;第二位从3-9的11位数字&#xff08;除去86&#xff0c;86开头&#xff09; 百度到的正…

URL?后参数有特殊字符问题

前端对于URL的参数不做处理 不处理、用URLDecoder.decode()处理、用URLEncoder.encode()处理、用URLEncoder.encode()处理后再用URLDecoder.decode()处理 结果 前端对于URL的参数用encodeURIComponent(‘XF-OPPZZD-26*316’)处理 结果 前端不处理有&字符时 结果会把后…

BUUCTF---[极客大挑战 2019]BabySQL1

1.这道题和之前做的几道题是相似的&#xff0c;这道题考的知识点更多。难度也比之前的大一些 2.尝试万能密码 or 1#发现过滤了or,使用1和1,发现他对单引号也进行了过滤。于是我尝试进行双写绕过&#xff0c;发现可以通过了。 3.由之前的做题经验可知&#xff0c;这道题会涉及到…

【C#语言入门】10. 操作符详解(下)

【C#语言入门】10. 操作符详解&#xff08;下&#xff09; 四、各类操作符的示例 (T)x 强制类型转换符 //ushort的maxvalue是65535 uint x 65536; ushort y (ushort)x; Console.WriteLine(y);//结果为0类型转换 隐式&#xff08;implicit&#xff09;类型转换 不丢失精度的…

CMake的cmake_dependent_option用法

前言 本篇文章讲解CMake的cmake_dependent_option用法 选项和变量 在讲cmake_dependent_option之前&#xff0c;先讲一下CMake的选项和变量 选项 在CMake中&#xff0c;选项可以用来控制构建过程中的一些开关。我们可以使用option()命令来添加一个选项。option的函数原型如…

ChatGPT的安全警告

ChatGPT作为一种强大的人工智能模型&#xff0c;在提供便利的同时&#xff0c;确实存在一些安全隐患&#xff0c;需要引起我们的关注和重视。 首先&#xff0c;ChatGPT可能会提供不准确或虚假的信息。这主要源于其训练数据的多样性和复杂性&#xff0c;以及模型的自身局限性。…

每日汇评:黄金上破2161美元纪录高位,有可能进一步上涨?

周四早间&#xff0c;金价在2150美元的历史高点附近盘整&#xff0c;并成功上破2160历史高位&#xff1b; 美元在美债收益率的压力下逐步走低&#xff0c;市场期待更多鲍威尔讲话和美国就业数据&#xff1b; 日线图上的RSI指标超买状况继续令黄金买家保持谨慎&#xff1b; 金价…

中外联合培养工商管理博士|社科大新加坡社科大学中文授课DBA

中外联合培养工商管理博士|社科大新加坡社科大学中文授课DBA 全球经济正在经历由科技进步和创新、政治和人口剧烈变化所带来的巨大的不确定性和挑战。面对日趋复杂的外部竞争环境&#xff0c;企业的领导者和管理者需要具备卓越的战略思维和全球洞察力、以科学的精神和严谨务实的…

如何在手机上中恢复已删除的照片

市场上有大量用于恢复手机已删除照片的应用程序。您可以尝试任何合法的应用程序来恢复意外删除的视频。其中一些应用程序包括 奇客数据恢复、Disk Drill等。 恢复已删除的 Android 照片 如果您不小心从 Android 设备中删除了任何重要视频&#xff0c;无需惊慌。您可以按照这些…

2024中国(浙江)环保产业与水科技博览会

汇聚科技智慧力量&#xff0c;助力美丽中国建设 浙江省环境服务业高质量发展大会 2024中国&#xff08;浙江&#xff09;环保产业与水科技博览会 China (Zhejiang) environmental protection industry and Water Technology Expo I 环保产业 I 水利科技 I 智慧水务 I 泵管阀…

Java8 CompletableFuture异步编程-入门篇

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 前言 1、Future vs CompletableFuture 1.1 准备工作 1.2 Future 的局限性 …

从0开始学习NEON(2)

1、前言 继上一个例子&#xff0c;本次继续来学习NEON&#xff0c;本次学习NEON中向量拼接的操作&#xff0c;主要应用在图像的padding中。 https://blog.csdn.net/weixin_42108183/article/details/136440707 2、案例 2.1 案例1 在某些情况下&#xff0c;需要取在每个向量…

小程序环形进度条爬坑

在做微信小程序的时候&#xff0c;发现用canvas做的环形进度条&#xff0c;在带滚动条的view里面显示有闪动、显示不全的问题&#xff0c;后面改成echart-weixin的pie图实现了&#xff0c;option配置如下 // 表示进度的百分比 var progressValue 70;option {series: [{type: …

双非二本实习前的准备day8

学习目标&#xff1a; 每天2-3到简单sql&#xff08;刷完即止&#xff09;&#xff0c;每天复习代码随想录上的题目2-3道算法&#xff08;时间充足可以继续&#xff09;&#xff0c;背诵的八股的问题也在这里记录了 今日碎碎念&#xff1a; 1&#xff09;今天任务&#xff1…

指数幂+力扣

题目 题目链接 . - 力扣&#xff08;LeetCode&#xff09; 题目描述 代码实现 class Solution { public:double myPow(double x, int n) {long t n;return t > 0 ? _myPow(x, t) : 1 / _myPow(x, -t);}double _myPow(double x, int n){if(n 0) return 1;double y _…

docker安装与配置-网络方式ftp方式

说明&#xff1a; 本文环境&#xff1a;CentOS 7 1、#ip地址配置(在xnode2的基础上) [rootxnode2 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eno16777736TYPEEthernetBOOTPROTOstaticDEFROUTEyesPEERDNSyesPEERROUTESyesIPV4_FAILURE_FATALnoIPV6INITyesIPV6_AUTOCONF…