在看 scull 驱动的 ioctl 代码之前, 我们需要涉及的另一点是如何使用这个额外的参数. 如果它是一个整数, 就容易: 它可以直接使用. 如果它是一个指针, 但是, 必须小心些.

当用一个指针引用用户空间, 我们必须确保用户地址是有效的. 试图存取一个没验证过的 用户提供的指针可能导致不正确的行为, 一个内核 oops, 系统崩溃, 或者安全问题. 它 是驱动的责任来对每个它使用的用户空间地址进行正确的检查, 并且返回一个错误如果它 是无效的.

在第 3 章, 我们看了 copy_from_user 和 copy_to_user 函数, 它们可用来安全地移动 数据到和从用户空间. 这些函数也可用在 ioctl 方法中, 但是 ioctl 调用常常包含小数 据项, 可通过其他方法更有效地操作. 开始, 地址校验(不传送数据)由函数 access_ok 实现, 它定义在 <asm/uaccess.h>:

int access_ok(int type, const void *addr, unsigned long size);

第一个参数应当是 VERIFY_READ 或者 VERIFY_WRITE, 依据这个要进行的动作是否是读用 户空间内存区或者写它. addr 参数持有一个用户空间地址, size 是一个字节量. 例如, 如果 ioctl 需要从用户空间读一个整数, size 是 sizeof(int). 如果你需要读和写给定 地址, 使用 VERIFY_WRITE, 因为它是 VERIRY_READ 的超集.

不象大部分的内核函数, access_ok 返回一个布尔值: 1 是成功(存取没问题)和 0 是失 败(存取有问题). 如果它返回假, 驱动应当返回 -EFAULT 给调用者.

关于 access_ok 有多个有趣的东西要注意. 首先, 它不做校验内存存取的完整工作; 它只 检查看这个内存引用是在这个进程有合理权限的内存范围中. 特别地, access_ok 确保这 个地址不指向内核空间内存. 第 2, 大部分驱动代码不需要真正调用 access_ok. 后面描 述的内存存取函数为你负责这个. 但是, 我们来演示它的使用, 以便你可见到它如何完成.

scull 源码利用了 ioclt 号中的位段来检查参数, 在 switch 之前: int err = 0, tmp;

int retval = 0;

/*

*  extract the type
and number bitfields, and don't decode

*  wrong cmds:
return ENOTTY (inappropriate ioctl) before access_ok()

*/

if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)

return -ENOTTY;

if (_IOC_NR(cmd) > SCULL_IOC_MAXNR)

return -ENOTTY;

/*

*  the direction is
a bitmask, and VERIFY_WRITE catches R/W

*  transfers. `Type'
is user-oriented, while

*  access_ok is
kernel-oriented, so the concept of "read" and

*  "write"
is reversed

*/

if (_IOC_DIR(cmd) & _IOC_READ)

err =
!access_ok(VERIFY_WRITE, (void user *)arg, _IOC_SIZE(cmd)); else if
(_IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ, (void   user *)arg, _IOC_SIZE(cmd));

if (err)

return -EFAULT;

在调用
access_ok 之后, 驱动可安全地进行真正的传输. 加上 copy_from_user 和 copy_to_user_ 函数, 程序员可利用一组为被最多使用的数据大小(1,
2, 4, 和 8 字节) 而优化过的函数. 这些函数在下面列表中描述, 它们定义在 <asm/uaccess.h>:

put_user(datum, ptr)

put_user(datum,
ptr)

这些宏定义写 datum 到用户空间; 它们相对快, 并且应当被调用来代替
copy_to_user 无论何时要传送单个值时. 这些宏已被编写来允许传递任何类型的 指针到 put_user, 只要它是一个用户空间地址. 传送的数据大小依赖 prt 参数的 类型, 并且在编译时使用 sizeof 和 typeof 等编译器内建宏确定. 结果是, 如果 prt 是一个 char 指针, 传送一个字节, 以及对于 2, 4, 和 可能的 8 字节.

put_user
检查来确保这个进程能够写入给定的内存地址. 它在成功时返回 0, 并 且在错误时返回 -EFAULT. put_user 进行更少的检查(它不调用
access_ok), 但是仍然能够失败如果被指向的内存对用户是不可写的. 因此, put_user 应当 只用在内存区已经用 access_ok 检查过的时候.

作为一个通用的规则,
当你实现一个 read 方法时, 调用 put_user 来节省几个 周期, 或者当你拷贝几个项时, 因此, 在第一次数据传送之前调用 access_ok 一
次, 如同上面 ioctl 所示.

get_user(local, ptr)

get_user(local,
ptr)

