当前位置: 首页 > news >正文

【linux仓库】万物至简的设计典范:如何用‘文件’这一个概念操纵整个Linux世界?

🌟 各位看官好,我是egoist2023!

🌍 Linux == Linux is not Unix !

🚀 今天来学习一切皆文件的相关知识。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦!

目录

书接上文

前言 

一切皆文件

刨根问底

总结


书接上文

本文深入剖析了Linux文件描述符(FD)的核心机制。我们从open系统调用的返回值切入,揭示了其“数组下标”的本质特性。通过追溯进程task_struct中的files_struct结构,我们阐明了0、1、2号FD被固定分配为标准输入、输出、错误的规则,并验证了C库FILE结构体对FD的封装关系。

在此基础上,我们系统讲解了FD的分配规则重定向的底层实现(基于dup2系统调用),以及父子进程间FD的继承机制

然而,这一切精巧的设计,都服务于一个更为宏大和深刻的Linux设计哲学——“一切皆文件”

FD,正是这一哲学得以实现的基石与枢纽。它不仅仅是一个用于访问普通文件的句柄,更是一个统一的抽象接口。正是凭借FD这个“万能手柄”,Linux才能将形态各异的外部设备——无论是磁盘、键盘、显示器,还是网络套接字、管道——都抽象为一种可以统一进行读写(read/write)操作的对象。

接下来,我们将超越普通文件的范畴,深入虚拟文件系统(VFS) 层,探索Linux是如何用“文件”这同一个概念,来统一抽象万物的。我们将看到,正是FD机制的存在,才使得“一切皆文件”从一句口号,变成了一个强大而优雅的现实。

前言 

OS要不要管理硬件呢?它是软硬件资源的管理者,要管理,那么该如何管理呢?

先描述,再组织!!!

struct device
{int type;int status;...struct list_head node;
}

一切皆文件

首先,在windows中是⽂件的东西,它们在linux中也是⽂件;其次⼀些在windows中不是⽂件的东

西,⽐如进程、磁盘、显⽰器、键盘这样硬件设备也被抽象成了⽂件,你可以使⽤访问⽂件的⽅法访问它们获得信息;甚⾄管道,也是⽂件;将来我们要学习⽹络编程中的socket(套接字)这样的东西,使⽤的接⼝跟⽂件接⼝也是⼀致的。

这样做最明显的好处是,开发者仅需要使⽤⼀套 API 和开发⼯具,即可调取 Linux 系统中绝⼤部分的资源。举个简单的例⼦,Linux 中⼏乎所有读(读⽂件,读系统状态,读PIPE)的操作都可以⽤

read 函数来进行;几乎所有更改(更改⽂件,更改系统参数,写 PIPE)的操作都可以⽤ write 函

数来进⾏。

之前我们讲过,当打开⼀个⽂件时,操作系统为了管理所打开的⽂件,都会为这个⽂件创建⼀个file结构体,该结构体定义在 /usr/src/kernels/3.10.0-1160.71.1.el7.x86_64/include/linux/fs.h 下,以下展示了该结构部分我们关系的内容:

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 file 中的 f_op 指针指向了⼀个 file_operations 结构体,这个结构体中的成员除了struct module* owner 其余都是函数指针。该结构和 struct file 都在fs.h下。

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 **);
};

file_operation 就是把系统调⽤和驱动程序关联起来的关键数据结构,这个结构的每⼀个成员都

对应着⼀个系统调用。读取 file_operation 中相应的函数指针,接着把控制权转交给函数,从⽽

完成了Linux设备驱动程序的⼯作。

上图中的外设,每个设备都可以有⾃⼰的read、write,但⼀定是对应着不同的操作⽅法!!但通过struct file 下 file_operation 中的各种函数回调,让我们开发者只⽤file便可调取 Linux 系统中绝⼤部分的资源!!这便是“linux下⼀切皆⽂件”的核心理解。

刨根问底

当我们打开⽂件时,操作系统在内存中要创建相应的数据结构来描述⽬标⽂件。⽽进程执⾏open系统调⽤,所以必须让进程和⽂件关联起来。每个进程都有⼀个指针*files, 指向⼀张表files_struct,该表最重要的部分就是包含⼀个指针数组,每个元素都是⼀个指向打开⽂件的指针!

Linux中,打开文件,要为我们创建struct fle,三个核心:
1.文件属性
2.文件内核缓冲区
3.底层设备文件的操作表(方法集)

根据上面所讲:

struct file 中还有一个 f_op 指针,它指向⼀个 file_operations 结构体。这个file_operations即是一个方法集,如read、write等等。

而每个设备都提供了自身需求的对应方法集的实现。

这样便做到了在上层部分函数接口都是统一的,而在下层部分每个函数接口的实现方法都是不同的。

