????????在Linux系統中,“一切皆文件”(Everything is a file)是一個核心設計哲學,它抽象了系統資源的訪問方式,使得幾乎所有硬件設備、進程、網絡連接等都可以通過統一的文件接口(如
open()
、read()
、write()
、close()
等系統調用)進行操作。
目錄
一、在Linux系統中,"文件"的概念比Windows更為廣泛
二、Linux中的文件
1、普通文件(Regular Files)
2、目錄(Directories)
3、設備文件(Device Files)
4、命名管道(Named Pipes, FIFO)
5、套接字(Sockets)
6、符號鏈接(Symbolic Links)
7、偽文件系統(ProcFS, SysFS, etc.)
8、標準輸入/輸出/錯誤(stdin, stdout, stderr)
9、例外情況
三、這一設計的主要優勢在于
為什么這樣設計?
四、補充說明
一、在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
(終端設備)。
-
示例操作:
# 向磁盤設備寫入數據(需謹慎!)
dd if=file.img of=/dev/sdb# 從鼠標設備讀取輸入(需權限)
cat /dev/input/mouse0
4、命名管道(Named Pipes, FIFO)
-
用于進程間通信(IPC)的特殊文件,數據先進先出。
-
示例:
mkfifo /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頻率)。
示例:
# 查看CPU信息
cat /proc/cpuinfo# 修改系統參數(如最大進程數)
echo 10000 > /proc/sys/kernel/pid_max
8、標準輸入/輸出/錯誤(stdin, stdout, stderr)
-
在Linux中,這些標準流也通過文件描述符訪問:
-
0
:stdin(如鍵盤輸入)。 -
1
:stdout(如終端輸出)。 -
2
:stderr(如錯誤輸出)。
-
重定向示例:
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 以下展示該結構體的相關部分內容:
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 頭文件中:
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返回-EINVALssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); // 向設備寫入數據,NULL返回-EINVALssize_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); // 僅對文件系統有用,設備文件應為NULLunsigned 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); // 無鎖版ioctllong (*compat_ioctl)(struct file *, unsigned int, unsigned long); // 兼容版ioctlint (*mmap)(struct file *, struct vm_area_struct *); // 將設備內存映射到進程地址空間,NULL返回-ENODEVint (*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下一切皆文件"理念的核心體現。