读和写方法都进行类似的任务, 就是, 从和到应用程序代码拷贝数据. 因此, 它们的原型 相当相似, 可以同时介绍它们:

ssize_t read(struct file *filp, char   user *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char   user *buff, size_t count, loff_t *offp);

对于 2 个方法, filp 是文件指针, count 是请求的传输数据大小. buff 参数指向持有被 写入数据的缓存, 或者放入新数据的空缓存. 最后, offp 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type"; 它的使用在后面讨论.

让我们重复一下, read 和 write 方法的 buff 参数是用户空间指针. 因此, 它不能被内 核代码直接解引用. 这个限制有几个理由:

  • · 依赖于你的驱动运行的体系, 以及内核被如何配置的, 用户空间指针当运行于内核 模式可能根本是无效的. 可能没有那个地址的映射, 或者它可能指向一些其他的随 机数据.
  • 就算这个指针在内核空间是同样的东西, 用户空间内存是分页的, 在做系统调用时 这个内存可能没有在 RAM 中. 试图直接引用用户空间内存可能产生一个页面错, 这 是内核代码不允许做的事情. 结果可能是一个"oops", 导致进行系统调用的进程死 亡.
  • · 置疑中的指针由一个用户程序提供, 它可能是错误的或者恶意的. 如果你的驱动盲 目地解引用一个用户提供的指针, 它提供了一个打开的门路使用户空间程序存取或 覆盖系统任何地方的内存. 如果你不想负责你的用户的系统的安全危险, 你就不能 直接解引用用户空间指针.

显然, 你的驱动必须能够存取用户空间缓存以完成它的工作. 但是, 为安全起见这个存取 必须使用特殊的, 内核提供的函数. 我们介绍几个这样的函数(定义于 <asm/uaccess.h>), 剩下的在第一章"使用 ioctl 参数"一节中. 它们使用一些特殊的, 依赖体系的技巧来确保 内核和用户空间的数据传输安全和正确.

scull 中的读写代码需要拷贝一整段数据到或者从用户地址空间. 这个能力由下列内核函 数提供, 它们拷贝一个任意的字节数组, 并且位于大部分读写实现的核心中.

unsigned long copy_to_user(void user *to,const void *from,unsigned long count); unsigned long copy_from_user(void *to,const void   user *from,unsigned long count);

尽管这些函数表现象正常的 memcpy 函数, 必须加一点小心在从内核代码中存取用户空间. 寻址的用户也当前可能不在内存, 虚拟内存子系统会使进程睡眠在这个页被传送到位时. 例如, 这发生在必须从交换空间获取页的时候. 对于驱动编写者来说, 最终结果是任何存 取用户空间的函数必须是可重入的, 必须能够和其他驱动函数并行执行, 并且, 特别的, 必须在一个它能够合法地睡眠的位置. 我们在第 5 章再回到这个主题.

这 2 个函数的角色不限于拷贝数据到和从用户空间: 它们还检查用户空间指针是否有效. 如果指针无效, 不进行拷贝; 如果在拷贝中遇到一个无效地址, 另一方面, 只拷贝部分数 据. 在 2 种情况下, 返回值是还要拷贝的数据量. scull 代码查看这个错误返回, 并且如 果它不是 0 就返回 -EFAULT 给用户.

用户空间存取和无效用户空间指针的主题有些高级, 在第 6 章讨论. 然而, 值得注意的是 如果你不需要检查用户空间指针, 你可以调用  copy_to_user 和  copy_from_user 来 代替. 这是有用处的, 例如, 如果你知道你已经检查了这些参数. 但是, 要小心; 事实上, 如果你不检查你传递给这些函数的用户空间指针, 那么你可能造成内核崩溃和/或安全漏洞.

至于实际的设备方法, read 方法的任务是从设备拷贝数据到用户空间(使用 copy_to_user), 而 write 方法必须从用户空间拷贝数据到设备(使用 copy_from_user). 每个 read 或 write 系统调用请求一个特定数目字节的传送, 但是驱动可自由传送较少数 据 -- 对读和写这确切的规则稍微不同, 在本章后面描述.

不管这些方法传送多少数据, 它们通常应当更新 *offp 中的文件位置来表示在系统调用成 功完成后当前的文件位置. 内核接着在适当时候传播文件位置的改变到文件结构. pread 和 pwrite 系统调用有不同的语义; 它们从一个给定的文件偏移操作, 并且不改变其他的 系统调用看到的文件位置. 这些调用传递一个指向用户提供的位置的指针, 并且放弃你的 驱动所做的改变.

