Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析

我们在前面讲到了file_operations,其是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL。其中有最重要的几个函数,分别是open()、read()、write()、ioctl(),下面分别对其进行解析

     

一、 打开和关闭设备函数

a -- 打开设备

      int (*open) (struct inode *, struct file *);

在操作设备前必须先调用open函数打开文件,可以干一些需要的初始化操作。当然,如果不实现这个函数的话,驱动会默认设备的打开永远成功。打开成功时open返回0。

b -- 关闭设备      

      int (*release) (struct inode *, struct file *);

当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为NULL。关闭设备永远成功。

这两个函数已经讲过,这里不再赘述,主要看下面几个函数


二、read()、write() 函数

        现在把 read()、write() 两个函数放一起讲,因为两个函数非密不可分的,先看一下两个函数的定义 

a -- read() 函数

   函数原型         ssize_t (*read) (struct file * filp, char __user * buffer, size_t    size , loff_t * p); 
   参数含义

 filp       :为进行读取信息的目标文件,

 buffer  :为对应放置信息的缓冲区(即用户空间内存地址);

 size     :为要读取的信息长度;

 p          :为读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,

                 移动的值为要读取信息的长度值

b -- write() 函数

   函数原型         ssize_t (*write) (struct file * filp, const char __user *   buffer, size_t count, loff_t * ppos); 
   参数含义

filp      :为目标文件结构体指针;

buffer :为要写入文件的信息缓冲区;

count  :为要写入信息的长度;

ppos   :为当前的偏移位置,这个值通常是用来判断写文件是否越界

         

       两个函数的作用分别是 从设备中获取数据发送数据给设备,应用程序中与之对应的也有 write() 函数及 read() 函数:

    len = read(fd,buf,len)

    len = write(fd,buf,size)

static ssize_t hello_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)

static ssize_t hello_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)



       我们知道,应用程序工作在用户空间,而驱动工作在内核空间,二者不能直接通信的,那我们用何种方法进行通信呢?下面介绍一下内核中的memcpy---copy_from_user和copy_to_user虽然说内核中不能使用C库提供的函数,但是内核也有一个memcpy的函数,用法跟C库中的一样。

        下面看一下copy_from_user() 及 copy_to_user() 函数的定义:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline int copy_from_user(void *to, const void __user volatile *from,  
  2.                  unsigned long n)  
  3. {  
  4.     __chk_user_ptr(from, n);  
  5.     volatile_memcpy(to, from, n);  
  6.     return 0;  
  7. }  
  8.   
  9. static inline int copy_to_user(void __user volatile *to, const void *from,  
  10.                    unsigned long n)  
  11. {  
  12.     __chk_user_ptr(to, n);  
  13.     volatile_memcpy(to, from, n);  
  14.     return 0;  
  15. }  
可以看到两个函数均是调用了 _memcpy() 函数

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void volatile_memcpy(volatile char *to, const volatile char *from,   
  2.                 unsigned long n)  
  3. {  
  4.     while (n--)  
  5.         *(to++) = *(from++);  
  6. }  

       其实在这里,我们可以思考,既然拷贝的功能上面的_memcpy() 函数就可以实现,为什么还要封装成 copy_to_user()和copy_from_user()呢?答案是_memcpy() 函数是有缺陷的,譬如我们在用户层调用函数时传入的不是字符串,而是一个不能访问或修改的地址,那样就会造成系统崩溃

      出于上面的原因, 内核和用户态之间交互的数据时必须要先对数据进行检测 ,如果数据是安全的,才可以进行数据交互。上面的函数就是memcpy的改进版,在memcpy功能的基础上加上的检查传入参数的功能,防止有些人有意或者无意的传入无效的参数。

     

      现在我们可以审视一下这两个函数了:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)  
  2.   
  3. static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)  
用法:

和memcpy的参数一样,但它根据传参方向的不同分开了两个函数。

"to"是相对于内核态来说的。所以,to函数的意思是从from指针指向的数据将n个字节的数据传到to指针指向的数据。

"from"也是相对于内核来说的。所以,from函数的意思是从from指针指向的数据将n个字节的数据传到to指针指向的数据

返回值:函数的返回值是指定要读取的n个字节中还剩下多少字节还没有被拷贝。

注意:

一般的,如果返回值不为0时,调用copy_to_user的函数会返回错误号-EFAULT表示操作出错。当然也可以自己决定。


又到了摆实例的时候了,这里只列出部分代码,看看这两个函数的用法:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static ssize_t hello_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)  
  2. {  
  3.     if(len>64)  
  4.     {  
  5.         len =64;  
  6.     }  
  7.     if(copy_to_user(buf,temp,len))  
  8.     {  
  9.         return -EFAULT;  
  10.     }     
  11.     return len;  
  12. }  
  13. static ssize_t hello_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)  
  14. {  
  15.     if(len>64)  
  16.     {  
  17.         len = 64;  
  18.     }  
  19.   
  20.     if(copy_from_user(temp,buf,len))  
  21.     {  
  22.         return -EFAULT;  
  23.     }  
  24.     printk("write %s\n",temp);  
  25.     return len;  
  26. }  
