zoukankan      html  css  js  c++  java
  • linux设备驱动归纳总结(三):4.ioctl的实现

    linux设备驱动归纳总结(三):4.ioctl的实现


    一、ioctl的简单介绍:

    尽管在文件操作结构体"struct file_operations"中有非常多相应的设备操作函数。可是有些命令是实在找不到相应的操作函数。

    CD-ROM的驱动,想要一个弹出光驱的操作,这样的操作并非全部的字符设备都须要的。所以文件操作结构体也不会有相应的函数操作。


    出于这种原因,ioctl就有它的用处了————一些没办法归类的函数就统一放在ioctl这个函数操作中。通过指定的命令来实现相应的操作。所以。ioctl函数里面都实现了多个的对硬件的操作。通过应用层传入的命令来调用相应的操作。


    来个图来说一下应用层与驱动函数的ioctl之间的联系:

    上面的图能够看出。fd通过内核后找到相应的inodefile结构体指针并传给驱动函数,而另外两个參数却没有改动(类型改了没什么关系)


    简介一下函数:

    int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);

    參数:

    1)inodefileioctl的操作有可能是要改动文件的属性,或者訪问硬件。要改动

    文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。

    2)cmd:命令,接下来要长篇大论地说。

    3)arg:參数,接下来也要长篇大论。

    返回值:

    1)假设传入的非法命令,ioctl返回错误号-EINVAL

    2)内核中的驱动函数返回值都有一个默认的方法,仅仅要是正数,内核就会傻乎乎的觉得这是正确的返回,并把它传给应用层,假设是负值,内核就会觉得它是错误号了。

    Ioctl里面多个不同的命令,那就要看它函数的实现来决定返回值了。

    打个例如,假设ioctl里面有一个相似read的函数。那返回值也就能够像read一样返回。

    当然。不返回也是能够的。


    二、ioctlcmd


    说白了,cmd就是一个数,假设应用层传来的数值在驱动中有相应的操作,这样就就能够了。


    来个最简单的ioctl实现:3rd_char_4/1st


    1)要先定义个命令。就用一个简单的0,来个命令的头文件,驱动和应用函数都要包括这个头文件

    /*test_cmd.h*/

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_CLEAR 0

    5

    6 #endif /*_TEST_CMD_H*/

    2)驱动实现ioctl

    命令TEST_CLEAR的操作就是清空驱动中的kbuf

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 switch(cmd){

    128 case TEST_CLEAR:

    129 memset(dev->kbuf, 0, DEV_SIZE);

    130 dev->cur_size = 0;

    131 filp->f_pos = 0;

    132 ret = 0;

    133 break;

    134 default: /*命令错误时的处理*/

    135 P_DEBUG("error cmd! ");

    136 ret = - EINVAL;

    137 break;

    138 }

    139

    140 return ret;

    141 }


    3)再来个应用程序:

    1 #include <stdio.h>

    2 #include <sys/types.h>

    3 #include <sys/stat.h>

    4 #include <fcntl.h>

    5 #include <sys/ioctl.h>

    6 #include "test_cmd.h"

    7

    8 int main(void)

    9 {

    10 char buf[20];

    11 int fd;

    12 int ret;

    13

    14 fd = open("/dev/test", O_RDWR);

    15 if(fd < 0)

    16 {

    17 perror("open");

    18 return -1;

    19 }

    20

    21 write(fd, "xiao bai", 10); //1先写入

    22

    23 ioctl(fd, TEST_CLEAR); //2再清空

    24

    25 ret = read(fd, buf, 10); //3再验证

    26 if(ret < 0)

    27 {

    28 perror("read");

    29 }

    30

    31 close(fd);

    32 return 0;

    33 }

    注:这里为了read返回出错,我改动了驱动的readwrite函数的開始时的第一个

    推断,一看就知道了。


    4)验证一下:

    [root: 1st]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 1st]# mknod /dev/test c 253 0

    [root: 1st]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    read: No such device or address //哈哈!出错了!

    由于没数据读取。



    依照上面的方法来定义一个命令是全然能够的,但内核开发者发现这样有点不正确劲。

    假设有两个不同的设备,但它们的ioctlcmd却一样的,哪天有谁不小心打开错了,而且调用ioctl,这样就完蛋了。由于这个文件中面相同有cmd相应实现。

    为了防止这种事情发生,内核对cmd又有了新的定义,规定了cmd都应该不一样。


    三、ioctl中的cmd


    一个cmd被分为了4个段,每一段都有各自的意义。cmd的定义在<linux/ioctl.h>。注:但实际上<linux/ioctl.h>中仅仅是包括了<asm/ioctl.h>,这说明了这是跟平台相关的。ARM的定义在<arch/arm/include/asm/ioctl.h>,但这文件也是包括别的文件<asm-generic/ioctl.h>,千找万找。最终找到了。


    <asm-generic/ioctl.h>中,cmd拆分例如以下:

    解释一下四部分。所有都在<asm-generic/ioctl.h>ioctl-number.txt这两个文档有说明。

    1)幻数:说得再好听的名字也仅仅只是是个0~0xff的数。占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,像设备号申请的时候一样,内核有一个文档给出一些推荐的或者已经被使用的幻数。

    /*Documentation/ioctl/ioctl-number.txt*/

    164 'w' all CERN SCI driver

    165 'y' 00-1F packet based user level communications

    166 <mailto:zapman@interlan.net>

    167 'z' 00-3F CAN bus card

    168 <mailto:hdstich@connectu.ulm.circular.de>

    169 'z' 40-7F CAN bus card

    170 <mailto:oe@port.de>

    能够看到'x'是还没有人用的。我就拿这个当幻数!


    2)序数:用这个数来给自己的命令编号,占8bit(_IOC_NRBITS),我的程序从1開始排序。


    3)传输数据方向:占2bit(_IOC_DIRBITS)。假设涉及到要传參。内核要求描写叙述一下传输的方向。传输的方向是以应用层的角度来描写叙述的。

    1)_IOC_NONE:值为0,无传输数据。

    2)_IOC_READ:值为1。从设备驱动读取数据。

    3)_IOC_WRITE:值为2,往设备驱动写入数据。

    4)_IOC_READ|_IOC_WRITE:双向传输数据。


    4)数据大小:与体系结构相关ARM下占14bit(_IOC_SIZEBITS),假设数据是int,内核给这个赋的值就是sizeof(int)


    强调一下。内核是要求按这个方案把cmd分类。当然你也能够不这样干。这仅仅是为了迎合内核的要求。让自己的程序看上去非常正宗。上面我的程序没按要求照样执行。


    既然内核这样定义cmd,就肯定有方法让用户方便定义:

    _IO(type,nr) //没有參数的命令

    _IOR(type,nr,size) //该命令是从驱动读取数据

    _IOW(type,nr,size) //该命令是从驱动写入数据

    _IOWR(type,nr,size) //双向传输数据

    上面的命令已经定义了方向。我们要传的是幻数(type)、序号(nr)和大小(size)。在这里szie的參数仅仅须要填參数的类型,如int,上面的命令就会帮你检測类型的正确然后赋值sizeof(int)


    有生成cmd的命令就必有拆分cmd的命令:

    _IOC_DIR(cmd) //从命令中提取方向

    _IOC_TYPE(cmd) //从命令中提取幻数

    _IOC_NR(cmd) //从命令中提取序数

    _IOC_SIZE(cmd) //从命令中提取数据大小


    越讲就越复杂了,既然说到这。随便就讲一下提前定义命令。

    提前定义命令是由内核来识别而且实现对应的操作,换句话说,一旦你使用了这些命令。你压根也不要指望你的驱动程序可以收到,由于内核拿掉就把它处理掉了。


    分为三类:

    1)可用于不论什么文件的命令

    2)仅仅用于普通文件的命令

    3)特定文件系统类型的命令


    事实上上面的我三类我也没搞懂,反正我自己随便编了几个数当命令都没出错,假设真的怕出错,那就不要用别人已经使用的幻数即可了。


    讲了这么多,最终要上程序了。改动一下上一个程序,让它看起来比較有内涵。

    /3rd_char/3rd_char_4/2nd

    1)先改一下命令:

    /*test_cmd.h*/

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_MAGIC 'x' //定义幻数

    5 #define TEST_MAX_NR 1 //定义命令的最大序数。仅仅有一个命令当然是1

    6

    7 #define TEST_CLEAR _IO(TEST_MAGIC, 0)

    8

    9 #endif /*_TEST_CMD_H*/


    2)既然这么辛苦改了cmd,在驱动函数当然要做一些參数检验:

    /*test.c*/

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令。当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130

    131 switch(cmd){

    132 case TEST_CLEAR:

    133 memset(dev->kbuf, 0, DEV_SIZE);

    134 dev->cur_size = 0;

    135 filp->f_pos = 0;

    136 ret = 0;

    137 break;

    138 default: /*命令错误时的处理*/

    139 P_DEBUG("error cmd! ");

    140 ret = - EINVAL;

    141 break;

    142 }

    143

    144 return ret;

    145 }

    每一个參数的传入都会先检验一下幻数还有序数是否正确。


    3)应用程序的验证

    结果跟上一个全然一样,由于命令的操作没有改动

    [root: 2nd]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 2nd]# mknod /dev/test c 253 0

    [root: 2nd]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    read: No such device or address


    五、ioctl中的arg之整数传參。



    上面讲的样例都没有使用ioctl的传參。

    这里先要说一下ioctl传參的方式。


    应用层的ioctl的第三个參数是"..."。这个跟printf"..."可不一样。printf中是意味这你能够传随意个数的參数。而ioctl最多也仅仅能传一个,"..."的意思是让内核不要检查这个參数的类型。也就是说,从用户层能够传入不论什么參数,仅仅要你传入的个数是1.


    通常会有两种的传參方法:

    1)整数。那但是省力又省心。直接使用就能够了。

    2)指针。通过指针的就传什么类型都能够了。当然用起来就比較烦。


    先说简单的,使用整数作为參数:

    样例,实现个命令,通过传入參数更改偏移量,尽管llseek已经实现,这里仅仅是想验证一下正数传參的方法。


    1)先加个命令:

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_MAGIC 'x' //定义幻数

    5 #define TEST_MAX_NR 2 //定义命令的最大序数

    6

    7 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    8 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    9

    10 #endif /*_TEST_CMD_H*/

    这里有人会问了。明明你是要传入參数,为什么不用_IOW而用_IO定义命令呢?

    原因有二:

    1)由于定义数据的传输方向是为了好让驱动的函数验证数据的安全性。而一般指针才须要检验安全性,由于有人会恶意传參(回忆一下copy_to_user)

    2)个人喜好,方便我敲代码介绍还有一种传參方法。说白了命令也仅仅是一个数,仅仅要不要跟提前定义命令冲突就能够了。


    2)更新test_ioctl

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令。当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130

    131 switch(cmd){

    132 case TEST_CLEAR:

    133 memset(dev->kbuf, 0, DEV_SIZE);

    134 dev->cur_size = 0;

    135 filp->f_pos = 0;

    136 ret = 0;

    137 break;

    138 case TEST_OFFSET: //依据传入的參数更改偏移量

    139 filp->f_pos += (int)arg;

    140 P_DEBUG("change offset! ");

    141 ret = 0;

    142 break;

    143 default: /*命令错误时的处理*/

    144 P_DEBUG("error cmd! ");

    145 ret = - EINVAL;

    146 break;

    147 }

    148

    149 return ret;

    150 }

    TSET_OFFSET命令就是依据传參更改偏移量,只是这里要注意一个问题,那就是參数的类型,驱动函数必需要知道从应用传来的參数是什么类型,不然就没法使用。

    在这个函数里。从应用层传来的參数是int,因此在驱动中也得用int


    3)再改一下应用程序:

    1 #include <stdio.h>

    2 #include <sys/types.h>

    3 #include <sys/stat.h>

    4 #include <fcntl.h>

    5 #include <sys/ioctl.h>

    6

    7 #include "test_cmd.h"

    8

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 fd = open("/dev/test", O_RDWR);

    16 if(fd < 0)

    17 {

    18 perror("open");

    19 return -1;

    20 }

    21

    22 write(fd, "xiao bai", 10); //先写入

    23

    24 ioctl(fd, TEST_OFFSET, -10); //再改偏移量

    25

    26 ret = read(fd, buf, 10); //再读数据

    27 printf("<app> buf is [%s] ", buf);

    28 if(ret < 0)

    29 {

    30 perror("read");

    31 }

    32

    33 close(fd);

    34 return 0;

    35 }


    4)验证一下

    [root: 3rd]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 3rd]# mknod /dev/test c 253 0

    [root: 3rd]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_ioctl]change offset! //更改偏移量

    <kernel>[test_read]read 10 bytes, cur_size:[0] //没错误。成功读取!

    <app> buf is [xiao bai]


    上面的传參非常easy把,接下来说一下以指针传參。

    考虑到參数不可能永远仅仅是一个正数这么简单,假设要传多一点的东西,譬如是结构体,那就得用上指针了。


    六、ioctl中的arg之指针传參。


    一讲到从应用程序传来的指针,就得想起我邪恶的传入了非法指针的样例。

    所以,驱动程序中不论什么与应用层打交道的指针,都得先检验指针的安全性。


    讲到这检验又有两种方法:

    1)用的时候才检验。

    2)一进来ioctl就检验。


    先说用的时候检验,说白了就是用copy_xx_user系列函数。以下实现一下:

    1)先定义个命令

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 struct ioctl_data{

    5 unsigned int size;

    6 char buf[100];

    7 };

    8

    9 #define DEV_SIZE 100

    10

    11 #define TEST_MAGIC 'x' //定义幻数

    12 #define TEST_MAX_NR 3 //定义命令的最大序数

    13

    14 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    15 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    16 #define TEST_KBUF _IO(TEST_MAGIC, 3)

    17

    18 #endif /*_TEST_CMD_H*/

    这里有定义多了一个函数,尽管这个命令是涉及到了指针的传參,但我还是_IOW,还是那一句,如今还不须要用上。

    该命令的操作是传进一个结构体指针。驱动依据结构体的内容改动kbufcur_size和偏移量。


    2)来个实现函数:

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126 struct ioctl_data val;

    127

    128 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    129 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    130 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    131

    132 switch(cmd){

    133 case TEST_CLEAR:

    134 memset(dev->kbuf, 0, DEV_SIZE);

    135 dev->cur_size = 0;

    136 filp->f_pos = 0;

    137 ret = 0;

    138 break;

    139 case TEST_OFFSET: //依据传入的參数更改偏移量

    140 filp->f_pos += (int)arg;

    141 P_DEBUG("change offset! ");

    142 ret = 0;

    143 break;

    144 case TEST_KBUF: //改动kbuf

    145 if(copy_from_user(&val, (struct ioctl_data *)arg, sizeof(struct ioctl_data))){

    146 ret = - EFAULT;

    147 goto RET;

    148 }

    149 memset(dev->kbuf, 0, DEV_SIZE);

    150 memcpy(dev->kbuf, val.buf, val.size);

    151 dev->cur_size = val.size;

    152 filp->f_pos = 0;

    153 ret = 0;

    154 break;

    155 default: /*命令错误时的处理*/

    156 P_DEBUG("error cmd! ");

    157 ret = - EINVAL;

    158 break;

    159 }

    160

    161 RET:

    162 return ret;

    163 }

    145行,由于指针是从用户程序传来。所以必须检查安全性。


    3)来个应用程序

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 struct ioctl_data my_data= {

    16 .size = 10,

    17 .buf = "123456789"

    18 };

    19

    20 fd = open("/dev/test", O_RDWR);

    21 if(fd < 0)

    22 {

    23 perror("open");

    24 return -1;

    25 }

    26

    27 write(fd, "xiao bai", 10);

    28

    29 ioctl(fd, TEST_KBUF, &my_data);

    30

    31 ret = read(fd, buf, 10);

    32 printf("<app> buf is [%s] ", buf);

    33 if(ret < 0)

    34 {

    35 perror("read");

    36 }

    37

    38 close(fd);

    39 return 0;

    40 }


    4)再来验证一下:

    [root: 4th]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_read]read 10 bytes, cur_size:[0]

    <app> buf is [123456789] //成功!

    注:相似copy_xx_user的函数含有put_userget_user等,我就不细说了。

    以下说另外一种方法:进入ioctl后使用access_ok检測。

    声明一下:以下的验证方法是不对的。

    假设不想看下去的话,今天的内容已经讲完了。


    先说一下access_ok的使用

    access_ok(type, addr, size)

    使用:检測地址的安全性

    參数:

    type:用于指定传输数据的方向。VERIFY_READ表示要读取应用层数据。VERIFT_WRITE表示要往应用层写如数据。注意:这里和IOR IOW的方向相反

    假设既读取又写入,那就使用VERIFY_WRITE

    addr:用户空间的地址

    size:数据的大小

    返回值:

    成功返回1,失败返回0


    既然知道怎么用,就直接来程序了:

    1)定义命令

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 struct ioctl_data{

    5 unsigned int size;

    6 char buf[100];

    7 };

    8

    9 #define DEV_SIZE 100

    10

    11 #define TEST_MAGIC 'x' //定义幻数

    12 #define TEST_MAX_NR 3 //定义命令的最大序数

    13

    14 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    15 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    16 #define TEST_KBUF _IOW(TEST_MAGIC, 3, struct ioctl_data)

    17

    18 #endif /*_TEST_CMD_H*/

    这里最终要用_IOW了!


    2)实现ioctl

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130 /*依据提取命令指定的方向推断指针的安全性*/

    131 if(_IOC_DIR(cmd) & _IOC_READ)

    132 ret = access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

    133 else if(_IOC_DIR(cmd) & _IOC_WRITE)

    134 ret = access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

    135 if(!ret) return - EFAULT;

    136

    137 switch(cmd){

    138 case TEST_CLEAR:

    139 memset(dev->kbuf, 0, DEV_SIZE);

    140 dev->cur_size = 0;

    141 filp->f_pos = 0;

    142 ret = 0;

    143 break;

    144 case TEST_OFFSET: //依据传入的參数更改偏移量

    145 filp->f_pos += (int)arg;

    146 P_DEBUG("change offset! ");

    147 ret = 0;

    148 break;

    149 case TEST_KBUF: //改动kbuf

    150 memset(dev->kbuf, 0, DEV_SIZE);

    151 memcpy(dev->kbuf, ((struct ioctl_data *)arg)->buf,

    152 ((struct ioctl_data *)arg)->size);

    153 dev->cur_size = ((struct ioctl_data *)arg)->size;

    154 filp->f_pos = 0;

    155 ret = 0;

    156 break;

    157 default: /*命令错误时的处理*/

    158 P_DEBUG("error cmd! ");

    159 ret = - EINVAL;

    160 break;

    161 }

    162

    163 return ret;

    164 }

    上面并没实用copy_to_user,而是通过access_ok来检測。


    3)再来个应用程序:

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 struct ioctl_data my_data= {

    16 .size = 10,

    17 .buf = "123456789"

    18 };

    19

    20 fd = open("/dev/test", O_RDWR);

    21 if(fd < 0)

    22 {

    23 perror("open");

    24 return -1;

    25 }

    26

    27 write(fd, "xiao bai", 10);

    28

    29 ret = ioctl(fd, TEST_KBUF, &my_data);

    30 if(ret < 0)

    31 {

    32 perror("ioctl");

    33 }

    34

    35 ret = read(fd, buf, 10);

    36 printf("<app> buf is [%s] ", buf);

    37 if(ret < 0)

    38 {

    39 perror("read");

    40 }

    41

    42 close(fd);

    43 return 0;

    44 }


    4)验证一下:效果和上一个一样

    [root: 5th]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_read]read 10 bytes, cur_size:[0]

    <app> buf is [123456789]


    以下就要如正题了,这个驱动是有问题的,那就是验证安全性全然不起作用!

    当我传入非法指针时,驱动相同会输出。不信能够自己传个邪恶地址(void *)0进去试一下。


    改动应用程序一样代码:

    29 ret = ioctl(fd, TEST_KBUF, &my_data);


    上面是我做的错误实现,我本来想验证。仅仅要经过access_ok检验,数据就会安全。没想到经过access_ok检验之后照样会出错。

    可是,copy_to_user相同是先调用access_ok再调用memcpy,它却没出错。这个我事情我如今都没搞明确,假设谁知道了麻烦指点一下。


    我查了设备驱动第三版。在144页有这种说法:

    1.access_ok并没有做完的全部的内存检查,

    2.大多数的驱动代码都不是用access_ok的,后面的内存管理会讲述。


    在这里书本上有这种约定:(都是我自己的理解)

    1.传入指针须要检查安全性。

    memcpy函数尽量不要在内核中使用。

    2.copy_to_user.copy_from_user.get_user.put_user函数会再拷贝数据前检測指针的安全性。不须要access_ok

    3.假设在ioctl函数开头使用了accsee_ok检验数据,接下来的代码能够使用__put_user__get_user这些不须要检測的函数(书上有样例)


    尽管还有写东西还没搞懂,但个人认为,假设使用个access_ok要这么麻烦的话。那我就不用好了,以后我就使用copy_xx_user函数,省力又省心。




    七、总结:


    这次讲了ioctl的实现:

    1)命令是怎么定义。

    2)參数怎么传递。


    =======================================================

    源码: 3rd_char_4.rar   

    <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?

    v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>

    阅读(31) | 评论(0) | 转发(0) |
给主人留下些什么吧!

~~

评论热议
查看全文
  • 相关阅读:
    c# winform 调用js
    删除Management Data Warehouse (MDW) job失败
    AjaxUpload跨域上传问题
    Ajax.BeginForm()实现ajax无刷新提交
    MSDTC故障排除
    验证淘宝店铺真实性的几个方案
    微信公众号接收消息和发送消息开发流程和注意事项
    MVC中return File(byte[],"image/jpeg")输入图片不清晰
    JavaScript Patterns 2.9 Coding Conventions
    JavaScript Patterns 2.8 Number Conversions with parseInt()
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7118217.html
  • Copyright © 2011-2022 走看看