前言

本文的主要内容是Linux下的ioctl接口介绍及其应用,注意在写代码的过程中体会ioctl接口与read和write函数的异同。


一、ioctl接口介绍

ioctl接口也叫做unlocked_ioctl接口。
unlocked_ioctl和read/write函数的异同点:
相同点:都可以往内核中写数据。
不同点:read函数只能完成读的功能,write函数只能完成写的功能,ioctl既可以读也可以写,但在读取大数据时,ioctl的效率不及read/write函数。
unlocked_ioctl接口共有四个分区,分别如下:
第一个分区:0-7位,命令的编号,范围是0-255。
第二个分区:8-15位,命令的魔数(幻数)。
第一个分区和第二个分区的主要作用是用来区分命令。
第三个分区:16-29位,表示传递的数据大小。
第四个分区:30-31位,代表读写的方向。
30-31位的四种情况如下:
00:表示用户程序和驱动程序没有数据的传递;
01:表示用户程序向驱动里面写数据;
10:表示用户程序从驱动里面读数据;
11:表示用户程序先写数据到驱动,然后再从驱动里面把数据读出来。
命令的合成宏与分解宏:
合成宏:
_IO(type,nr) :用来定义没有数据传递的命令。
_IOR(type,nr,size) :用来定义从驱动中读取数据的命令。
_IOW(type,nr,size) :用来定义向驱动写入数据的命令。
_IOWR(type,nr,size) :用来定义数据交换类型的命令,先写入数据,再读取数据这类命令。
参数:
type:表示命令组成的魔数(幻数),即8-15位。
nr:表示命令组成的编号,即0-7位。
size:表示命令组成的参数传递大小,这里传递的不是数字,而是数据类型,如果要传4字节,就可以写成int。
分解宏:
_IOC_DIR(nr) :分解命令的方向,30-31位。
_IOC_TYPE:分解命令的魔数(幻数),8-15位。
_IOC_NR(nr) :分解命令的编号,0-7位。
_IOC_SIZE(nr) :分解命令的复制数据的大小,16-29位。
参数:
nr:要分解的命令。


二、简单打印程序

1.代码文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#define CMD_TEST0 _IO('A',0)
#define CMD_TEST1 _IO('B',1)
#define CMD_TEST2 _IOW('A',2,int)
#define CMD_TEST3 _IOR('A',3,int)

int main(int argv,char *arg[])
{
    printf("CMD_TEST0 30-31 bits is %d.\n",_IOC_DIR(CMD_TEST0));
    printf("CMD_TEST3 30-31 bits is %d.\n",_IOC_DIR(CMD_TEST3));
    printf("CMD_TEST0 7-15 bits is %c.\n",_IOC_TYPE(CMD_TEST0));
    printf("CMD_TEST1 7-15 bits is %c.\n",_IOC_TYPE(CMD_TEST1));
    printf("CMD_TEST2 0-7 bits is %d.\n",_IOC_NR(CMD_TEST2));
    return 0;
}

2.运行结果

使用如下命令编译该文件。

gcc app.c -o app

然后运行该文件,其打印结果如下。
在这里插入图片描述


三、ioctl读写程序

1.ioctl.c文件

#include "linux/init.h"
#include "linux/module.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/uaccess.h"
#include "linux/io.h"

#define CMD_TEST0 _IO('A',0)
#define CMD_TEST1 _IO('A',1)
#define CMD_TEST2 _IOW('A',2,int)
#define CMD_TEST3 _IOW('A',3,int)
#define CMD_TEST4 _IOR('A',4,int)

int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open!\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("misc_release bye!\n");
    return 0;
}

long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
{
    int val;
    switch(cmd){
        case CMD_TEST0: 
            printk("CMD_TEST0 is 0!\n");
            break;
        case CMD_TEST1:
            printk("CMD_TEST1 is 1!\n");
            break;
        case CMD_TEST2: 
            printk("The CMD_TEST2's value is %d.\n",value);
            break;
        case CMD_TEST3:
            printk("The CMD_TEST3's value is %d.\n",value);
            break;
        case CMD_TEST4:
            val = 100;
            if(copy_to_user((int *)value, &val, sizeof(val))!=0)
            {
                printk("copy_to_user error!\n");
                return -1;
            }
            break;
    }
    return 0;
}

struct file_operations misc_fops = {  //在结构体中引入相关函数
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .unlocked_ioctl = misc_ioctl
};

struct miscdevice misc_dev={
    .minor = MISC_DYNAMIC_MINOR, //次设备号
    .name = "misc_device",   //设备节点的名字
    .fops = &misc_fops
};

static int misc_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("miscdevice registered error!\n");
	    return -1;
    }
	printk("miscdevice registered ok!\n");
    return 0;
}
static int misc_exit(void)
{
    misc_deregister(&misc_dev);
	printk("misc exit!\n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

2.Makefile文件

obj-m += ioctl.o
KDIR:=/linux/linux-4.1.15
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

3.app.c文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#define CMD_TEST0 _IO('A',0)
#define CMD_TEST1 _IO('A',1)
#define CMD_TEST2 _IOW('A',2,int)
#define CMD_TEST3 _IOW('A',3,int)
#define CMD_TEST4 _IOR('A',4,int)

int main(int argv,char *arg[])
{
    int fd;
    int value; 
    fd = open("/dev/misc_device", O_RDWR);
    if(fd < 0)
    {
        perror("open error!\n"); 
        return fd; 
    }
    /*下面的三个while在编译时逐个编译,注释掉另外两个*/
    /*1.通过宏直接置数*/
    while(1)
    {
        ioctl(fd, CMD_TEST0); //这里的值就是传向long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
        sleep(1);
        ioctl(fd, CMD_TEST1);
        sleep(1);
    }
    /*2.写数*/
    /*while(1)
    {
        ioctl(fd, CMD_TEST2,0);
        sleep(1);
        ioctl(fd, CMD_TEST3,1);
        sleep(1);
    }*/
    /*3.读数*/
    /*while(1)
    {
        ioctl(fd,CMD_TEST4,&value);
        printf("value is %d.\n",value);
        sleep(1);
    }*/
    return 0;
}

4.运行结果

1>.通过宏直接置数

运行结果如下。
在这里插入图片描述

2>.写数

运行结果如下。
在这里插入图片描述

3>.读数

运行结果如下。
在这里插入图片描述


总结

以上就是Linux下的ioctl接口介绍及其应用的所有内容了,通过这个例子,我们应该对ioctl接口的作用比较熟悉,相对来说,ioctl接口的代码量比read和write函数的要少一些,也更加简单明了。
本文参考视频:https://www.bilibili.com/video/BV1Vy4y1B7ta?p=32https://www.bilibili.com/video/BV1Vy4y1B7ta?p=33

Logo

更多推荐