在任何程序(可以向外延伸到其他很多領域)的生命周期中,復雜性都會不可避免地增加。
程序越大,工作的人越多,管理復雜性就越困難,程序員在修改系統時將所有相關因素牢記在心中變得越來越難;這會減慢開發速度并導致錯誤,從而進一步延緩開發速度并增加成本。
很多大型系統的本質問題是復雜性問題,數百個甚至更多的微服務相互調用/依賴,組成一個組件數量大、行為復雜、時刻在變動(發布、配置變更)當中的動態的、復雜的系統。
如果,我們將領域問題的復雜度與技術細節的復雜度混合在了一起,這最終將導致——整體復雜度的指數級增長。
復雜性的一個衡量維度:
一致性是降低系統復雜性并使其行為更明顯的強大工具。如果系統是一致的,則意味著相似的事情以相似的方式完成,而不同的事情則以不同的方式完成。一致性會產生認知影響力:一旦您了解了某個地方的工作方式,就可以使用該知識立即了解其他使用相同方法的地方。如果系統的實施方式不一致,則開發人員必須分別了解每種情況。這將花費更多時間。
一致性減少了錯誤。如果系統不一致,則實際上兩種情況可能不同,但兩種情況可能看起來相同。開發人員可能會看到一個看起來很熟悉的模式,并根據以前對該模式的遭遇做出錯誤的假設。另一方面,如果系統是一致的,則基于熟悉情況的假設將是安全的。一致性允許開發人員以更少的錯誤來更快地工作。
一致性是投資心態的另一個例子。確保一致性的工作將需要一些額外的工作:確定約定,創建自動檢查程序,尋找類似情況以模仿新代碼,以及進行代碼審查以教育團隊。這項投資的回報是您的代碼將更加明顯。開發人員將能夠更快,更準確地了解代碼的行為,這將使他們能夠以更少的錯誤來更快地工作。
一致性可以應用于系統中的許多級別。這里有一些例子。
編碼樣式。如今,開發組織通常擁有樣式指南,這些樣式指南將程序結構限制在編譯器所強制執行的規則之外。現代風格指南解決了一系列問題,例如縮進,大括號放置,聲明順序,命名,注釋以及對認為危險的語言功能的限制。樣式指南使代碼更易于閱讀,并且可以減少某些類型的錯誤。
接口。具有多個實現的接口是一致性的另一個示例。一旦了解了接口的一種實現,其他任何實現都將變得更易于理解,因為您已經知道它將必須提供的功能。
設計模式。設計模式是某些常見問題的普遍接受的解決方案,例如用于用戶界面設計的模型視圖控制器方法。如果您可以使用現有的設計模式來解決問題,則實現會更快地進行,更有可能起作用,并且您的代碼對讀者來說也會更明顯。
不變量。不變式是始終為真的變量或結構的屬性。例如,存儲文本行的數據結構可能會強制要求每行以換行符終止。不變式減少了代碼中必須考慮的特殊情況的數量,并使推理行為的方式變得更加容易。
一致性很難維護,尤其是當許多人長時間從事一個項目時。一組人可能不了解另一組中建立的約定。新來者不了解規則,因此他們無意間違反了約定并創建了與現有約定沖突的新約定。以下是建立和保持一致性的一些技巧:
“無名,萬物之始也; 有名,萬物之母也”。(老子的《道德經》第一章)
譯文:無,可以用來表示天地渾沌未開之際的狀況,而有,則是宇宙萬物產生之本原的命
名。
抽象的使用是計算機科學中最為重要的概念之一。例如,為一組函數規定一個簡單的應用程序接口(API)就是一個很好的編程習慣,程序員無需了解它內部的工作便可以使用這些代碼。不同的編程語言提供不同形式和等級的抽象支持,例如 Java 類的聲明和 C 語言的函數原型。操作系統中也存在著很多的抽象:
在處理器里,指令集結構提供了對實際處理器硬件的抽象。使用這個抽象,機器代碼程序表現得就好像它是運行在一個一次只執行一條指令的處理器上。底層的硬件比抽象描述的要復雜精細得多,它并行地執行多條指令,但又總是與那個簡單有序的模型保持一致。只要執行模型一樣,不同的處理器實現也能執行同樣的機器代碼,而又提供不同的開銷和性能。
文件是對 IO 的抽象,虛擬存儲器是對程序存儲器的抽象,而進程是對一個正在運行的程序的抽象。我們再增加一個新的抽象:虛擬機,它提供對整個計算機(包括操作系統、處理器和程序)的抽象。虛擬機的思想是 IBM 在 20 世紀 60 年代提出來的,但是最近才顯示出其管理計算機方式上的優勢,因為一些計算機必須能夠運行為不同操作系統(例如,Microsoft Windows、MacOS 和 Linux)或同一操作系統的不同版本而設計的程序。
抽象與模塊化設計的思想緊密相關。抽象是實體的簡化視圖,其中省略了不重要的細節。抽象是有用的,因為它們使我們更容易思考和操縱復雜的事物。在模塊化編程中,每個模塊以其接口的形式提供抽象。該接口提供了模塊功能的簡化視圖;從模塊抽象的角度來看,實現的細節并不重要,因此在接口中將其省略。
在抽象的定義中,“無關緊要”一詞至關重要。從抽象中忽略的不重要的細節越多越好。但是,如果細節不重要,則只能將其從抽象中省略。常見的不當抽象可能包含以下兩種:
首先,它可以包含并非真正重要的細節。當這種情況發生時,它會使抽象變得不必要的復雜,從而增加了使用抽象的開發人員的認知負擔。
第二個錯誤是抽象忽略了真正重要的細節。這導致模糊不清:僅查看抽象的開發人員將不會獲得正確使用抽象所需的全部信息。忽略重要細節的抽象是錯誤的抽象:它可能看起來很簡單,但實際上并非如此。
例如,考慮一個文件系統。文件系統提供的抽象省略了許多細節,例如用于選擇存儲設備上的哪些塊用于給定文件中的數據的機制。這些詳細信息對于文件系統的用戶而言并不重要(只要系統提供足夠的性能即可)。但是,文件系統實現的一些細節對用戶很重要。大多數文件系統將數據緩存在主內存中,并且它們可能會延遲將新數據寫入存儲設備以提高性能。一些應用程序(例如數據庫)需要確切地知道何時將數據寫入存儲設備,因此它們可以確保在系統崩潰后將保留數據。因此,將數據刷新到輔助存儲的規則必須在文件系統的接口中可見。
我們不僅依靠抽象來管理復雜性,而且不僅在編程中,而且在日常生活中無處不在。微波爐包含復雜的電子設備,可將交流電轉換為微波輻射并將該輻射分布到整個烹飪腔中。幸運的是,用戶看到了一個簡單得多的抽象,它由幾個按鈕控制微波的定時和強度。汽車提供了一種簡單的抽象概念,使我們可以在不了解電動機,電池電源管理,防抱死制動,巡航控制等機制的情況下駕駛它們。
另外一個典型的抽象模型,就是計算機的存儲管理模型。
我們知道,在由半導體器件、電路板組成的計算硬件體系中,根本沒有操作系統、TCP/IP、內存分配、垃圾回收等等概念模型。聰明的人類(這些人通常就是計算機科學家了),就是靠著杰出的想象力與抽象能力,設計出了計算機存儲分層抽象模型:
一個32位操作系統的例子。其中,1GB為操作系統的內核空間,用戶無法更改,這部分不用管它;剩下的3GB位用戶的內存空間,這是供用戶程序使用的。
想必,分層是宇宙創造萬物的方式。從地球構造到雞蛋構造,從太陽系組成到細胞結構,無不如此。
地球分層構造圖:
雞蛋結構圖:
太陽系:
細胞結構:
操作系統分層圖:
軟件系統由層組成,其中較高的層使用較低層提供的功能。例如:
在文件系統中,最上層實現文件抽象。文件由可變長度的字節數組組成,可以通過讀寫可變長度的字節范圍來更新該字節。文件系統的下一個下一層在固定大小的磁盤塊的內存中實現了高速緩存。調用者可以假定經常使用的塊將保留在內存中,以便可以快速訪問它們。最低層由設備驅動程序組成,它們在輔助存儲設備和內存之間移動塊。
在諸如 TCP 的網絡傳輸協議中,最頂層提供的抽象是從一臺機器可靠地傳遞到另一臺機器的字節流。此級別在較低級別上構建,該級別可以盡最大努力在計算機之間傳輸有限大小的數據包:大多數數據包將成功交付,但某些數據包可能會丟失或亂序交付。
從最底層的物理鏈路層層層向上封裝抽象,解決了復雜的網絡通信的問題。同樣的,任何復雜的問題,通過分層最終總能夠回歸最本質、最簡單。
DDD 分層架構遵循了“關注點分離”原則,將屬于業務邏輯的關注點放到:
1、領域層(Domain Layer)中,
2、而將支撐業務邏輯的技術實現放到基礎設施層(Infrastructure Layer)中。
3、應用層(Application Layer),扮演了雙重角色。一方面它作為業務邏輯的外觀(Facade),暴露了能夠體現業務用例的應用服務接口;另一方面它又是業務邏輯與技術實現的粘合劑,實現二者之間的協作。
下圖“分層架構“展現的就是一個典型的領域驅動設計分層架構。藍色區域的內容與業務邏輯有關,灰色區域的內容與技術實現有關,二者涇渭分明,然后匯合在應用層。應用層確定了業務邏輯與技術實現的邊界,通過直接依賴或者依賴注入(DI,Dependency Injection)的方式將二者結合起來。
為了將我們的應用部署到服務器上,我們需要為其配置一個運行環境。從底層到頂層有這樣的運行環境及容器:
實現上這是一個請求的處理過程,一個 HTTP 請求會先到達你的主機。如果你的主機上運行著多個虛擬機實例,那么請求就會來到這個虛擬機上。又或者是如果你是在 Docker 這一類容器里運行你的程序的話,那么也會先到達 Docker。隨后這個請求就會交由 HTTP 服務器來處理,如 Apache、Nginx,這些 HTTP 服務器再將這些請求交由對應的應用或腳本來處理。隨后將交由語言底層的指令來處理。
什么是結構(Structure)?結構,是由組成整體的各部分的搭配和安排。古人寫毛筆字,有云:“結構圓備如篆法,飄颺灑落如章草。”(晉·衛夫人《筆陣圖》)現代人做軟件結構設計,依然追尋著這樣一種美感——簡潔、優雅、小巧玲瓏若珍珠寶石一般的美。
在軟件架構領域,“結構”包括軟件元素,它們之間的關系,元素和關系的屬性,以及每個元素的引入和配置的基本原理(ISO/IEC 42010:20072)。
這里定義了架構的三要素:職責明確的模塊或者組件、組件間明確的關聯關系、約束和指導原則。
軟件系統的架構是一種隱喻,類似于建筑物的體系結構,是一種整體與局部關系的抽象描述,架構是軟件系統內部設計中最重要而又模糊的方面。有系統的地方就需要架構,大到航空飛機,小到一個電商系統里面的一個功能組件,都需要設計和架構。
架構,
(1)是對系統中的實體,以及實體之間的關系,所進行的抽象描述,
(2)是對事物的功能與形式元素之間的對應關系,所做的分配,
(3)是對元素之間的關系,以及元素同周邊環境之間的關系所做的定義。
架構能將目標系統按某個原則進行切分,切分的原則,是要便于不同的角色進行并行工作,結構良好的創造活動要優于毫無結構的創造活動。
舉一個蓋房子的例子。我們偶爾會從新聞上聽到某某房子倒塌、高架塌陷等悲慘事件,但是想想背后的原因,是不是因為房子、橋梁結構不合理,施工偷工減料,質量檢查沒有履行職責等原因導致?
這個在軟件工程領域,道理是相通的。一個沒有經過合理設計就匆忙開發上線的系統,遲早要還“技術債”。
架構并不由系統的功能決定,而是由系統的非功能屬性決定。
架構需要:
1、控制系統復雜性,將核心業務邏輯和技術細節的分離與解耦。
2、保證系統高可用。
3、提升團隊整體的研發效能。
架構師的職責是:
1、努力訓練自己的思維,用它去理解復雜的系統,
2、通過合理的分解和抽象,理解并解析需求,
3、創建有用的模型,
4、確認、細化并擴展模型,管理架構;
5、進行系統分解形成整體架構,
6、能夠正確的技術選型,
7、能夠制定技術規格說明并有效推動實施落地。
大多數程序員的編程行為,都是戰術思維方式,著眼于使功能盡快運行。
但是,如果您想要一個好的設計,則必須采取更具戰略性的方法,在此上花費時間來制作干凈的設計并解決問題。
在戰術編程方法中,核心關注點是,使某些功能正常工作,例如新功能或錯誤修復。
乍一看,這似乎是完全合理的:還有什么比編寫有效的代碼更重要的呢?
但是,戰術編程幾乎不可能產生出良好的系統設計。
戰術編程的問題是它是短視的。
如果您是戰術編程人員,那么您將只是盡快完成任務,您不會花費太多時間來尋找最佳設計。您只想盡快使某件事起作用。您告訴自己,可以增加一些復雜性或引入一兩個小錯誤,如果這樣可以使當前任務更快地完成,則可以。
如果您進行戰術編程,則每個編程任務都會帶來一些此類復雜性。
為了快速完成當前任務,他們每個人似乎都是一個合理的折衷方案。
但是,復雜性迅速累積,尤其是如果每個人都在戰術上進行編程的時候。
不久之后,某些復雜性將開始引起問題,但是,您會告訴(qi pian)自己,使下一個功能正常工作比返回并重構現有代碼更為重要。從長遠來看,重構可能會有所幫助,但是肯定會減慢當前的任務。
因此,您需要快速修補程序來解決遇到的任何問題。這只會增加復雜性,然后需要更多補丁。
很快代碼變得一團糟,但是到現在為止,情況已經很糟糕了,清理它需要花費數月的時間。您的日程安排無法容忍這種延遲,解決一個或兩個問題似乎并沒有太大的區別,因此您只是在戰術上保持編程。
幾乎每個軟件開發組織,都有至少一個將戰術編程發揮到極致的開發人員:戰術龍卷風(The tactical tornado)。
戰術龍卷風是一位多產的程序員,他抽出代碼的速度比其他人快得多,但完全以戰術方式工作。實施快速功能時,沒有人能比戰術龍卷風更快地完成任務。
在某些組織中,管理層將戰術龍卷風視為英雄。但是,戰術龍卷風留下了毀滅的痕跡。他們很少被將來必須使用其代碼的工程師視為英雄。通常,其他工程師必須清理戰術龍卷風留下的混亂局面,這使得那些工程師(他們是真正的英雄)的進步似乎比戰術龍卷風慢。
在戰術編程中,您將不斷增加一些復雜性,這些復雜性將來會引起問題。
成為一名優秀的軟件設計師的第一步是要意識到僅工作代碼是不夠的。引入不必要的復雜性以更快地完成當前任務是不可接受的。最重要的是系統的長期結構。任何系統中的大多數代碼都是通過擴展現有代碼庫編寫的,因此,作為開發人員,最重要的工作就是促進這些將來的擴展。因此,盡管您的代碼當然必須工作,但您不應將“工作代碼”視為主要目標。您的主要目標必須是制作出出色的設計,并且這種設計也會起作用。這是戰略計劃。
戰略性編程需要一種投資心態。您必須花費時間來改進系統的設計,而不是采取最快的方式來完成當前的項目。這些投資會在短期內讓您放慢腳步,但從長遠來看會加快您的速度。一些投資將是積極的。例如,值得花一些時間為每個新類找到一個簡單的設計。而不是實施想到的第一個想法,請嘗試幾種替代設計并選擇最簡潔的設計。試想一下將來可能需要更改系統的幾種方式,并確保設計容易。編寫好的文檔是主動投資的另一個例子。
如果您進行戰略性編程,則將不斷對系統設計進行小幅改進。
一開始,戰術性的編程方法將比戰略性方法更快地取得進展。但是,在戰術方法下,復雜性積累得更快,從而降低了生產率。
隨著時間的流逝,戰略方針會帶來更大的進步。注意:此圖僅用于定性說明;我不知道對曲線精確形狀的任何經驗測量。
相反,如果您進行戰術編程,則可以將第一個項目完成的速度提高 10%到 20%,但是隨著時間的推移,復雜性的累積會降低開發速度。不久之后,您的編程速度至少會降低 10–20%。您將很快退回在開始時節省的所有時間,并且在系統的整個生命周期中,與采用策略性方法相比,您的開發速度將更加緩慢。
與傳統的前期、重量級的企業架構設計相比,我們建議采用演進式架構(Evolutionary Architecture)。它提供了企業架構的好處,卻沒有試圖準確預測未來所帶來的問題。
演進式架構不需要猜測組件將如何被重用,而是支持適應性,使用適當的抽象、數據庫遷移、測試套件、持續集成和重構來收獲系統內發生的重用。
盡管我們盡了最大努力,但復雜度仍會隨著時間的推移而增加,但是更簡單的設計使我們能夠在復雜性壓倒性優勢之前構建更大,功能更強大的系統。復雜性的應對永遠不會是一勞永逸,我們需要不斷地推陳出新,是動態、漸進的重塑自己對軟件系統的認識,不斷認識問題和尋找更優解的持續迭代:
互聯網行業的軟件系統,很難一開始就做出完美的設計,通過一個個功能模塊衍生迭代,系統才會逐步成型;對于現存的系統,也很難通過一個大動作,一勞永逸地解決所有問題。系統設計是需要持續投入的工作,通過細節的積累,最終得到一個完善的系統。因此,好的設計是日拱一卒的結果,在日常工作中要重視設計和細節的改進。
美麗勝于丑陋。
顯式優于隱式。
簡單勝于復雜。
復雜勝于復雜。
扁平比嵌套好。
疏勝于密。
可讀性很重要。
特殊情況不足以打破規則。
盡管,實用第一,簡潔第二。
錯誤永遠不應該無聲無息地過去。
除非明確沉默。
面對曖昧,拒絕猜測的誘惑。
應該有一種——最好只有一種——顯而易見的方法來做到這一點。
雖然這種方式一開始可能不明顯,除非你是荷蘭人。
現在總比沒有好。
雖然永遠不會比現在好。
如果實現很難解釋,那就是一個壞主意。
如果實現容易解釋,這可能是一個好主意。
命名空間是一個非常棒的主意——讓我們做更多這樣的事情吧!
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
架構師,是一個既能掌控整體全局,又能洞悉局部瓶頸,并依據具體的業務場景,給出解決方案的團隊領導型人物。
軟件系統架構師綜合的知識能力包括9個方面,即:
1、戰略規劃能力。
2、業務流程建模能力。
3、信息數據結構能力。
4、技術架構選擇和實現能力。
5、應用系統架構的解決和實現能力。
6、基礎IT知識及基礎設施、資源調配能力。
7、信息安全技術支持與管理保障能力。
8、IT審計、治理與基本需求分析、獲取能力。
9、面向軟件系統可靠性與系統生命周期的質量保障服務能力。
作為系統架構師,必須成為所在開發團隊的技術路線指導者;具有很強的系統思維的能力;需要從大量互相沖突的系統方法和工具中區分出哪些是有效的,哪些是無效的。架構師應當是一個成熟的、豐富的、有經驗的、有良好教育的、學習快捷、善溝通和決策能力強的人。豐富是指他必須具有業務領域方面的工作知識,知識來源于經驗或者教育。他必須廣泛了解各種技術并精通一種特定技術,至少了解計算機通用技術以便確定那種技術最優,或組織團隊開展技術評估。優秀的架構師能考慮并評估所有可用來解決問題的總體技術方案。需要良好的書面和口頭溝通技巧,一般通過可視化模型和小組討論來溝通指導團隊確保開發人員按照架構建造系統。
具備的能力
(1)技術能力
技術能力,不用置疑肯定是最重要的。技術能力弱的架構不是一個好架構。所以,你需要知道所有主流技術的基本原理、應用場景,及快速解決問題的能力。所以,架構師必須要有見識,所需知識面肯定是要不斷拓展的。你需要清楚在什么樣的場景用什么樣的技術比較合適,并知道可能存在什么樣的風險。來了需求,你腦袋是空的,不知道用什么技術這是最可怕的。
(2)架構能力
這個可以表現為抽象能力、整體規劃能力、及設計能力。你需要照在業務的角度進行系統分解、技術選型、架構搭建,以及規范制定。架構出來了至少可以滿足最近的發展,或者可以很方便對現有架構進行擴容。有人說架構不需要懂業務,我面試過的就有明確表示不做業務架構。當然有方面的架構師,如中間件架構師,運維基礎設施架構師等。但一般的后端架構師都是需要了解業務,不理解業務你如果進行系統分解,服務劃分,及根據不同業務作出不同的架構。技術都是為業務服務的,不站在業務的角度設計架構,那架構就是空談。[1]
(3)溝通能力
這個看起來不是最重要的,其實也非常重要。作為一個優秀的架構師,你需要清楚的知道客戶的需求,需要不斷和需求人員進行溝通,以達到客戶真正的目的。不論是不是架構師,任何一個職場人,提高自己的溝通表達能力無疑是不可或缺的。有一句話怎么說的,領導就喜歡拍馬屁的。做領導的大多不是技術特別牛的,但溝通能力肯定是很好的。
憑借卓越的內存效率、速度與安全性等特點,近幾年 Rust 可謂深受大廠青睞:
2019 年,AWS 表示開始在其基礎架構中越來越多地使用 Rust 后,決定贊助 Rust,即 Rust 團隊可以優惠租用 AWS 基礎設施以進行語言開發。
2021 年 2 月 9 日,Rust 基金會成立,Mozilla、亞馬遜、華為、谷歌和微軟作為創始白金成員,承諾在兩年時間里每年投入不少于 100 萬美元的預算,以用于 Rust 項目的開發、維護和推廣。
2022 年,Meta 宣布將 Rust 語言納入其服務器端編程語言。
2022 年 12 月,Linux 內核 6.1 發布,包含了初始 Rust 支持。
今年年初,谷歌宣布支持使用 Rust 開發 Chromium。
……
在這眾多大廠之中,微軟對于 Rust 的重視與支持力度也一直未減。繼 5 月效仿 Linux 用 Rust 重寫部分 Windows 內核后,近來微軟在擁抱 Rust 上又進了一步:微軟在 GitHub 中發布了一系列開發工具包,讓開發者可以使用 Rust 語言來編寫 Windows 驅動程序。
對此,不少開發者在感慨:沒想到啊,Windows 在擁抱 Rust 方面居然走在了 Linux 前面!
在擁抱 Rust 的路上,微軟曾遭到反對
事實上,早在 2019 年 2 月,微軟工程師 Matt Miller 在以色列舉行的安全會議 BlueHat 上曾透露:從 2006 年到 2018 年,微軟旗下產品過去 12 年修復的所有漏洞中,有七成涉及的都是內存安全問題。
內存安全問題占比高的原因,主要是因為 Windows 大多是以 C 和 C++ 編寫的——著名的“內存不安全”語言。內存管理代碼只要有一個漏洞,就會導致大量的內存安全錯誤,從而可能引發遠程代碼執行或權限提升漏洞等攻擊。
對于這些潛在風險,同年 7 月微軟研究院發布了一份聲明,希望“在漏洞發生之前消除一整類漏洞”,并表示“滿足這些要求的最有前途的新系統編程語言之一,就是最初由 Mozilla 發明的 Rust”。從這份聲明可以看出,至少自 2019 年開始,微軟已打算在擁抱 Rust 上積極布局。
到了去年 9 月,微軟 Azure 首席技術官 Mark Russinovich 在 X(前推特)上發文,正式呼吁業界淘汰 C / C++,應改用更加安全的 Rust 語言:
說到語言,現在是時候停止用 C/C++ 啟動任何新項目了,在需要使用非 GC 語言的情況下使用 Rust。為了安全性和可靠性,業界應該宣布這些語言已被淘汰。
可不同于 Mark Russinovich 對 Rust 的支持和青睞,當時他的這則帖子下多是反對和質疑的聲音:“這完全是一個與顯示脫節、不切實際的想法。”
“這聽起來像是在指責語言本身而不是程序員。C++ 是門好語言,只是許多使用它的人基本上不懂編程,而換一種語言并不能解決這個問題。”
“Rust 是一種不錯的語言,但它甚至還未達到 1.0 版本,我認為我們不應該為了尚未經過實戰檢驗的語言而放棄久經考驗的語言。”
盡管不被看好,但微軟轉向 Rust 的決心依舊堅定。今年 5 月 Mark Russinovich 宣布微軟已用 Rust 重寫部分 Windows 內核:“如果你在 Windows 11 Insider ring 上,那么將首次感受到 Rust 在 Windows 內核中帶來的魔力。”
當時,或許是網友對微軟擁抱 Rust 的決定已逐漸接受,也或許是微軟解釋過并非是用 Rust 替換內核中 C/C++ 的整個“40 年工作”,而是將其中一些內部的 C++ 數據類型替換成 Rust,因此在這則帖子下大多是正面留言。
從 2019 年放出風聲,到已用 Rust 重寫部分 Windows 11 內核代碼,如今微軟擁抱 Rust 的程度仍在繼續加深:在 Github 上發布工具包,讓開發者能用 Rust 編寫 Windows 驅動程序——這無疑是為操作系統實現內存安全編程的關鍵一步。
仍處于早期開發階段,不建議“用于商業用途”
從 Mark Russinovich 在 X 上分享的 Github 鏈接來看,這個由微軟 Surface 團隊開發的新項目名為 windows-drivers-rs,是一個由多個 Rust 組件(Crates)組成的項目,可幫助開發人員用 Rust 開發 Windows 驅動程序。
該項目同時支持 WDM(Windows Driver Model)和 WDF(Windows Driver Foundation)兩種不同的驅動程序開發模型:WDM 驅動程序級別較低,與操作系統緊密相連,而 WDF 驅動程序則通過框架庫與系統交互。
據介紹,windows-drivers-rs 具體包含以下板塊:
wdk-build:一個用于配置 Cargo 構建腳本的庫,可用于綁定生成和 WDK(Windows Developer Kit)的下游鏈接。
wdk-sys:將 FFI 直接綁定到 WDK 中提供的 API。
wdk:與 WDK 中的 API 安全綁定。
wdk-panic:使用 WDK 構建的程序的默認 panic 處理程序實現。
wdk-alloc:為使用 WDK 編譯的二進制文件提供分配支持。
wdk-macros:宏集合,有助于更輕松地與 wdk-sys 的直接綁定進行交互。
如需查看 windows-drivers-rs 用于創建驅動程序的示例,開發者可前往:https://github.com/microsoft/Windows-rust-driver-samples。
值得注意的是,微軟補充:雖然該項目的計劃靈活運用不同的 WDK 版本和不同的 WDF 版本,但目前“僅針對 NI eWDK、KMDF 1.33、UMDF 2.33 和 WDM 驅動程序進行了測試”,對于“較舊的 DDK 可能會缺少鏈接器選項”。
此外,微軟還表示 windows-drivers-rs 目前仍處于早期開發階段,因此“不建議用于商業用途”,但鼓勵社區和開發者對其進行試驗、建議和反饋,并將利用 GitHub 論壇作為與社區互動的主要形式。
開發者提問:Rust 如何處理異常?
就目前而言,已有少數開發者提出了當前這個旨在助力開發者用 Rust 開發 Windows 驅動程序的新工具平臺存在的一些問題,其中一個引起討論的問題就是 Rust 如何處理異常。
一位開發者指出:“對于 Windows 內核(以及整個操作系統)來說,結構化異常處理是 Windows 開發不可或缺的一部分,也是讓 Rust 成為 Windows 內核開發現實的真正障礙。”
但與其他編程語言不同,在 Rust 語言中沒有異常這一說,它通常用 Result 類型來處理可恢復的錯誤,而在遇到不可恢復的錯誤時,Rust 會提供一個特殊的宏 panic!。當執行這個宏時,程序會打印一段錯誤提示信息,展開(unwind)并清理當前的棧(Stack),然后退出程序的執行。
正如另一位開發者所說,“Windows 內核中的 Panic 往往是最后的手段,只應保留給內核已損壞且無法恢復的情況”,因此不少人認為 Rust 調用 Panic 的方式“在內核代碼中是不可取的,這可能會導致系統崩潰”。
那么,你對于 Rust 進一步入駐 Windows 的趨勢又有何看法呢?
參考鏈接:
https://github.com/microsoft/windows-drivers-rs
https://devclass.com/2023/09/25/microsoft-posts-early-stages-code-for-developing-windows-drivers-in-rust/
歡迎參與 CSDN 重磅發起的《2023 AI 開發者生態調查問卷》,分享您真實的 AI 使用體驗,更有精美好禮等你拿!