在用户态中,很多时候除了要读写内核设备外,还要进行一些其他的控制。ioctl可以实现用户态和内核态的通信,即通过用户态向内核态发送命令,在内核态的switch case中做出相应的处理,即可在用户态实现对内核态的控制。那么ioctl是怎么实现的呢?
1. 首先用户态要向内核态发送命令,看看命令是怎么构成的。一个ioctl命令被分为多个字段,包括:类型(幻数)、序数、传送方向以及参数的大小。
type:幻数。选择一个号码(记住先仔细阅读ioctl-number.txt),并在整个驱动程序中使用这个号码,8位宽(_IOC_TYPEBITS).
number:序数(顺序编号、在我们的驱动中可以用命令的枚举序数)。也是8为宽
direction:数据传送的方向。
size:所涉及ioctl函数中所传送参数的大小。
linux中有一些构造构造命令编号的宏:
_IO(type, nr)用于构造无参数命令的编号
_IOR(type, nr, datatype)用于构造从设备中读取数据的命令的编号。
_IOW(type, nr, datatype)用于构造向设备中写数据的命令的编号。
_IOWR(type, nr, datatype)用于构造双向传输数据的命令的编号。
2.在用户空间中。ioctl系统调用的原型:
ioctl(int fd, unsigned long cmd, void *arg);
fd: 为打开的设备的文件描述符
cmd:为上面生成的命令码编号
arg:用户需要传入的参数(在用户空间和内核空间要通过copy_from_user和copy_to_user交换数据)
3.在内核空间ioctl的原型如下:
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
inode 和 filp两个指针的值对应于应用程序传送的文件描述符fd。
在内核中可以用_IO_TYPE(cmd)来获取命令的幻数,可以用来判断是不是我们要出来的命令
_IO_NR(cmd)可以用来获取命令的命令码,用于在switch case中处理。
4.当用一个指针在用户空间时,必须确保指向的用户空间时合法的。为此,我们可以通过access_ok验证地址(而不用传输数据)。
int access_ok(int type, const void *addr, unsigned int size)
第一个参数应该是VERIFY_READ和VERIFY_WRITE,取决于是要写入还是读取数据。addr是一个用户空间地址,size是字节数。