read 和 write 方法都在发生错误时返回一个负值. 相反, 大于或等于 0 的返回值告知调 用程序有多少字节已经成功传送. 如果一些数据成功传送接着发生错误, 返回值必须是成 功传送的字节数, 错误不报告直到函数下一次调用. 实现这个传统, 当然, 要求你的驱动 记住错误已经发生, 以便它们可以在以后返回错误状态.

尽管内核函数返回一个负数指示一个错误, 这个数的值指出所发生的错误类型( 如第 2 章 介绍 ), 用户空间运行的程序常常看到 -1 作为错误返回值. 它们需要存取 errno 变量来 找出发生了什么. 用户空间的行为由 POSIX 标准来规定, 但是这个标准没有规定内核内部 如何操作.

linux scull 中的读写代码的更多相关文章

  1. linux scull 中的设备注册

    在内部, scull 使用一个 struct scull_dev 类型的结构表示每个设备. 这个结构定义为: struct scull_dev { struct scull_qset *data;  ...

  2. linux scull 中的缺陷

    让我们快速看一段 scull 内存管理代码. 在写逻辑的深处, scull 必须决定它请求的内 存是否已经分配. 处理这个任务的代码是: if (!dptr->data[s_pos]) { dp ...

  3. 【转】 Linux内核中读写文件数据的方法--不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法  有时候需要在Linuxkernel--大 ...

  4. Android 怎样在linux kernel 中读写文件

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  5. Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁

    在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...

  6. Linux内核中锁机制之信号量、读写信号量

    在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...

  7. 大话Linux内核中锁机制之信号量、读写信号量

    大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...

  8. 大话Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁

    大话Linux内核中锁机制之内存屏障.读写自旋锁及顺序锁 在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论 ...

  9. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

随机推荐

  1. POJ1485 Sumdiv

    Sumdiv Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22680   Accepted: 5660 Descripti ...

  2. PHP学习(数据类型)

    PHP中,支持8种原始类型,其中包括四种标量类型.两种复合类型和两种特殊类型.PHP是一门松散类型的语言,不必向PHP声明变量的数据类型,PHP会自动把变量转换为自动的数据类型,一定程度降低了学习PH ...

  3. linux awk命令详解,使用system来内嵌系统命令, awk合并两列

    linux awk命令详解 简介 awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分 ...

  4. c# 日期函数

    DateTime dt = DateTime.Now;Label1.Text = dt.ToString();//2005-11-5 13:21:25Label2.Text = dt.ToFileTi ...

  5. python 并发之线程

    一.什么是线程 #指的是一条流水线的工作过程,关键的一句话:一个进程内最少自带一个线程,其实进程根本不能执行,进程不是执行单位,是资源的单位,分配资源的单位 #线程才是执行单位 #进程:做手机屏幕的工 ...

  6. 你在用 JWT 代替 Session?

    现在,JSON Web Tokens (JWT) 是非常流行的.尤其是 Web 开发领域. 流行 安全 稳定 易用 支持 JSON 所有这些因素,令 JWT 名声大振. 但是,今天我要来说说使用 JW ...

  7. BZOJ1085 luogu2324骑士精神题解

    没有什么特别好的办法,只好用搜索去做 因为一次移动最多归位一个骑士 所以可以想到用IDA*,为了简化状态 我们用k,x,y,sum来表示移动了k步,空格在x,y,还用sum个没有归位的情况 然后枚举转 ...

  8. 洛谷1758 BZOJ1566 管道取珠题解

    题目链接 一道人类智慧的dp题 首先我们可以将∑ai^2转化为求取两次,两次一样的方案数 然后用f[i][j][k][l]表示第一个人在第一个串中取到i第二个串中取到j 第二个人在一个串中取到k第二个 ...

  9. sql select时增加常量列

    阅读更多 string sql="select a,b,'常量' as c from table" 注:单引号' ' 很重要,否则编译时会把其看成查询参数,从而提示参数未指定错误. ...

  10. @bzoj - 2395@ [Balkan 2011]Timeismoney

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 有n个城市(编号从0..n-1),m条公路(双向的),从中选择n ...