这些宏定义用来从用户空间接收单个数据.
它们象 put_user 和 put_user, 但 是在相反方向传递数据. 获取的值存储于本地变量 local; 返回值指出这个操作是 否成功. 再次,   get_user 应当只用在已经使用 access_ok 校验过的地址.

如果做一个尝试来使用一个列出的函数来传送一个不适合特定大小的值,
结果常常是一个 来自编译器的奇怪消息, 例如"coversion to non-scalar type requested". 在这些情况
中, 必须使用 copy_to_user 或者 copy_from_user.

linux 使用 ioctl 参数的更多相关文章

  1. linux mount命令参数及用法详解

    linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...

  2. 【转】linux expr命令参数及用法详解

    在抓包过程中,查看某个设定时间内,数据上下行多少,用命令expr 计算! --------------------------------------------------------------- ...

  3. linux dmesg命令参数及用法详解(linux显示开机信息命令)

    linux dmesg命令参数及用法详解(linux显示开机信息命令) http://blog.csdn.net/zhongyhc/article/details/8909905 功能说明:显示开机信 ...

  4. linux sed命令参数及用法详解

    linux sed命令参数及用法详解 http://blog.csdn.net/namecyf/article/details/7336308 1. Sed简介 sed 是一种在线编辑器,它一次处理一 ...

  5. Linux mail 命令参数

    linux mail 命令参数: 使用mail发邮件时,必须先将sendmail服务启动. mail –s “邮件主题” –c”抄送地址” –b “密送地址” -- -f 发送人邮件地址 –F 发件人 ...

  6. Linux VM子系统参数调整

    Timesten数据库下的Linux page子系统参数调整  如果Timesten(TT)采用了Durablecommits或是share memory segment被lock的话,那么linux ...

  7. linux内核启动参数

    Linux内核启动参数   Console Options                         参数 说明 选项 内核配置/文件   console=Options 用于说明输出设备 tt ...

  8. linux dd命令参数及用法详解---用指定大小的块拷贝一个文件(也可整盘备份)

    linux dd命令参数及用法详解---用指定大小的块拷贝一个文件 日期:2010-06-14 点击:3830 来源: 未知 分享至:            linux dd命令使用详解 dd 的主要 ...

  9. nginx的linux服务器内核参数调整【转】

    概述 由于默认的linux内核参数考虑的是最通用场景,这明显不符合用于支持高并发访问的Web服务器的定义,所以需要修改Linux内核参数,让Nginx可以拥有更高的性能: 在优化内核时,可以做的事情很 ...

随机推荐

  1. 【JZOJ4788】【NOIP2016提高A组模拟9.17】序列

    题目描述 输入 输出 样例输入 1 5 2 1 3 0 3 2 2 0 1 0 样例输出 1 数据范围 解法 考虑没有模的情况,问题就仅仅只是简单的差分问题(广告铺设): 设r[i]是第i位需要加的次 ...

  2. Linux安装mongoDB步骤和方法

    Linux安装mongoDB步骤和方法 下载mongoDB数据库 mongodb-linux-x86_64-3.0.15.tgz 存放到linux文件夹中 ftp软件直接拖上去 解压文件夹(解压后,会 ...

  3. Directx11教程(16) D3D11管线(5)

    原文:Directx11教程(16) D3D11管线(5) 本章我们学习VS中具体做些什么事情? 首先再看看我们的VS shader代码: Clolor.vs - PixelInputType Col ...

  4. 发布网站时 遇到XX类型 同时存在XX.dll和XX.dll中

    遇到该问题的可能如下: 1.复制了页面 更改了名字 可是对应的一些地方没有注意 <%@ Page Language="C#" AutoEventWireup="tr ...

  5. VelocityTracker监控速度!!!

    用来追踪触摸事件(flinging事件和其他手势事件)的速率.用obtain()函数来获得类的实例,用addMovement(MotionEvent)函数将motion event加入到Velocit ...

  6. CPU核数和线程数查找

    方法1: 方法2:

  7. 巨蟒python全栈开发-第11阶段 ansible3_2入门八个模块

    大纲: 1.file模块 2.fetch模块 3.yum&&pip模块 4.service模块 5.cron模块 6.user模块 7.group模块

  8. SDUT-3340_数据结构实验之二叉树一:树的同构

    数据结构实验之二叉树一:树的同构 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 给定两棵树T1和T2.如果T1可以通过 ...

  9. angularjs 自定义指令弹窗

    (function() { 'use strict'; angular.module('frontierApp') .directive('confirmPopup', ['$timeout', Co ...

  10. dubbo开启access log步骤

    1,maven依赖 maven version由2.5.3调整为2.5.3.2 <dependency>     <groupId>com.alibaba</groupI ...