Linux 应用程序如何操作底层硬件
·
Linux 应用程序如何操作底层硬件
本文梳理,在 Linux 系统下应用程序是如何一步步操作底层硬件设备的。了解这一流程,是理解 Linux 驱动开发的第一步。
01 用户空间与内核空间的隔离
在 Linux 系统中,出于对系统稳定性和安全性的保护,程序的运行空间被严格划分为了两部分:
- 应用程序:运行在 用户空间(User Space)
- Linux 驱动:属于内核的一部分,运行在 内核空间(Kernel Space)
⚠️ 用户空间是不能直接对内核空间进行操作的。
因此,应用程序必须通过 「系统调用」(System Call),实现从用户空间「陷入」(Trap)到内核空间,从而间接控制底层硬件。
02 file_operations 结构体
在 Linux 内核源码的 include/linux/fs.h 文件中,定义了一个至关重要的结构体:file_operations。
💡 重点:它包含了所有的 Linux 内核驱动操作函数。可以说,它是连接应用层系统调用和底层驱动程序的核心「桥梁」。

核心源码剖析
struct file_operations {
// 拥有该结构的模块的指针,一般为 THIS_MODULES
struct module *owner;
// 用来修改文件当前的读写位置
loff_t (*llseek) (struct file *, loff_t, int);
// 从设备中同步读取数据
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
// 向设备写入数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
// 初始化一个异步的读取操作
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
// 初始化一个异步的写入操作
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
// 仅用于读取目录,对于设备文件该字段为 NULL
int (*readdir) (struct file *, void *, filldir_t);
// 轮询函数,判断目前是否可以进行非阻塞的读写或写入
unsigned int (*poll) (struct file *, struct poll_table_struct *);
// 执行设备 I/O 控制命令
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
// 不使用 BLK 文件系统时,使用此函数指针代替 ioctl
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
// 在 64 位系统上,32 位的 ioctl 调用将使用此函数指针代替
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
// 用于请求将设备内存映射到进程地址空间
int (*mmap) (struct file *, struct vm_area_struct *);
// 打开设备
int (*open) (struct inode *, struct file *);
// 刷新待处理的数据
int (*flush) (struct file *, fl_owner_t id);
// 关闭设备
int (*release) (struct inode *, struct file *);
// 同步/异步刷新待处理的数据
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
// 通知设备 FASYNC 标志发生变化
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
03 核心理念:一切皆文件
最基础的设备:字符设备
在 Linux 驱动硬件层中,最基本、最常见的就是字符设备(如常见的按键、IIC、SPI、LCD 屏幕等)。
工作特点:数据按字节流的方式进行读写(一个字节接着一个字节,严格区分先后顺序)。
Linux 核心哲学:「一切皆文件」
当与硬件相对应的驱动程序加载成功后,系统就会在 /dev 目录下自动生成对应的设备文件(例如 /dev/xxx)。
对于上层的业务应用程序来说:
👉 操作这个文件,就等同于操作底层的硬件。
举个例子:
应用程序使用 open 操作文件,使用 write 向文件写入数据(底层触发硬件动作,如点亮灯),使用 read 读取状态。底层硬件的复杂逻辑对应用层完全透明。
定期更新一些简单的技术干货或者开发踩坑历程,欢迎大家关注我~(/ω\)
更多推荐




所有评论(0)