而上层不就是C++中实现多态的基类吗?下层不就是C++中实现多态的派生类。

输出结论:一切皆文件!是站在进程的视角,在struct file结构体之上,看待文件的视角,strcutfile的方法集是一样的,但访问方式是不同的,因为不同的设备有不同的实现方法。

总结

本文深入解析了Linux"一切皆文件"的设计哲学,重点剖析了文件描述符(FD)的核心机制。从进程task_struct中的files_struct结构出发,阐述了FD作为"数组下标"的本质特性及其分配规则。通过分析struct file和file_operations结构体,揭示了Linux如何通过统一接口(如read/write)抽象各类设备资源,实现不同设备的差异化操作。关键点在于:上层提供统一接口,下层由各设备实现具体操作,类似C++的多态机制,使进程能以统一视角访问异构资源。这种设计极大简化了开发,使开发者仅需一套API即可操作绝大多数系统资源。

 


文章转载自:

http://IRY3Hrh2.sxwfx.cn
http://sV9NMDYU.sxwfx.cn
http://Wqqo8dIP.sxwfx.cn
http://HEHu1A2F.sxwfx.cn
http://2h9yfLHP.sxwfx.cn
http://L0wx4NoV.sxwfx.cn
http://TovmDzR6.sxwfx.cn
http://3MGNzN2o.sxwfx.cn
http://joi6AgvV.sxwfx.cn
http://HnAOt7nG.sxwfx.cn
http://EIiOchHX.sxwfx.cn
http://LMwvMjol.sxwfx.cn
http://TH7i7NVk.sxwfx.cn
http://JWNmkuhf.sxwfx.cn
http://NbpP1YZ8.sxwfx.cn
http://qMuaI4lj.sxwfx.cn
http://Dtv27fFu.sxwfx.cn
http://o0TKeJ5T.sxwfx.cn
http://A7fC4ZZL.sxwfx.cn
http://tPVIABy3.sxwfx.cn
http://POYicq4P.sxwfx.cn
http://CDpEC0f8.sxwfx.cn
http://i5o3zfJC.sxwfx.cn
http://QZywAVfZ.sxwfx.cn
http://xl6uXRxh.sxwfx.cn
http://XdM7tiaM.sxwfx.cn
http://PwEWWeY9.sxwfx.cn
http://Zjc7P4JP.sxwfx.cn
http://A1qWjnc2.sxwfx.cn
http://8F9g0nnV.sxwfx.cn
http://www.dtcms.com/a/362404.html

相关文章:

  • 【数据分享】土地利用shp数据分享-内蒙古
  • Python应用——ffmpeg处理音视频的常见场景
  • 谷歌AdSense套利是什么?怎么做才能赚到钱
  • 安卓QQ闪照获取软件(支持TIM)
  • 各省市信息化项目管理办法中的网络安全等级保护如何规定的?
  • 智能化企业级CRM系统开发实战:飞算JavaAI全流程体验
  • 【音视频】火山引擎实时、低延时拥塞控制算法的优化实践
  • 在 Delphi 5 中获取 Word 文档页数的方法
  • ⸢ 肆 ⸥ ⤳ 默认安全:安全建设方案 ➭ a.信息安全基线
  • 在线宠物用品|基于vue的在线宠物用品交易网站(源码+数据库+文档)
  • 从Web2到Web3:一场重塑数字未来的“静默革命”
  • OpenMMLab 模型部署利器:MMDeploy 详细介绍
  • 小学一到六年级语文/英语/数学作业出题布置网站源码 支持生成PDF和打印
  • Windows 电脑发现老是自动访问外网的域名排障步骤
  • 《微服务协作实战指南:构建全链路稳健性的防御体系》
  • 公司电脑监控软件应该怎么选择?五款超实用的公司电脑监控软件推荐
  • 云电脑是什么?与普通电脑的区别在哪里?——天翼云电脑体验推荐
  • 从电脑底层到进程创建:一篇看懂冯诺依曼、OS和进程
  • Netty从0到1系列之I/O模型
  • 基于Python毕业设计推荐:基于Django的全国降水分析可视化系统
  • 使用 qmake 生成 Makefile,Makefile 转换为 Qt 的 .pro 文件
  • npy可视化方法
  • 【Day 42】Shell-expect和sed
  • 量子计算+AI成竞争关键领域,谷歌/微软/微美全息追赶布局步入冲刺拐点!
  • 【音视频】WebRTC-NetEQ 分析
  • 第 12 篇:网格边界安全 - Egress Gateway 与最佳实践
  • Agentless:革命性的无代理软件工程方案
  • Coze源码分析-工作空间-资源查询-前端源码
  • 低空经济的中国式进化:无人机与实时视频链路的未来五年
  • SpringCloud框架组件梳理