2020-08-20 linux kernel Ioctl 编程

一、ioctl函数的命令定义方法:

int (*unlocked_ioctl)(struct file*filp,unsigned int cmd,unsigned long arg)

虽然其中没有指针的参数,但是通常采用arg传递指针参数。cmd是一个命令。每一个命令由一个整形数据构成(32bits),将一个命令分成四部分,每一部分实现具体的配置,设备类型(幻数)8bits,序号8bits,数据大小13/14bits,方向2bits。命令的实现实质上就是通过简单的移位操作,将各个部分组合起来而已。

一个命令的分布的大概情况如下:

|--方向位(31-30)--|---数据长度(29-16)-----|----设备类型(15-8)----|----序号(7-0)-------|

方向位主要是表示对设备的操作,比如读设备,写设备等操作以及读写设备等都具有一定的方向,2个bits只有4种方向。

数据长度表示每一次操作(读、写)数据的大小,一般而言每一个命令对应的数据大小都是一个固定的值,不会经常改变,14bits说明可以选择的数据长度最大为16k。

设备类型类似于主设备号(由于8bits,刚好组成一个字节,因此经常采用字符作为幻数,表示某一类设备的命令),用来区别不同的命令类型,也就是特定的设备类型对应特定的设备。

序号主要是这一类命令中的具体某一个,类似于次设备号(256个命令),也就是一个设备支持的命令多达256个。

在内核中用来定义命令的宏:

_IO(type,nr)                      表示定义一个没有方向的命令,

_IOR(type,nr,size)            表示定义一个类型为type,序号为nr,数据大小为size的读命令

_IOW(type,nr,size)            表示定义一个类型为type,序号为nr,数据大小为size的写命令

_IOWR(type,nr,size)          表示定义一个类型为type,序号为nr,数据大小为size的写读命令

通常的type可采用某一个字母或者数字作为设备命令类型。

实际运用中通常采用如下的方法定义一个具体的命令:

//头文件

/*定义一系列的命令*/

/*幻数,主要用于表示类型*/

#define MAGIC_NUM 'k'

/*打印命令*/

#define MEMDEV_PRINTF _IO(MAGIC_NUM,1)

/*从设备读一个int数据*/

#define MEMDEV_READ _IOR(MAGIC_NUM,2,int)

/*往设备写一个int数据*/

#define MEMDEV_WRITE _IOW(MAGIC_NUM,3,int)

/*最大的序列号*/

#define MEM_MAX_CMD 3

在内核中用来解析命令的宏:

对命令进行解析的宏,用来确定具体命令的四个部分(方向,大小,类型,序号)具体如下所示:

/*确定命令的方向*/

_IOC_DIR(nr)                 

/*确定命令的类型*/

_IOC_TYPE(nr)                 

/*确定命令的序号*/

_IOC_NR(nr)                       

/*确定命令的大小*/

_IOC_SIZE(nr) 

上面的几个宏可以用来命令,实现命令正确性的检查。


二、ioctl的实现过程主要包括如下的过程:

1)命令的检测

2)指针参数的检测

3)命令的控制switch-case语句

1、命令的检测主要包括类型的检查,数据大小,序号的检测,通过结合上面的命令解析宏可以快速的确定。

        /*检查类型,幻数是否正确*/

        if(_IOC_TYPE(cmd)!=MAGIC_NUM)

                return -EINVAL;

        /*检测命令序号是否大于允许的最大序号*/

        if(_IOC_NR(cmd)> MEM_MAX_CMD)

                return -EINVAL;

2、主要是指针参数的检测。指针参数主要是因为内核空间和用户空间的差异性导致的,因此需要判断来自用户空间指针的有效性。使用copy_from_user,copy_to_user,get_user,put_user之类的函数时,由于函数会实现指针参量的检测,因此可以省略,但是采用__get_user(),__put_user()之类的函数时一定要进行检测。具体的检测方法如下所示:

if(_IOC_DIR(cmd) & _IOC_READ)

        err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));

else if(_IOC_DIR(cmd) & _IOC_WRITE)

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

if(err)/*返回错误*/

        return -EFAULT;

当方向是读时,说明是从设备读数据,然后写到用户空间,因此要检测用户空间的指针是否可写,采用VERIFY_WRITE,而当方向是写时,说明是往设备中写数据,因此需要检测用户空间中的指针的可读性VERIFY_READ。检查通常采用access_ok()实现检测,第一个参数为读写,第二个为检测的指针,第三个为数据的大小。

3、命名的控制:

命令的控制主要是采用switch和case相结合实现的,这于window编程中的检测各种消息的实现方式是相同的。

/*根据命令执行相应的操作*/

        switch(cmd)

        {

                case MEMDEV_PRINTF:

                        printk("<--------CMD MEMDEV_PRINTF Done------------>\n\n");

                        ...

                        break;

                case MEMDEV_READ:

                        ioarg = &mem_devp->data;

                        ...

                        ret = __put_user(ioarg,(int *)args);

                        ioarg = 0;

                        ...

                        break;

                case MEMDEV_WRITE:

                        ...

                        ret = __get_user(ioarg,(int *)args);

                        printk("<--------CMD MEMDEV_WRITE Done ioarg = %d--------->\n\n",ioarg);

                        ioarg = 0;

                        ...

                        break;

                default:

                        ret = -EINVAL;

                        printk("<-------INVAL CMD--------->\n\n");

                        break;

        }

这只是基本的框架结构,实际中根据具体的情况进行修改。这样就实现了基本的命令控制。

三、文件操作支持的集合如下:

/*添加该模块的基本文件操作支持*/

static const struct file_operations mem_fops =

{

        /*结尾不是分号,注意其中的差别*/

        .owner = THIS_MODULE,

        .llseek = mem_llseek,

        .read = mem_read,

        .write = mem_write,

        .open = mem_open,

        .release = mem_release,

        /*添加新的操作支持*/

        .unlocked_ioctl = mem_ioctl,

};

需要注意不是ioctl,而是unlocked_ioctl。

四、access_ok 

access_ok — 检查用户空间指针是否有效

注意,根据体系结构的不同,这个函数可能只是检查指针是否在用户空间范围内——在调用这个函数之后,内存访问函数可能仍然返回 -EFAULT

函数原型:

access_ok ( type,  addr, size);

参数说明:

type,Type of access: VERIFY_READ or VERIFY_WRITE.请注意,VERIFY_WRITE是VERIFY_READ的超集——如果写入一个块是安全的,那么从它读取总是安全的。

addr,要检查的块的开始的用户空间指针

size,要检查的块的大小

返回值:

此函数检查用户空间中的内存块是否可用。如果可用,则返回真(非0值),否则返回假 (0) 。

用户上下文:在用户上下文。这个功能可能会休眠。

示例

static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)

{

if (access_ok(VERIFY_READ, from, n))

n = __copy_from_user(to, from, n);

else /* security hole - plug it */

memset(to, 0, n);

return n;

}

static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)

{

if (access_ok(VERIFY_WRITE, to, n))

n = __copy_to_user(to, from, n);

return n;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355