
于此止?:个人主页
本文选自专栏: 《Linux篇》
❄️个人专栏传送门: 《QT百日筑基篇》 《算法篇》
《C++与STL剖析篇》 《数据结构篇》 《Python基础篇》《嵌入式砺剑录》
⭐️纵有狂风拔地起,我亦乘风破万里⭐️

Linux下一切皆文件
前言:本文是一篇短文,主要是介绍我们对应的怎么去理解Linux一切皆文件。
我们在之前学Linux中听说过一切皆文件,但是我们还是直局限于我们的概念,当时我们只是记住即可并不理解它们的意思,下面我们就来详细的理解一切接文件把。
理解Linux一切皆文件
首先在我们经常使用的Windows下我们是文件的在我们的Linux下还是软件,但是有些在windows不是文件的东西比如(磁盘、网卡、键盘……)他在Linux下也抽象成立软件,我们可以通过访问文件的信息来获取他们的信息。
这样做的好处是我们设计一台API和开发工具就能控制我们这些文件,即可调取Linux下的大部分资源。在Linux中几乎很多的事情都需要读(读文件…)的操作我们都是使用read来进行读取。
我们之前在讲解进程的时候看过下面的OS操作系统的图片。我们在操作对应的硬件的时候也是将我们的硬件看作软件, 那么该怎么进行查看呢?我们下面来进行解说。

首先我们底层会有一个个硬件,我们在硬件之上会通过一些驱动进行控制。我们这里将我们对应的驱动就是对于底层进行IO。将对应的IO进行对我们硬件进行输出。


我们可以查看一下我们对应的内核源代码中的struct_file
我们有对应的inode信息, 也就是我们文件的属性,还有我们对应的文件方法(struct file_operation)。还有我们对应的引用计数和权限。
struct file {
…
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
…
atomic_long_t f_count; // 表示打开文件的引用计数,如果有多个文件指针指向它,就会增加f_count的值。
unsigned int f_flags; // 表示打开文件的权限
fmode_t f_mode; // 设置对文件的访问模式,例如:只读,只写等。所有的标志在头文件<fcntl.h> 中定义
loff_t f_pos; // 表示当前读写文件的位置
…
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
现在我们看一下我们的struct operation的内核代码
file_operation存在我们的一些程序的方法,我们我面可能通过这些方法将我们的代码进行刷新到 我们的驱动程序中。
struct file_operations {
struct module *owner;
//指向拥有该模块的指针;
loff_t (*llseek) (struct file *, loff_t, int);
//llseek 方法用作改变文件中的当前读/写位置,并且新位置作为(正的)返回值.
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型,常常是目标平台本地的整数类型).
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.
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);
//初始化设备上的一个异步写.
int (*readdir) (struct file *, void *, filldir_t);
//对于设备文件这个成员应当为 NULL; 它用来读取目录,并且仅对**文件系统**有用.
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
//mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.
int (*open) (struct inode *, struct file *);
//打开一个文件
int (*flush) (struct file *, fl_owner_t id);
//flush 操作在进程关闭它的设备文件描述符的拷贝时调用;
int (*release) (struct inode *, struct file *);
//在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.
int (*fsync) (struct file *, struct dentry *, int datasync);
//用户调用来刷新任何挂着的数据.
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
//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 **);
};
上图中的外设,每个设备都可以有自己的 read、write,但一定是对应着不同的操作方法!!但通过 struct file 下 file_operation 中的各种函数回调,让我们开发者只用 file 便可调取 Linux 系统中绝大部分的资源!!这便是 “linux 下一切皆文件” 的核心理解。






