在看 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. R语言数据分析利器data.table包—数据框结构处理精讲

    R语言数据分析利器data.table包-数据框结构处理精讲 R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点快.包括两个方面,一方面是写的快,代 ...

  2. ServletConfig详解 (转载)

    ServletConfig详解  (转载)   容器初始化一个servlet时,会为这个servlet建一个唯一的ServletConfig.容器从DD读出Servlet初始化参数,并把这些参数交给S ...

  3. scorllview嵌套gridview和listview的兼容问题

    ScrollView嵌套GridView或ListView,由于这两款控件都自带滚动条,当他们碰到一起的时候便会出问题,即GridView会显示不全. 解决办法:自定义一个GridView控件 pac ...

  4. 19-2 from和modelform的用法和介绍

    一 form 1. form的作用 1. 生成HTML代码 2. 帮我们做数据有效性的校验 3. 保留上次输入内容,显示错误提示 2. form组件校验数据有效性   1. 内置的校验规则 1. re ...

  5. Directx11教程(48) depth/stencil buffer的作用

    原文:Directx11教程(48) depth/stencil buffer的作用      在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所 ...

  6. oracle 共享服务器监控

    1.   观察sga的使用情况 select * from v$sgastat where pool=’large pool’; 2.   观察调度程序是否充足: 首先看每个调度程序的忙闲: sele ...

  7. php7 新内容

    1.use增强 以thinkphp5.0为例 namespace app\home\controller;use think\{Loader,Controller,Captcha,Request}; ...

  8. spring data jpa 原生sql 别名字段无法注入

    开发四年只会写业务代码,分布式高并发都不会还做程序员?->>>    在使用entityManager.createNativeQuery(sql,User.class)这个方法时, ...

  9. mysql 查询当天、昨天、本周、上周、本月、上月、今年、去年数据

    mysql查询今天.昨天.7天.近30天.本月.上一月 数据 今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 昨天 SELECT ...

  10. C#判断文件是否被混淆

    可以使用混淆工具对一个DLL 和 exe 进行混淆. 但是如何知道一个文件是否已经混淆了. 在发布之前,需要知道是不是有文件忘了混淆. 要判断文件是否混淆,必须知道常用的混淆手法. 混淆就是因为编写的 ...