设备驱动程序设计中相关问题分析

1. file_operations结构体的概述

Linux驱动程序中最重要的涉及3个重要的内核数据结构,分别为file_operations,file和inode。在linux中inode结构用于表示文件,而file结构则表示打开的文件的描述,因为对于单个文件而言可能会有许多个表示打开的文件的描述符,因而就可能会的对应有多个file结构,但是都指向单个inode结构。

在系统内部,I/O设备的存取操作通过特定的的入口来进行,而这组特定的入口由驱动程序来提供的。通常这组设备驱动的接口是由结构体file_operations向系统说明的,结构体file_operations是定义在include/linux/fs.h中的。

structfile_operations {

    struct module *owner;          //防止模块在使用的时候被卸载

    loff_t (*llseek) (struct file *, loff_t,int);

    ssize_t (*read) (struct file *, char *,size_t, loff_t *);

    ssize_t (*write) (struct file *, const char*, size_t, loff_t *);

    int (*readdir) (struct file *, void *,filldir_t);

    unsigned int (*poll) (struct file *, structpoll_table_struct *);

    int (*ioctl) (struct inode *, struct file*, unsigned int, unsigned long);

    int (*mmap) (struct file *, structvm_area_struct *);

    int (*open) (struct inode *, struct file*);

    int (*flush) (struct file *);

    int (*release) (struct inode *, struct file*);

    int (*fsync) (struct file *, struct dentry*, int datasync);

    int (*fasync) (int, struct file *, int);

    int (*lock) (struct file *, int, structfile_lock *);

    ssize_t (*readv) (struct file *, conststruct iovec *, unsigned long, loff_t *);

    ssize_t (*writev) (struct file *, conststruct iovec *, unsigned long, loff_t *);

    ssize_t (*sendpage) (struct file *, structpage *, int, size_t, loff_t *, int);

    unsigned long (*get_unmapped_area)(structfile *, unsigned long, unsigned long, unsigned long, unsigned long);

};

详细分析结构体中比较重要的几个方法:

struct module *owner;

它是一个指向拥有这个结构的模块的指针.这个成员用来在它的操作还在被使用时阻止模块被卸载。

loff_t (*llseek) (struct file *, loff_t, int);

      llseek方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值。指针参数filp为进行读取信息的目标文件结构体指针; loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示。

ssize_t (*read) (struct file * filp, char * , size_t,loff_t * );

这个函数用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败.一个非负返回值代表了成功读取的字节数( 返回值是一个"signed size" 类型, 常常是目标平台本地的整数类型)。

ssize_t (*write) (struct file*, const char * ,size_t,loff_t *);

发送数据给设备. 如果 NULL,-EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.

(注:这个操作和上面的对文件进行读的操作均为阻塞操作)

int (*readdir) (struct file * ,void *, filldir_t);

对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用。

int (*ioctl) (struct inode*, struct file*, unsignedint, unsigned long );

ioctl 系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外, 几个 ioctl 命令被内核识别而不必引用 fops 表.如果设备不提供 ioctl 方法, 对于任何未事先定义的请求(-ENOTTY,"设备无这样的ioctl"), 系统调用返回一个错误.

int (*open) (struct inode * inode , struct file * filp) ;

这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知。与open()函数对应的是release()函数。

int (*release) (struct inode *, struct file *);

release ()函数当最后一个打开设备的用户进程执行close()系统调用的时候,内核将调用驱动程序release()函数:

void release(struct inode inode,struct file*file),release函数的主要任务是清理未结束的输入输出操作,释放资源,用户自定义排他标志的复位等。在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.

 

2.驱动编写的框架

设备驱动程序的主体一般是写好了的。嵌入式开发人员要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式,也可以理解为静态加载与动态加载的方式。

在这里可大致将驱动编写分为以下几个部分:

u      驱动程序的注册和注销;

u      设备的打开和释放;

u      设备的读写操作;

u      设备的控制操作;

u      设备的中断和轮询处理。

 

3.模块的概念

模块:内核本身是很庞大的一个结构,需要的组件很多。编译内核时,用户 可以把所有的代码编译进内核,但是这样会引起两个问题:一是内核过大;二是 当需要添加或者删除内核时,需要重新再编译内核,而重新编译内核是一个是否花费时间的工作,所以有了内核模块的概念。 模块并不编译到内核中,编译后存放在指定的目录,当需要使用时动态加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值