测试程序:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. char buf[]="111232342342342";  
  8. char temp[64]={0};  
  9. main()  
  10. {  
  11.     int fd,len;  
  12.   
  13.     fd = open("/dev/hello",O_RDWR);  
  14.     if(fd<0)  
  15.     {  
  16.         perror("open fail \n");  
  17.         return ;  
  18.     }  
  19.   
  20.     write(fd,buf,strlen(buf));  
  21.   
  22.     len=read(fd,temp,sizeof(temp));  
  23.   
  24.     printf("len=%d,%s \n",len,temp);  
  25.   
  26.     close(fd);  
  27. }  


      到这里open、close、read、write四个函数已经学完,下面我们来看一下四个函数使用时,到底经历了一个怎样的过程:

注:箭头方向是从调用的一方指向受作用的一方

    

    

   

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

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

相关文章

机电传动控制第一周学习笔记

机电传动控制第一周学习笔记&#xff1a; 1 这一周主要讲述了概论和机电传动控制系统动力学基础两个章节内容。 2 绪论中说明了《机电传动控制》课程主要内容为下图所示&#xff1a; 3机电传动控制系统动力学基础章节主要内容分为&#xff1a; &#xff08;1&#xff09;a&…

opengl 配置

OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的专业图形程序接口。 OpenGL是SGI公司开发的一套计算机图形处理系统&#xff0c;是图形硬件的软件接口&#xff0c;任何一个OpenGL应用程序无须考虑其运行环境所在平台与操作系统&#xff0c;在任何一个遵循OpenG…

Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动

编写驱动的第一步仍是看原理图&#xff1a; 可以看到&#xff0c;该蜂鸣器由 GPD0_0 来控制 &#xff0c;查手册可知该I/O口由Time0 来控制&#xff0c;找到相应的寄存器&#xff1a; a -- I/O口寄存器及地址 GPD0CON 0x114000a0 b -- Time0 寄存器及地址 基地址为&#xff1a…

专访:混合云的发展趋势

近日&#xff0c;业界享有盛誉的vForum2013大会在京召开&#xff0c;此次大会云集了近百家国内外知名的云计算、数据存储、大数据及信息安全厂商&#xff0c;共同讨论了虚拟化、云计算及未来IT模式的发展趋势。笔者也有幸在大会期间采访到了VMware 大中华区技术总监张振伦先生&…

Tomcat7性能优化

用了很久的Tomcat&#xff0c;没怎么看过它的优化&#xff0c;今天抽出时间研究了下&#xff0c;将内容记录下。 首先&#xff0c;是客户端访问tomcat的一个过程&#xff0c;如图所示&#xff1a; 图中间虚线框部分是 Apache基金下的服务器来做静态资源处理的&#xff0c;而这部…

Fast Image Cache – iOS 应用程序高性能图片缓存

Fast Image Cache 是一种在 iOS 应用程序中高效、持续、超快速的存储和检索图像的解决方案。任何良好的 iOS 应用程序的用户体验都应该是快速&#xff0c;平滑滚动的&#xff0c;Fast Image Cache 提供图像高速缓存有助于使这更容易实现。 对于图片丰富的应用程序&#xff0c;图…

Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析

解析完 open、close、read、write 四个函数后&#xff0c;终于到我们的 ioctl() 函数了 一、 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速…

android自动化框架简要剖析(一):运行原理+基本框架

android自动化测试原理&#xff1a; 1、将测试apk和被测试apk&#xff0c;运行在一个进程中&#xff1b;通过instrumentation进行线程间的通信 2、通过android.test.AndroidTestCase及其子类&#xff0c;控制android系统对象 3、通过android.test.InstrumentationTestCase 及其…

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

现在&#xff0c;我们来编写自己第一个字符设备驱动 —— 点亮LED。&#xff08;不完善&#xff0c;后面再完善&#xff09; 硬件平台&#xff1a;Exynos4412&#xff08;FS4412&#xff09; 编写驱动分下面几步&#xff1a; a -- 查看原理图、数据手册&#xff0c;了解设备的操…

Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 &#xff08;三&#xff09;—— 字符设备驱动结构&#xff08;中&#xff09; &#xff0c;我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} &#xff0c;下面来介绍另一个比较重要数据结构 struct _file_operatio…

Android开发群

为什么80%的码农都做不了架构师&#xff1f;>>> 我的自建Android应用开发群&#xff0c;欢迎大家来聊聊呀&#xff01;201427584 转载于:https://my.oschina.net/catia/blog/176384

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚&#xff0c;地接的导游非常热情&#xff0c;如同大多数人一样&#xff0c;导游也会在这短短的几天内&#xff0c;尽可能的表现自己&#xff0c;此文聊聊导游说的三句话。 旅游三不“较”&#xff1a; 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法&#xff0c;利用cat /proc/devices查看申请到的设备名&#xff0c;设备号。 第一种是使用mknod手工创建&#xff1a;mknod filename type major minor 第二种是自动创建设备节点&#xff1a;利用udev&#xff08;mdev&#xff09;来实现设备…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

MM引擎新应用——爱车加油记

基于MM应用引擎开发的EXTJS应用&#xff0c;车主每次加完汽油后&#xff0c;记录加油时的里程数以及加油金额和汽油价格&#xff0c;就可计算出上次加油后行驶的里程数、上次加油的平均油耗。点击刷新按钮&#xff0c;即可计算有记录以来的行驶公里数和再次期间加油金额和平均油…