外行對“一切都是文件”的解釋——與 Windows 有什麼不同?
我知道“一切都是文件”意味著即使設備在 Unix 和類 Unix 系統中也有它們的文件名和路徑,這允許在各種資源上使用通用工具,而不管它們的性質如何。但我無法將其與我使用過的唯一其他作業系統 Windows 進行對比。我已經閱讀了一些關於這個概念的文章,但我認為它們對於非開發人員來說有些難以理解。外行的解釋是人們需要的!
例如,當我想將文件複製到連接到讀卡器的 CF 卡上時,我會使用類似
zcat name_of_file > /dev/sdb
在 Windows 中,我認為讀卡器將作為驅動程序出現,我認為我們會做類似的事情。那麼,“一切都是文件”的理念在這裡有何不同?
“一切都是文件”有點油嘴滑舌。“一切都出現在文件系統的某個地方”更接近於標記,即使這樣,它也更像是一種理想,而不是系統設計的法則。
例如,Unix 域套接字不是文件,但它們確實出現在文件系統中。您可以
ls -l
使用域套接字來顯示其屬性,通過 修改其訪問控制chmod
,並且在某些 Unix 類型系統(例如 macOS,但不是 Linux)上,您甚至可以將cat
數據傳入/傳出。但是,即使使用與Unix 域套接字相同的BSD 套接字系統呼叫創建和操作正常TCP/IP 網路套接字,TCP/IP 套接字也不會出現在文件系統中¹,儘管沒有特別好的理由應該這樣做是真的。
另一個出現在文件系統中的非文件對象的例子是Linux 的
/proc
文件系統。這個特性向使用者空間公開了大量關於核心執行時操作的細節,主要是作為虛擬純文字文件。許多/proc
條目是只讀的,但也有很多/proc
是可寫的,因此您可以使用任何可以修改文件的程序來更改系統的執行方式。唉,我們又遇到了一個不理想的情況:BSD Unix在預設情況下執行/proc
,而 System V Unix 暴露的 via/proc
比 Linux 少得多。我無法將其與 MS Windows 進行對比
首先,您可以在網上和書籍中找到很多關於 Unix 完全是關於文件 I/O 和 Windows 在這方面被“破壞”的觀點已經過時了。Windows NT修復了很多這個問題。
現代版本的 Windows 具有統一的 I/O 系統,就像 Unix 一樣,因此您可以根據需要通過 TCP/IP 套接字
ReadFile()
而不是 Windows 套接字特定的 API從 TCP/IP 套接字讀取網路數據WSARecv()
。這與Unix Way完全平行,您可以使用通用read(2)
Unix 系統呼叫或特定於套接字的呼叫從網路套接字讀取recv(2)
。²儘管如此,Windows 仍然未能將這一概念提升到與 Unix 相同的水平,即使是在 2021 年。Windows 體系結構的許多區域無法通過文件系統訪問,或者不能被視為類似文件。一些例子:
- 司機。
Windows 的驅動子系統很容易與 Unix 的一樣豐富和強大,但是要編寫程序來操作驅動程序,您通常必須使用Windows Driver Kit,這意味著編寫 C 或 .NET 程式碼。
在 Unix 類型的作業系統上,您可以從命令行對驅動程序做很多事情。你幾乎肯定已經做到了,只要將不需要的輸出重定向到
/dev/null
.³ 2. 程序間通信。Windows 程序不能像 Unix 命令行程序那樣通過文本流和管道輕鬆地相互通信。Unix GUI 通常要麼建立在命令行程序之上,要麼導出文本命令界面,因此同樣簡單的基於文本的通信機制也適用於 GUI 程序。 3. 系統資料庫。
Unix 沒有直接等效的 Windows 系統資料庫。相同的資訊分散在文件系統中,主要分佈
/etc
在/proc
和/sys
.如果您沒有看到驅動程序、管道和 Unix 對 Windows 系統資料庫的回答與“一切都是文件”有任何關係,請繼續閱讀。
“一切都是文件”的理念在這裡有何不同?
我將通過擴展我上面的三點來詳細解釋這一點。
長答案,第 1 部分:驅動器與設備文件
假設您的 CF 卡讀卡器
E:
在 Windows 和/dev/sdc
Linux 下顯示。它有什麼實際區別?這不僅僅是一個小的語法差異。
在 Linux 上,我可以說用零
dd if=/dev/zero of=/dev/sdc
覆蓋的內容。/dev/sdc
想一想這意味著什麼。
dd(1)
在這裡,我有一個普通/dev/zero
的使用者空間程序/dev/sdc
(dd
不知道它正在讀取和寫入特殊設備。正如我們將在下面看到的,它也適用於正常文件,或混合設備和文件。在 Windows 上沒有簡單的方法將
E:
驅動器歸零,因為 Windows 區分文件和驅動器,因此您不能使用相同的命令來操作它們。您可以獲得的最接近的方法是在沒有“快速格式化”選項的情況下進行磁碟格式化,這會將大部分驅動器內容歸零,但隨後會在其上寫入一個新的文件系統。如果我不想要一個新的文件系統怎麼辦?如果我真的希望磁碟只填充零怎麼辦?讓我們大方接受這個要求,在
E:
. 要在 Windows 上的程序中執行此操作,我必須呼叫特殊的格式化 API。⁴ 在 Linux 上,您無需編寫程序來訪問作業系統的“格式化磁碟”功能:您只需執行適當的使用者空間程序即可您要創建的文件系統類型,無論是mkfs.ext4
,mkfs.xfs
還是您所擁有的。這些程序會將文件系統寫入/dev
您傳遞的任何文件或節點上。因為
mkfs
Unixy 系統上的類型程序不會人為地區分設備和普通文件,所以我可以在我的 Linux 機器上的普通文件中創建一個ext4 文件系統:$ dd if=/dev/zero of=myfs bs=1k count=1k $ mkfs.ext4 -F myfs
這將創建一個
myfs
在目前目錄中呼叫的 1 MiB 磁碟映像。然後我可以像掛載任何其他外部文件系統一樣掛載它:$ mkdir mountpoint $ sudo mount -o loop myfs mountpoint $ grep $USER /etc/passwd > mountpoint/my-passwd-entry $ sudo umount mountpoint
現在我有一個 ext4 磁碟映像,其中包含一個名為的文件
my-passwd-entry
,其中包含我的使用者/etc/passwd
條目。如果我願意,我可以將該圖像放到我的 CF 卡上:
$ sudo dd if=myfs of=/dev/sdc1
或者,我可以將該磁碟映像打包,郵寄給您,然後讓您將其寫入您選擇的介質,例如 USB 記憶棒:
$ gzip myfs $ echo "Here's the disk image I promised to send you." | mutt -a myfs.gz -s "Password file disk image" \ you@example.com
所有這一切在 Linux⁵ 上都是可能的,因為文件、文件系統和設備之間沒有人為的區別。Unix 系統上的許多東西要麼是文件,要麼是通過文件系統訪問的,所以它們看起來像文件,或者以其他方式看起來足夠像文件,可以這樣對待它們。
Windows 的文件系統概念是一個大雜燴。它區分目錄、驅動器和網路資源。在 Windows 中混合了三種不同的語法:類 Unix
..\FOO\BAR
路徑系統、C:
像\\SERVER\PATH\FILE.TXT
. 這是因為它是 Unix、CP/M、MS-DOS和LAN Manager思想的積累,而不是單一的連貫設計。這就是為什麼 Windows 文件名中有這麼多非法字元的原因。Unix 有一個統一的文件系統,所有東西都通過一個通用的方案訪問。
/etc/passwd
對於在 Linux 機器上執行的程序,/media/CF_CARD/etc/passwd
、 和之間沒有功能差異/mnt/server/etc/passwd
。本地文件、外部媒體和網路共享都以相同的方式處理。⁶Windows 可以達到與我上面的磁碟映像範例類似的目的,但是您必須使用由非常有才華的程序員編寫的特殊程序。這就是為什麼 Windows 上有這麼多“虛擬 DVD”類型的程序。缺乏核心作業系統功能為填補空白的程序創造了一個人為的市場,這意味著你有一群人在競爭創建最好的虛擬 DVD 類型的程序。我們在 *ix 系統上不需要這樣的程序,因為我們可以使用循環設備掛載 ISO 磁碟映像。
磁碟擦除程序等其他工具也是如此,我們在 Unix 系統上也不需要這些工具。想要您的 CF 卡內容不可恢復地被打亂,而不僅僅是歸零?好的,
/dev/random
用作數據源,而不是/dev/zero
:$ sudo dd if=/dev/random of=/dev/sdc
在 Linux 上,我們不會不斷地重新發明這樣的輪子,因為核心作業系統功能不僅執行良好,而且執行良好,被廣泛使用。僅舉一個例子,引導 Linux 機器的典型方案涉及使用我上面展示的技術創建的虛擬磁碟映像。⁷
我覺得公平地說,如果 Unix 從一開始就將 TCP/IP I/O 集成到文件系統中,我們就不會有
netcat
vssocat
vsNcat
vs mess,其原因與導致Windows 上的磁碟映像和擦除工具激增:缺乏可接受的作業系統工具。nc
長答案,第 2 部分:管道作為虛擬文件
儘管起源於 DOS,但 Windows 從來沒有豐富的命令行傳統。
這並不是說 Windows 沒有命令行,或者它缺少許多命令行程序。如今,Windows 甚至有一個非常強大的命令外殼,恰當地稱為PowerShell。
然而,缺乏命令行傳統會產生連鎖反應。您會獲得
DISKPART
在 Windows 世界中幾乎不為人知的工具,因為大多數人通過電腦管理 MMC 管理單元進行磁碟分區等。然後,當您確實需要編寫分區創建腳本時,您會發現這DISKPART
並不是真正由另一個程序驅動的。是的,您可以將一系列命令寫入腳本文件並通過 執行它DISKPART /S scriptfile
,但這是全有或全無。在這種情況下,您真正想要的是更像GNUparted
的東西,它將接受單個命令,例如parted /dev/sdb mklabel gpt
. 這允許您的腳本逐步進行錯誤處理。這一切與“一切都是文件”有什麼關係?簡單:管道將命令行程序 I/O 變成某種“文件”。管道是單向流,不像普通磁碟文件那樣隨機訪問,但在許多情況下,差異並不重要。重要的是你可以附加兩個獨立開發的程序,讓它們通過簡單的文本進行通信。從這個意義上說,任何兩個以Unix 方式設計的程序都可以通信。
在您確實需要文件的情況下,將程序輸出轉換為文件很容易:
$ some-program --some --args > myfile $ vi myfile
但是,當“一切都是文件”的理念為您提供了更好的方法時,為什麼要將輸出寫入臨時文件呢?如果您只想將該命令的輸出讀入
vi
編輯器緩衝區,vi
則可以直接為您執行此操作。從vi
“正常”模式,說::r !some-program --some --args
這會將程序的輸出插入到目前游標位置的活動編輯器緩衝區中。在幕後,
vi
是使用管道將程序的輸出連接到一些程式碼,這些程式碼使用相同的作業系統呼叫,而不是從文件中讀取。如果這兩種情況:r
——即有和沒有!
——在所有常見的vi
. 我想不出一個很好的理由不這樣做。這也不是 的最新功能
vi
;它可以追溯到古代ed(1)
的文本編輯器。這個強大的想法在 Unix 中反復出現。
對於第二個例子,回想一下我
mutt
上面的電子郵件命令。我必須將其編寫為兩個單獨的命令的唯一原因是我希望將臨時文件命名為*.gz
,以便正確命名電子郵件附件。如果我不關心文件名,我可以使用程序替換來避免創建臨時文件:$ echo "Here's the disk image I promised to send you." | mutt -a <(gzip -c myfs) -s "Password file disk image" \ you@example.com
通過將輸出
gzip -c
轉換為 FIFO(類似於文件)或/dev/fd
對象(類似於文件)來避免臨時性。⁸對於這種強大的想法在 Unix 中出現的第三種方式,請考慮
gdb
在 Linux 系統上。這是用於任何用 C 和 C++ 編寫的軟體的調試器。從其他系統來到 Unix 的程序員看到gdb
並幾乎總是抱怨它,“哎呀,它太原始了!” 然後他們去尋找一個 GUI 調試器,找到幾個現有的調試器之一,然後愉快地繼續他們的工作……經常沒有意識到 GUI 只是在gdb
下面執行,在它上面提供了一個漂亮的 shell。大多數 Unix 系統上沒有競爭的低級調試器,因為沒有必要讓程序在那個級別上競爭。我們所需要的只是一個好的低級工具,如果該低級工具可以通過管道輕鬆通信,我們都可以將其作為高級工具的基礎。這意味著我們現在有一個記錄在案的調試器介面,可以直接替換
gdb
. 不幸的是,主要競爭對手gdb
沒有走這條低摩擦的道路,但撇開這個小問題不談,lldb
它和gdb
.為了在 Windows 機器上實現同樣的功能,可替換工具的創建者必須定義某種形式的外掛或自動化 API。這意味著除了最流行的程序之外不會發生這種情況,因為建構普通的命令行使用者界面和完整的程式 API 需要大量工作。
這種魔力是通過無處不在的基於文本的IPC來實現的。
儘管 Windows 的核心具有 Unix 風格的匿名管道,但很少看到普通使用者程序在命令 shell 之外將它們用於IPC ,因為 Windows 缺乏這種先在命令行版本中創建所有核心服務,然後在其上建構 GUI 的傳統。分別放在上面。這導致在沒有 GUI 的情況下無法執行某些操作,這也是與 Linux 相比 Windows有如此多遠端桌面系統的原因之一。這無疑是為什麼 Linux 是雲作業系統的部分原因,一切都由遠端管理完成。在很大程度上,命令行界面比 GUI 更容易自動化,因為“一切都是文件”。
考慮 SSH。你可能會問,它是如何工作的?SSH 將網路套接字(類似於文件)連接到偽 tty at
/dev/pty*
(類似於文件)。現在,您的遠端系統通過與 Unix 方式無縫匹配的連接連接到本地系統,如果需要,您可以通過 SSH 連接管道數據。你知道這個概念現在有多強大嗎?
從程序的角度來看,管道文本流與文件沒有區別,除了它是單向的。程序從管道中讀取的方式與從文件中讀取的方式相同:通過文件描述符。FD 絕對是 Unix 的核心;文件和管道在兩者上使用相同的 I/O 抽像這一事實應該告訴你一些事情。⁹
Windows 世界缺乏這種簡單文本通信的傳統,只能通過COM或.NET使用重量級OOP介面。如果您需要自動化這樣的程序,您還必須編寫一個 COM 或 .NET 程序。這比在 Unix 機器上設置管道要困難得多。
缺少這些複雜程式 API 的 Windows 程序只能通過諸如剪貼板或 File/Save 等貧乏的介面進行通信,然後是 File/Open。
長答案,第 3 部分:系統資料庫與配置文件
Windows 系統資料庫和 Unix 系統配置方式之間的實際差異也說明了“一切都是文件”理念的好處。
在 Unix 類型的系統上,我可以僅通過檢查文件從命令行查看系統配置資訊。我可以通過修改這些相同的文件來改變系統行為。在大多數情況下,這些配置文件只是純文字文件,這意味著我可以使用 Unix 上的任何可以處理純文字文件的工具來操作它們。
在 Windows 上編寫系統資料庫腳本並不容易。
最簡單的方法是在一台機器上通過系統資料庫編輯器 GUI 進行更改,然後使用via files盲目地將這些更改應用到其他機器
regedit``*.reg
。這並不是真正的“腳本”,因為它不允許你有條件地做任何事情:要麼全有,要麼全無。如果您的系統資料庫更改需要任何數量的邏輯,那麼下一個最簡單的選擇是學習PowerShell,這相當於學習 .NET 系統程式。就像 Unix 只有 Perl,而您必須通過它進行所有臨時系統管理。現在,我是 Perl 的粉絲,但不是每個人都是。Unix 允許您使用您喜歡的任何工具,只要它可以操作純文字文件。
腳註:
- 計劃 9修復了這個設計失誤,通過
/net
虛擬文件系統暴露網路 I/O 。Bash 有一個稱為
/dev/tcp
允許通過正常文件系統函式進行網路 I/O 的功能。由於它是 Bash 功能,而不是核心功能,因此在 Bash 之外或根本不使用 Bash 的系統上是不可見的。這通過反例說明了為什麼通過文件系統使所有數據資源可見是一個好主意。 2. “現代 Windows”是指 Windows NT 及其所有直系後代,包括 Windows 2000、所有版本的 Windows Server 以及從 XP 開始的所有面向桌面的 Windows 版本。我使用該術語來排除基於 DOS 的 Windows 版本,即 Windows 95 及其直接後代 Windows 98 和 Windows ME,以及它們的 16 位前身。您可以通過在後者的作業系統中缺乏統一的 I/O 系統來看到區別。您不能
ReadFile()
在 Windows 95 上傳遞 TCP/IP 套接字;您只能將套接字傳遞給 Windows 套接字 API。請參閱 Andrew Schulman 的開創性文章Windows 95:它不是什麼,以更深入地了解該主題。 3. 毫無疑問,/dev/null
它是 Unix 類型系統上的真正核心設備,而不僅僅是一個特殊大小寫的文件名,就像NUL
在 Windows 中表面上等效的一樣。儘管 Windows 試圖阻止您創建文件,但可以通過欺騙
NUL
手段繞過這種保護,欺騙 Windows 的文件名解析邏輯。如果您嘗試使用或資源管理器訪問該文件,Windows 將拒絕打開它,但您可以通過 Cygwin 對其進行寫入,因為它使用與範常式序類似的方法打開文件,您可以通過類似的技巧將其刪除。cmd.exe
相比之下,Unix 會很樂意讓你
rm /dev/null
,只要你有寫權限/dev
,並讓你在它的位置重新創建一個新文件,這一切都沒有詭計,因為那個dev 節點只是另一個文件。雖然缺少該開發節點,但核心的空設備仍然存在;在您通過mknod
.你甚至可以在別處創建額外的空設備開發節點:呼叫它沒關係
/home/grandma/Recycle Bin
,只要它是空設備的開發節點,它的工作方式與/dev/null
. 4. Windows中實際上有兩個高級“格式化磁碟”API:SHFormatDrive()
和Win32_Volume.Format()
.有兩個非常…嗯… Windows的原因。第一個要求 Windows 資源管理器顯示其正常的“格式化磁碟”對話框,這意味著它可以在任何現代版本的 Windows 上執行,但僅在使用者以互動方式登錄時。另一個您可以在後台呼叫而無需使用者輸入,但它直到 Windows Server 2003 才被添加到 Windows。沒錯,直到 2003 年,在 Unix
mkfs
從第一天開始發布的世界中,核心作業系統行為都隱藏在 GUI 後面。在
/etc/mkfs
我1974 年的 Unix V5 副本中,有一個 4136 字節的靜態連結PDP-11執行檔。(Unix直到 1980 年代後期才獲得動態連結,所以它不像其他地方有一個大型庫在做所有真正的工作。)它的原始碼 - 包含在 V5 系統映像中/usr/source/s2/mkfs.c
- 是一個完全獨立的 457- C行程序。#include
連說法都沒有!這意味著您不僅可以
mkfs
在高層次上檢查什麼,還可以使用創建 Unix 時使用的相同工具集進行試驗,就像40 年前的Ken Thompson一樣。用 Windows 試試。你今天最接近的是下載DOS 原始碼,它於2014 年首次發布,你會發現它只是一堆彙編原始碼。它只會使用你手頭可能沒有的過時工具建構,最終你會得到你自己的 DOS 2.0 副本,這是一個遠不如 1974 年的Unix V5強大的作業系統,儘管它在近十年後發布。(為什麼要談 Unix V5?因為它是最早仍然可用的完整 Unix 系統。早期的版本顯然已經被時間遺忘了。有一個項目拼湊了一個 V1/V2 時代的 Unix,但它似乎缺失了
mkfs
,儘管存在上面連結的 V1 手冊頁證明它一定存在於某個地方,某個時候。要麼把這個項目放在一起的人找不到mkfs
要包含的現存副本,要麼我很討厭找到沒有 的文件find(1)
,這在那個系統中也不存在.:)
)現在,您可能會想,“我不能只呼叫
format.com
嗎?在 Windows 上呼叫mkfs
和在 Unix 上呼叫不一樣嗎?” 唉,不,不一樣,原因有很多:
- 首先,
format.com
不是為編寫腳本而設計的。它會提示您“準備就緒時按 ENTER”,這意味著您需要向其輸入發送一個 Enter 鍵,否則它將掛起。- 然後,如果您想要的不僅僅是成功/失敗狀態程式碼,則必須打開其標準輸出以進行讀取,這在 Windows 上比它必須要復雜得多。(在 Unix 上,連結文章中的所有內容都可以通過一個簡單的
popen(3)
呼叫來完成。)- 經歷了所有這些複雜性之後,
format.com
電腦程序的輸出比 的輸出更難解析,mkfs
主要用於人類消費。- 如果你跟踪它做了什麼
format.com
,你會發現它做了一堆複雜的呼叫DeviceIoControl()
,ufat.dll
等等。它不僅僅是打開一個設備文件並將一個新的文件系統寫入該設備。這是您從一家在全球擁有 176000 名員工並需要繼續僱傭他們的公司獲得的設計。
- 在談論循環設備時,我一般只談論 Linux 而不是 Unix,因為循環設備在 Unix 類型系統之間不可移植。macOS、BSD 等也有類似的機制,只是語法有所不同。
- 早在磁碟驅動器的大小與洗衣機一樣大且成本超過部門主管的豪華汽車的時代,與現代計算環境相比,大型電腦實驗室將共享更大比例的集體磁碟空間。透明地將遠端磁碟移植到本地文件系統的能力使這種分佈式系統更容易使用。例如,這就是我們得到的地方
/usr/share
。對比 Windows,遠端磁碟通常要麼映射到驅動器號,要麼必須通過 UNC 路徑訪問,而不是透明地集成到本地文件系統中。驅動器字母為您提供很少的符號表達選擇;是
P:
指BigServer 上的“公共”空間,還是軟體鏡像伺服器上的“packages”目錄?UNC 路徑意味著您必須記住遠端文件所在的伺服器,這在擁有數百或數千個文件伺服器的大型組織中變得困難。直到 2007 年發布的 Windows Vista 引入了NTFS 符號連結, Windows 才獲得符號連結,直到十年後它們才可用 。Windows 的符號連結比 Unix 的符號連結(自 1977年以來 Unix 的一個特性)更強大一點,因為它們還可以指向遠端文件共享,而不僅僅是指向本地路徑。Unix在 1984 年通過 NFS 以不同的方式做到了這一點,它建立在 Unix 預先存在的掛載點功能之上,它從一開始就擁有。
因此,根據您的看法,Windows 落後 Unix 大約 2、3 或 4 個十年。你可能會反對,“但它現在有 Unix 風格的符號連結*! ”然而這沒有抓住重點,因為這意味著在 Windows 上沒有**使用*它們的幾十年的傳統,所以在一個使用 Unix 系統的世界中,大多數人都不知道它們普遍使用它們,因此如果不了解它們,就不可能長時間使用 Unix 系統。
MKLINK
Windows 的程序倒退也無濟於事,您仍然無法從 Windows Explorer 創建它們,而 Windows Explorer 的 Unix 等效項通常允許您創建符號連結。 7. Linux 機器並不總是在引導序列中使用虛擬磁碟映像。有很多不同的方法可以做到這一點。 8. Bash 會根據系統的功能選擇該方法,因為/dev/fd
它並非隨處可用。 9. 順便說一下,網路套接字描述符也是下面的 FD。