linux一些重要数据结构
如同你想象的, 注册设备编号仅仅是驱动代码必须进行的诸多任务中的第一个. 我们将很 快看到其他重要的驱动组件, 但首先需要涉及一个别的. 大部分的基础性的驱动操作包括 3 个重要的内核数据结构, 称为 file_operations, file, 和 inode. 需要对这些结构的 基本了解才能够做大量感兴趣的事情, 因此我们现在在进入如何实现基础性驱动操作的细 节之前, 会快速查看每一个.
3.3.1. 文件操作
init 脚本 scull.init 不在命令行中接受驱动选项, 但是它支持一个配置文件, 因为它被设计来在启动和关机时自
动使用.
到现在, 我们已经保留了一些设备编号给我们使用, 但是我们还没有连接任何我们设备操 作到这些编号上. file_operation 结构是一个字符驱动如何建立这个连接. 这个结构, 定 义在 <linux/fs.h>, 是一个函数指针的集合. 每个打开文件(内部用一个 file 结构来代 表, 稍后我们会查看)与它自身的函数集合相关连( 通过包含一个称为 f_op 的成员, 它指 向一个 file_operations 结构). 这些操作大部分负责实现系统调用, 因此, 命名为 open, read, 等等. 我们可以认为文件是一个"对象"并且其上的函数操作称为它的"方法", 使用 面向对象编程的术语来表示一个对象声明的用来操作对象的动作. 这是我们在 Linux 内核 中看到的第一个面向对象编程的现象, 后续章中我们会看到更多.
传统上, 一个 file_operation 结构或者其一个指针称为 fops( 或者它的一些变体). 结 构中的每个成员必须指向驱动中的函数, 这些函数实现一个特别的操作, 或者对于不支持 的操作留置为 NULL. 当指定为 NULL 指针时内核的确切的行为是每个函数不同的, 如同本 节后面的列表所示.
下面的列表介绍了一个应用程序能够在设备上调用的所有操作. 我们已经试图保持列表简 短, 这样它可作为一个参考, 只是总结每个操作和在 NULL 指针使用时的缺省内核行为.
在你通读 file_operations 方法的列表时, 你会注意到不少参数包含字串 user. 这种 注解是一种文档形式, 注意, 一个指针是一个不能被直接解引用的用户空间地址. 对于正 常的编译, user 没有效果, 但是它可被外部检查软件使用来找出对用户空间地址的错误 使用.
本章剩下的部分, 在描述一些其他重要数据结构后, 解释了最重要操作的角色并且给了提 示, 告诫和真实代码例子. 我们推迟讨论更复杂的操作到后面章节, 因为我们还不准备深 入如内存管理, 阻塞操作, 和异步通知.
struct module *owner
第一个 file_operations 成员根本不是一个操作; 它是一个指向拥有这个结构的模 块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间 中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏.
loff_t (*llseek) (struct file *, loff_t, int);
llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32 位平台上也至少 64 位宽. 错 误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预 知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述).
ssize_t (*read) (struct file *, char user *, size_t, loff_t *);
用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 - EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返 回值是一个 "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 返回给调用 write 系统调用的程序. 如果 非负, 返回值代表成功写的字节数.
ssize_t (*aio_write)(struct kiocb *, const char user *,
size_t, loff_t *); 初始化设备上的一个异步写.
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 命令被内核识别而不必引用 fops 表. 如果设 备不提供 ioctl 方法,
对于任何未事先定义的请求(-ENOTTY, "设备无这样的 ioctl"), 系统调用返回一个错误.
int (*mmap)
(struct file *, struct vm_area_struct *);
mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系 统调用返回 -ENODEV.
int (*open)
(struct inode *, struct file *);
尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如 果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.
int (*flush) (struct file *);
flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待) 设备的任何未完成的操作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 当 前,
flush 在很少驱动中使用; SCSI 磁带驱动使用它, 例如, 为确保所有写的数据 在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的 请求.
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 标志的改变. 异步通知是一个高级的主题, 在 第 6 章中描述. 这个成员可以是
NULL 如果驱动不支持异步通知.
int (*lock)
(struct file *, int, struct file_lock *);
lock 方法用来实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几 乎从不实现它.
ssize_t
(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
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 系统调用的读, 使用最少的拷贝从一个文件描述符搬移数 据到另一个. 例如, 它被一个需要发送文件内容到一个网络连接的 web 服务器使用. 设备驱动常常使 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, unsigned long);
这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存 段中. 这个任务通常由内存管理代码进行; 这个方法存在为了使驱动能强制特殊设 备可能有的任何的对齐请求. 大部分驱动可以置这个方法为 NULL.[10]10
int
(*check_flags)(int)
这个方法允许模块检查传递给 fnctl(F_SETFL...) 调用的标志. int
(*dir_notify)(struct file *, unsigned long);
这个方法在应用程序使用 fcntl 来请求目录改变通知时调用. 只对文件系统有用; 驱动不需要实现 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,
};
这个声明使用标准的 C 标记式结构初始化语法. 这个语法是首选的, 因为它使驱动在结构 定义的改变之间更加可移植, 并且, 有争议地, 使代码更加紧凑和可读. 标记式初始化允 许结构成员重新排序; 在某种情况下, 真实的性能提高已经实现, 通过安放经常使用的成 员的指针在相同硬件高速存储行中.
linux一些重要数据结构的更多相关文章
- 牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结 转载
基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...
- 【转】牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...
- linux VFS 内核数据结构
<strong>简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp.</strong> 文件描述 ...
- 面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会 ...
- 【转】[IT综合面试]牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结
感谢IT面试群 S-北京-陈磊 的整理分享. 基础篇:操作系统.计算机网络.设计模式 提高篇:WIN32.MFC与Linux 算法篇:算法与数据结构 一:操作系 ...
- Linux物理内存相关数据结构
节点:pg_data_t typedef struct pglist_data { zone_t node_zones[MAX_NR_ZONES]; zonelist_t node_zonelists ...
- Linux USB驱动数据结构
struct usb_ctrlrequest { __u8 bRequestType; __u8 bRequest; __le16 wValue; __le16 wIndex; ...
- linux 内核数据结构之 avl树.
转载: http://blog.csdn.net/programmingring/article/details/37969745 https://zh.wikipedia.org/wiki/AVL% ...
- Linux 内核进程管理之进程ID
Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构 ...
随机推荐
- pytest 用 @pytest.mark.usefixtures("fixtureName")装饰类,可以让执行每个case前,都执行一遍指定的fixture
conftest.py import pytest import uuid @pytest.fixture() def declass(): print("declass:"+st ...
- bnd.bnd属性文件格式
1.Header以大写字母开头 Bundle-Name: StoreAdminProductsTool 2.Instruction以-和小写字母开头 -sources: true 3. Macro形式 ...
- Intellij IDEA设置忽略部分类编译错误
有些时候我们的项目中有些错误,但这些错误并不影响项目的整体运行(或许是没有使用到),默认情况下idea是无法通过编译的,因此也就无法部署运行,要达到正确运行项目的目的需要作一些设置才行. 设置Inte ...
- 网络流24题 骑士共存(DCOJ8023)
题目描述 在一个 n*n 个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘上某些方格设置了障碍,骑士不得进入. 对于给定的 n*n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以 ...
- hdu5441 并查集 长春网赛
对于每次询问的大的值,都是从小的值开始的,那就从小到大处理,省去很多时间,并且秩序遍历一遍m; 这题cin容易超时,scanf明显快很多很多.G++又比C++快; //这代码scanf400+,cin ...
- Uva 10334
UVa 10334 这道题几乎和UVa 495是一样的. #include<iostream> #include<cstdio> #define mod 1000000 usi ...
- 使用 EnumWindows 找到满足你要求的窗口 - walterlv
原文:使用 EnumWindows 找到满足你要求的窗口 - walterlv 使用 EnumWindows 找到满足你要求的窗口 2019-04-30 13:11 在 Windows 应用开发中,如 ...
- Vue电商后台管理系统项目第2天-首页添加表格动态渲染数据&分页
0x01.使用Github学习的姿势 基于昨天的内容,今天的内容需要添加几个单文件组件,路由文件也需要做相应的增加,今天重点记录使用Element-UI中的表格组件实现数据动态渲染的实现流程和分页功能 ...
- java基础-内存分配
1.java运行时的数据区:程序计数器.方法区.虚拟机栈.本地方法栈.堆 ①.程序计数器:一块较小的内存空间,可看作当前线程所执行的字节码的行号指示器 ②.java虚拟机栈:与程序计数器一样,也是线程 ...
- Oracle包utl_inaddr
作用:用于取得局域网或Internet环境中的主机名和IP地址. 1.utl_inaddr.get_host_address 环境中IP地址 如果查询失败,则提示系统错误 查询www.qq.com的I ...