LDD3学习笔记(4):字符驱动2

1、重要的数据结构

注册设备编号仅仅是驱动代码需要完成的任务之一,还有很多基础性的驱动操作需要驱动代码来完成,这里有3个重要的内核数据结构需要了解一下分别是:file_operations、file、inode。

1.1、文件操作

File_operation结构的功能是建立一个字符驱动与设备编号的连接。通常结构中的每个成员必须指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作置为NULL。

__user这种注解是一种文档形式,表明一个指针是一个不能被直接解除引用的用户空间地址,对于正常的编译没有效果。

Struct module* owner

指向拥有这个结构的模块的指针。

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

Llseek用来改变文件中当前读/写位置,并且新位置作为返回值,错误返回负值。

Ssize_t(*read) (struct file* , char __user* , size_t , loff_t*);

用来从设备中获取数据,如果这个方法是NULL,将导致系统调用-EINVAL失败,返回非负值代笔成功读取的字节数(返回值是一个signed size 类型,通常是目标平台本地的整数类型)。

Ssize_t (*aio_read) (struct kiocb * , char __user* , size_t , loff_t);

初始化一个异步读,肯恩在函数返回前不结束的读操作,如果这个方法是NULL,所有的操作将会由read代替进行(同步读)。

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

发送数据给设备,如果这个方法NULL,将返回-EINVAL给调用这个方法的程序,如果非负,返回值代表成功写的字节数。

Ssize_t(*aio_write)(struct kiocb* , const char __user* , size_t , loff_f *);

初始化设备上的一个异步写。

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

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

Unsigned int (*poll)(struct file* , struct poll_table_struct *);

Poll方法为3个系统调用的后端:poll epoll select,它们都用作查询一个或多个文件描述符的读和写是否会拥塞。Poll应当方法返回一个位掩码指示读或写是否拥塞,并提供给内核信息,使调用进程睡眠直到I/O变为可能,如果一个驱动的poll方法为NULL,则设备被假定为不阻塞可读可写。

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

Ioctl系统调用提供了发出设备特定命令的方法,如果设备不提供ioctl,对于任何事先未定义的请求,系统调用都会返回一个错误。

Int (*mmap)(struct file * , struct vm_area_struct *);

Mmap用来请求将设备内存映射到进程的地址空间。如果这个方法是NULL,mmap系统调用返回-ENODEY。

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

这常常是对设备文件进行的第一个操作,不要求驱动声明一个对应的方法,如果这个方法是NULL,设备打开一只成功,但是你的驱动不会得到通知。

Int (*flush)(struct file *);

Flush操作在进程关闭它的设备文件描述符的拷贝时调用,它应当执行设备的任何未完成的操作。

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

在文件结构被释放时引用这个操作,如同open,release可以为NULL。

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

这个方法是fsync系统调用的后端,用户调用来刷新任何挂着的数据,如果这个指针是NULL,系统返回-EINVAL。

Int (*aio_fsync) (struct kiocb* , int);

Fsync方法的异步版本。

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

这个操作用来通知设备它的FASYNC标志发生改变,如果驱动不支持异步通知,这个成员可以使NULL。

Int(*lock)(struct file * , int , struct file_lock*);

Lock发放用来实现文件加锁,加锁对常规文件时必不可少的特性,但是设备驱动几乎从不实现它。

Ssize_t(*readv)(struct file* , const struct iovec* , unsigned long , loff_f*);

Ssize_t(*writev)(struct file* , const struct iovec* , unsigned long , loff_t*);

这个方法实现发散/汇聚读和写操作,应用程序偶尔需要做一个包含多个内存区的单个读或写操作,这个系统调用允许他们这样做而不必对数据进行额外拷贝,如果这些函数指针为NULL,read和write方法被调用(可能多于一次)。

Ssize_t(*sendfile)(struct file* , loff_t* , size_t,read_actor_t,void *);

这个方法实现sendfile系统调用的读,使用最少的拷贝从一个文件描述符搬移数据到另一个,设备驱动常常使sendfile为NULL。

Ssize_t(*sendpage)(struct file* , struct page* , int ,size_t ,loff_t* , int);

Sendpage是sendfile的另一半,它由内核调用来发送数据,一次一页,到对应的文件,设备驱动通常不实现sendpage。

Unsigned long(*get_unmapped_area)(struct file * , unsigned long ,unsigned long ,unsigned long);

这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存段中,大部分驱动可以置这个方法为NULL。

Int (*check_flags)(int);

这个方法允许模块检查传递给fnctl(F_SETEL...)调用的标志。

Int(*dir_notify)(struct file* , unsigned long);

这个方法在应用程序使用fnctl来请求目录改变通知时调用,只对文件系统有用,驱动不需要实现dir_notify。

Scull设备驱动只实现最重要的设备方法,它的file_operations结构是如下初始化的:

Struct file_operations scull_fops={.owner = THIS_MODULE,

.llseek = scull_llseek,

.read = scull_read,

.write = scull_write,

.ioctl = scull_ioctl,

.open = scull_open,

,release = scull_release,

};

1.2、文件结构

Struct file是设备驱动中第二个重要的数据结构,注意file与用户空间程序的FILE指针没有任何关系,一个FILE定义在C库中,从不出现在内核代码中,一个struct file是内核结构,从不出现在用户程序中。

Struct file代表一个打开的文件,在内核空间,系统中每个打开的文件都有一个关联的struct file ,在内核open时创建,并传递给操作文件的函数,直到最后关闭,然后内核释放这个数据结构。

在内核源码中,struct file的指针常常称为file或者filp(file pointer),为避免混淆,以后file指结构,filp指结构指针。下面是file结构的一些重要成员:

Mode_t f_mode:文件模式

确定文件时可读或是可写,通过位FMODE_READ和FMODE_WRITE确定。

Loff_t f_pos:当前读写位置

Loff_t在所有平台都是64位的,驱动如果需要知道文件的当前位置,可以读这个值。

Unsigned int f_flags:文件标志

如O_RDONLY O_NONBLOCK O_SYNC,驱动应该检查O_NONBLOCK标志来查看是否是请求非阻塞操作,其他的很少使用。

Struct file_operations *f_op:和文件关联的操作

Void *private_data:open系统调用设置这个指针为NULL,在为驱动调用open方法之前,你可自由使用这个成员或者忽略它。

Struct dentry* f_dentry:关联到文件的目录入口结构。

1.3、inode结构

Inode结构由内核在内部用来表示文件。

有用的成员:

Dev_t i_rdev:对于代表设备文件的节点,这个成员包含实际的设备编号。

Struct cdev* i_cdev:

Struct cdev是内核的内部结构,代表字符设备。

从一个inode中获取主次编号的宏:

Unsigned int iminor(struct inode * inode);

Unsigned int imajor(struct inode* inode);

时间: 2024-10-02 18:42:20

LDD3学习笔记(4):字符驱动2的相关文章

LDD3学习笔记(6):字符驱动4

  1.快速参考 本章介绍了下面符号和头文件. struct file_operations 和 struct file 中的成员的列表这里不重复了. #include <linux/types.h> dev_t  dev_t 是用来在内核里代表设备号的类型. int MAJOR(dev_t dev); int MINOR(dev_t dev); 从设备编号中抽取主次编号的宏. dev_t MKDEV(unsigned int major, unsigned int minor); 从主次编号

LDD3学习笔记(3):字符驱动1

1.scull( Simple Character Utility for Loading  Localities)的设计 Scull是一个字符驱动,它操作一块内存区域,就好像它是一个设备,因此在以下的介绍中我们可以互换的使用设备和scull操作的内存区. 编写驱动的第一步是定义驱动将要提供给用户的功能. 模块实现的每种设备都被引用为一种类型. Scull源码实现: scull0到scull3四个设备,每个都由一个全局永久的内存区组成,全局表示如果设备被多次打开设备中含有的数据将由所有打开它的文

LDD3学习笔记(5):字符驱动3

  1.字符设备注册 在运行时获得一个独立的cdev结构的代码: Struct cdev* my_cdev=cdev_alloc(); My_codev->ops=&my_fops; 将cdev结构嵌入自己设备特定的结构: Void cdev_init(struct cdev* cdev , struct file_operations* fops); Cdev结构建立后,告诉内核: Int cdev_add(struct cdev*dev ,dev_t num,unsigned int c

LDD3学习笔记(9):高级字符驱动操作

 1.ioctl接口 ioctl 驱动方法有和用户空间版本不同的原型: int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); 2.阻塞I/O 阻塞进程,使它进入睡眠直到请求可继续. 当一个进程被置为睡眠,它被标识为处于一个特殊的状态并且从调度器的运行队列中去除,直到发生某些事情改变了那个状态.一个睡着的进程将不被任何CPU调度,因此不会运行,一个睡着的进程已被搁置到系

LDD3学习笔记(1):设备驱动简介

 1.几乎每个系统操作都被映射到具体的物理设备上,而任何设备的控制操作都由特定于要寻址的相关代码来进行,这些代码成为设备驱动. 2.linux可以在运行时扩展由内核提供的特性,既可以在系统运行时增加内核的功能(也可以删除). 3.每块可以在运行时添加到内核的代码称为一个模块,每个模块由目标代码组成(既功能的实现). 4.linux将设备分为三种基本类型:字符设备.块设备.网络设备.典型的字符设备如:/dev/tty1(串口),块设备如:/dev/sda(磁盘),网络设备如:eth0(网卡),在这

LDD3学习笔记(20):网络驱动

  #include <linux/netdevice.h> 定义 struct net_device 和 struct net_device_stats 的头文件, 包含了几个其他网络驱动需要的头 文件. struct net_device *alloc_netdev(int sizeof_priv, char *name, void (*setup)(struct net_device *); struct net_device *alloc_etherdev(int sizeof_pri

LDD3学习笔记(21):tty驱动

  #include <linux/tty_driver.h> 头文件, 包含 struct tty_driver 的定义和声明一些在这个结构中的不同的标志. #include <linux/tty.h> 头文件, 包含 tty_struct 结构的定义和几个不同的宏定义来易于存取 struct termios 的成员的单个值. 它还含有 tty 驱动核心的函数声明. #include <linux/tty_flip.h> 头文件, 包含几个 tty flip 缓冲内联

LDD3学习笔记(15):PCI驱动

 #include <linux/pci.h> 包含 PCI 寄存器的符号名和几个供应商和设备 ID 值的头文件. struct pci_dev; 表示内核中一个 PCI 设备的结构. struct pci_driver; 代表一个 PCI 驱动的结构. 所有的 PCI 驱动必须定义这个. struct pci_device_id; 描述这个驱动支持的 PCI 设备类型的结构. int pci_register_driver(struct pci_driver *drv); int pci_m

LDD3学习笔记(16):USB驱动

  #include <linux/usb.h> 所有和 USB 相关的头文件. 它必须被所有的 USB 设备驱动包含. struct usb_driver; 描述 USB 驱动的结构. struct usb_device_id; 描述这个驱动支持的 USB 设备的结构. int usb_register(struct usb_driver *d); 用来从USB核心注册和注销一个 USB 驱动的函数. struct usb_device *interface_to_usbdev(struct