在Linux系统中,"一切皆文件"(Everything is a file)是一个核心设计哲学,它抽象了系统资源的访问方式,使得几乎所有硬件设备、进程、网络连接等都可以通过统一的文件接口(如
open()
、read()
、write()
、close()
等系统调用)进行操作。
目录
一、在Linux系统中,"文件"的概念比Windows更为广泛
[1、普通文件(Regular Files)](#1、普通文件(Regular Files))
[3、设备文件(Device Files)](#3、设备文件(Device Files))
[4、命名管道(Named Pipes, FIFO)](#4、命名管道(Named Pipes, FIFO))
[6、符号链接(Symbolic Links)](#6、符号链接(Symbolic Links))
[7、伪文件系统(ProcFS, SysFS, etc.)](#7、伪文件系统(ProcFS, SysFS, etc.))
[8、标准输入/输出/错误(stdin, stdout, stderr)](#8、标准输入/输出/错误(stdin, stdout, stderr))
一、在Linux系统中,"文件"的概念比Windows更为广泛
- Windows中的文件在Linux中同样被视为文件
- Windows中非文件对象(如进程、磁盘、显示器、键盘等硬件设备)在Linux中也被抽象为文件
- 管道同样被视为文件
- 后续将学习到的网络编程中的套接字(socket)也采用文件接口
二、Linux中的文件
1、普通文件(Regular Files)
-
包括文本文件、二进制文件等,存储在磁盘或其他存储设备中。
-
例如:
/home/user/document.txt
。
2、目录(Directories)
-
目录本质上是包含其他文件列表的特殊文件。
-
例如:
/etc/
目录中保存了系统配置文件列表。
3、设备文件(Device Files)
Linux将硬件设备抽象为文件,分为两类:
-
块设备(Block Devices):以固定大小的数据块访问(如磁盘)。
- 例如:
/dev/sda
(第一块硬盘)。
- 例如:
-
字符设备(Character Devices):以字符流形式访问(如键盘、鼠标)。
- 例如:
/dev/tty
(终端设备)。
- 例如:
示例操作:
bash
# 向磁盘设备写入数据(需谨慎!)
dd if=file.img of=/dev/sdb
# 从鼠标设备读取输入(需权限)
cat /dev/input/mouse0
4、命名管道(Named Pipes, FIFO)
-
用于进程间通信(IPC)的特殊文件,数据先进先出。
-
示例:
bashmkfifo /tmp/my_pipe echo "Hello" > /tmp/my_pipe & # 写入端 cat < /tmp/my_pipe # 读取端
5、套接字(Sockets)
-
用于网络或本地进程间通信的文件。
-
例如:
/var/run/docker.sock
是Docker守护进程的通信套接字。
6、符号链接(Symbolic Links)
-
指向其他文件的快捷方式。
-
例如:
/bin/sh
可能是指向/bin/bash
的符号链接。
7、伪文件系统(ProcFS, SysFS, etc.)
-
/proc :动态反映进程和内核状态的文件(如
/proc/cpuinfo
、/proc/1234/
为PID 1234的进程信息)。 -
/sys:暴露内核设备和驱动的配置(如调节CPU频率)。
示例:
bash
# 查看CPU信息
cat /proc/cpuinfo
# 修改系统参数(如最大进程数)
echo 10000 > /proc/sys/kernel/pid_max
8、标准输入/输出/错误(stdin, stdout, stderr)
-
在Linux中,这些标准流也通过文件描述符访问:
-
0
:stdin(如键盘输入)。 -
1
:stdout(如终端输出)。 -
2
:stderr(如错误输出)。
-
重定向示例:
bash
ls /nonexistent 2> /dev/null # 将错误输出重定向到"黑洞"设备
9、例外情况
并非所有资源都是文件,例如:
-
线程调度、内存分配等底层操作仍需通过系统调用(如
mmap()
)。 -
某些现代内核特性(如cgroups)可能不完全遵循此规则。
三**、这一设计的主要优势在于**
- 开发者仅需掌握一套API和开发工具即可调用系统大部分资源
- 几乎所有读取操作(读取文件、系统状态、管道等)都可通过read函数实现
- 几乎所有写入操作(修改文件、系统参数、管道等)都可通过write函数完成
为什么这样设计?
-
统一性:所有资源通过文件接口操作,简化编程模型。
-
抽象性:用户无需关心底层细节(如硬件差异)。
-
灵活性 :文件权限(如
chmod
)、重定向(如>
)等机制可通用。
四、补充说明
当打开文件时,系统会创建对应的file结构体进行管理。该结构体定义于: /usr/src/kernels/3.10.0-1160.71.1.el7.x86_64/include/linux/fs.h 以下展示该结构体的相关部分内容:

c
struct file {
...
struct inode *f_inode; /* Cached inode pointer */
const struct file_operations *f_op;
...
atomic_long_t f_count; /* Reference count for open files */
unsigned int f_flags; /* File access flags (read/write permissions) */
fmode_t f_mode; /* File access mode (defined in headers) */
loff_t f_pos; /* Current read/write position */
...
} __attribute__((aligned(4))); /* Force 4-byte alignment */
值得注意的是,struct file 中的 f_op 指针指向一个 file_operations 结构体,该结构体除 struct module* owner 成员外,其余均为函数指针。这两个结构体均定义于 fs.h 头文件中:

c
struct file_operations {
struct module *owner; // 指向所属模块的指针
loff_t (*llseek)(struct file *, loff_t, int); // 修改文件当前读写位置,返回新位置
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); // 从设备读取数据,NULL返回-EINVAL
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); // 向设备写入数据,NULL返回-EINVAL
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); // 无锁版ioctl
long (*compat_ioctl)(struct file *, unsigned int, unsigned long); // 兼容版ioctl
int (*mmap)(struct file *, struct vm_area_struct *); // 将设备内存映射到进程地址空间,NULL返回-ENODEV
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); // 异步刷新
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 **); // 设置租约
};
file_operation
是连接系统调用与驱动程序的核心数据结构,其每个成员都对应着一个特定的系统调用。当系统调用执行时,会读取 file_operation
中对应的函数指针,并将控制权转交给该函数,从而完成 Linux 设备驱动程序的调用流程。
为帮助理解,我们用一张图来总结上述内容:

图中的外设设备虽然各自拥有独立的读写操作方式,但通过 struct file 结构中 file_operation 的函数回调机制,开发者仅需使用 file 接口就能访问 Linux 系统中的绝大多数资源。这正是"Linux下一切皆文件"理念的核心体现。