Linux设备驱动开发-file_operations结构体day02
作者: kiki
参考书:<linux设备驱动开发详解-宋宝华>
转载请注明出处!
day02
摘要: file_operations()结构体的结构与成员函数
1.file_operations结构体
其成员函数是字符设备驱动与内核虚拟文件系统的接口,是用户空间对Linux进行系统调用最终的落实者,把系统调用和驱动程序关联起来.
注意: __usr是一个宏,其后的指针指向用户空间.
(1)读设备
/*读设备*/
ssize_t xxx_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
...
copy_to_user(buf,...,...);
...
}
filp是文件结构体指针
buf是用户空间内存的地址,不宜直接读写
count是要读的字节数
f_pos是读的位置相对于文件开头的偏移
(2)写设备
ssize_t xxx_write(struct file *filp,const char__user *buf,size_t count,loff_t *f_pos)
{
...
copy_from_user(...,buf,...);
...
}
filp是文件结构体指针
buf是用户空间内存的地址,不宜直接读写
count是要写的字节数
f_pos是写的位置相对于文件开头的偏移
**说明:**由于用户空间不能直接访问内核空间的内存,所以借助
copy_from_user(),用户空间缓冲区到内核空间的复制
copy_to_user(),内核空间到用户空间缓冲区的复制
当要复制的内存是简单类型时, 如char , int , long , 等,可以用下列函数进行操作:
int val; /*内核空间整型变量*/
...
get_user(val,(int*)arg); /*用户到内核,arg是用户空间的地址*/
...
put_user(val,(int*)arg); /*内核到用户,arg时用户空间的地址*/
内核空间在访问用户空间的缓冲区时,需要先检查其合法性,通过access_ok(type,addr,size) 判断,来确定传入的缓冲区的确属于用户空间.如果不做这项检查,操作系统内核很容易被攻击,就会存在安全漏洞.
此函数检查用户空间中的内存块是否可用。如果可用,则返回真(非0值),否则返回假 (0) 。例:
static ssize_t read_port(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
unsigned long i = *ppos;
char __user *tmp = buf;
if (!access_ok(VERIFY_WRITE,buf,count))
return -EFAULT;
while (count-- > 0 && i<65536){
if (__put_user(inb(i),tmp) < 0)
return -EFAULT;
i++;
tmp++;
}
*ppos = i;
return tmp-buf;
}
__put_user()与put_user()的区别:后者会自动进行access_ok()检查.上述代码用到__put_user()是因为我们手动做access_ok()检查.
get_user()和__get_user()区别相似.
copy_from_user()和copy_to_user()内部也进行了access_ok()检查.
(3)I/O控制函数
long xxx_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
...
switch (cmd){
case XXX_CMD1:
...
break;
case XXX_CMD2:
...
break;
default:
/*不能支持的命令*/
return -ENOTTY;
}
return 0;
}
cmd参数为事先定义的I/O控制命令,arg为对应于该命令的参数.例:
SET_BAUDRATE是设置波特率的命令
arg就应该是波特率值.
2.字符设备驱动中,需要定义一个file_operations实例,并将具体设备驱动函数赋给file_operations的成员:
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.read = xxx_read,
.write = xxx_write,
.unlocked_ioctl = xxx_ioctl,
...
};
以上代码中xxx_fops与day01中模块加载函数的
cdev_init(&xxx_dev.cdev,&xxx_fops)函数进行与cdev的连接.