ARM平臺的虛擬化介紹

来源:http://www.cnblogs.com/miachel-zheng/archive/2017/05/09/6829801.html
-Advertisement-
Play Games

本篇博文主要介紹虛擬化的基本思想以及在arm平臺如何做虛擬化,arm提供的硬體feature等等。 虛擬化技術簡介 虛擬化技術 虛擬化是一個概念,單從這個概念的角度來看,只要是用某一種物品去模擬另一種物品都可以稱為虛擬化,甚至於有些飯店用豆腐做出肉的味道,我認為這也可以稱為一種虛擬化。但是這裡我們主 ...


本篇博文主要介紹虛擬化的基本思想以及在arm平臺如何做虛擬化,arm提供的硬體feature等等。

虛擬化技術簡介

虛擬化技術

虛擬化是一個概念,單從這個概念的角度來看,只要是用某一種物品去模擬另一種物品都可以稱為虛擬化,甚至於有些飯店用豆腐做出肉的味道,我認為這也可以稱為一種虛擬化。但是這裡我們主要討論的是電腦領域的虛擬化,我們這樣定義虛擬化“虛擬化是將單一物理設備模擬為相互隔離的多個虛擬設備,同時保證這些虛擬設備的高效性”。這個概念的定義里還包含了對虛擬化的要求,也就是這裡的隔離性(isolated)和有效性(efficient)。我們常說的hypervisor,有些書也把它稱為VMM(virtual machine monitor)則是一個直接運行在物理硬體上的軟體,它的功能就是管理物理硬體,以便在不同的虛擬機之間共用這些物理資源(cpu,記憶體,外設等等),既然hypervisor直接給物理外設打交道,那它當然需要運行在特權模式了,在過去沒有virtualization extesion的情況下,guest os和guest application只能都運行在de-privileged模式,如下圖所示。

Popek和Goldberg有一篇虛擬化的經典論文,把需要在特權模式下執行的指令分成了兩類:

  • sensitive instructions(敏感指令):這些指令試圖去更改系統資源的配置信息,或者它的執行結果依賴於系統的狀態。
  • privileged instructions(特權指令):這些指令在非特權模式下會trap(產生異常,陷入中斷向量表),在特權模式下可以正常執行。

Popek和Goldberg提出構建hypervisor的要求:敏感指令是特權指令的子集。這種標準現在被稱為classically virtualized(經典可虛擬化模型),雖然在不滿足這個要求的情況下也可以做虛擬化(二進位翻譯技術,後面會介紹),但是如果滿足這個要求,實現起來會容易很多。下麵介紹現有的虛擬化技術:

  • Pure virtualization(完全虛擬化):完全虛擬化要求硬體架構是可虛擬化的(符合經典可虛擬化模型),當trap進入hypervisor後,由hypervisor去模擬敏感指令的執行,這項技術也被稱為trap-and-emulate。當一個guest os想要去訪問特權資源(物理外設),就會產生一個trap喚醒hypervisor,hypervisor去模擬這個訪問,然後返回到guest os的下一條指令去繼續執行。如下圖所示,紅色箭頭表示一個trap。可以看出,每一條特權指令都需要很多條指令去模擬,所以這種trap-and-emulate開銷非常大,對系統性能有很大影響。

  • Binary rewriting(二進位重寫):二進位重寫就是當硬體架構不可虛擬化(不符合經典可虛擬化模型)採用的方法。它可以分為靜態的和動態的,靜態的二進位重寫是通過掃描ELF文件,把所有的敏感指令替換成一個trap指令(系統調用指令),或者用一些非敏感指令去模擬執行這條敏感指令,動態的二進位重寫對敏感指令的處理和靜態的類似,只不過它是在運行時去逐條分析指令,其實這種方法更不好,因為不管是不是敏感指令,都需要逐條分析才能確定,非常耗時,靜態方法在運行時的性能要好過動態方法,但是經常出現一些莫名其妙的錯誤,因為運行時狀態非常複雜,靜態修改很難預料到所有情況。上述過程如下圖所示。

  • para-virtualization(類虛擬化):這種虛擬化方法,很多書都把它譯為半虛擬化,其實這種譯法是不准確的,半虛擬化(partial-virtualization)是一個早已存在的技術,它只虛擬化部分外設來滿足某些專門的軟體的執行環境,但是不能運行所有可能運行在物理機上的軟體。如果讀者對此有疑問,請參閱《系統虛擬化:原理與實現》,intel開源技術中心和復旦大學並行處理研究所著,書中1.3節對此有討論。其實想解釋清楚這部分內容是很難的,還要理解各種各樣的虛擬化漏洞。簡單來說,類虛擬化通過修改guest os的源碼(API級),使得guest os避免這些難以虛擬化的指令(虛擬化漏洞)。操作系統通常會使用到處理器提供的全部功能,例如特權級別、地址空間和控制寄存器等。類虛擬化首先要解決的問題就是如何陷入VMM。典型的做法是修改guest os的相關代碼,讓os主動讓出特權級別,而運行在次一級特權上。這樣,當guest os試圖去執行特權指令時,保護異常被觸發,從而提供截獲點供VMM來模擬(也可以採用hypercall的方式,下麵介紹)。既然內核代碼已經需要修改,類虛擬化還可以進一步優化I/O。也就是說,類虛擬化不是去模擬真實世界中的設備,因為太多的寄存器模擬會降低性能。相反,類虛擬化可以自定義出高度優化的I/O協議,這種I/O協議完全基於事物,可以達到近乎物理機的速度。
    其實OKL4用的類虛擬化就是修改hypervisor提供給guest os的API(不同於底層硬體),同時修改guest os的源碼,把那些敏感指令換成hypercall(calls into hypervisor)。下圖展示的是,對於pure virtualization,硬體和hypervisor的API是相同的,但是對於para-virtualization是不同的。

虛擬化技術的比較

Pure virtualization and binary rewriting

pure virtualization和binary rewriting都是不修改機器的API的,所以任何guest os都可以直接運行在虛擬化環境。但是,由於所有的特權指令都會導致trap,所以在虛擬環境下特權指令的執行開銷要遠遠高於在native環境下。以前,x86和ARM都不符合classically virtualized時,VMWare採用binary rewriting在x86架構上實現虛擬化,經過優化後的性能開銷小於10%,但是這項技術十分複雜。由於實現起來的複雜,就會增加運行在特權模式下的代碼,這會增加attack surface和hypervisor出現bug的幾率,所以會降低整個系統的安全性和隔離性。

Para-virtualization

Para-virtualization雖然是一個新詞,在2002年中的Denali virtual machine monitor被提出來。但是這種設計理念早在1970的IBM的CMS系統就出現了,當時使用DIAG指令調用到hypervisor里去,並且一直到現在還有很多研究機構在使用這種理念,如Mach,Xen和L4。
Para-virtualization相比於pure virtualization可以提供更好的性能,因為它直接使用各種API而不是通過trap->decode->hardware emulation的過程來實現模擬。當然,它的缺點我也在之前的博客中提到過,那就是必須修改源碼,讓guest os使用新的API,這不僅是一項繁重的任務,同時對於一些非開源的操作系統,我們必須採用其他方法,除非這些非開源操作系統的廠商願意與我們合作。

Virtual memory in virtualization environment

為什麼要把記憶體管理部分單獨拿出來討論一下呢?因為這部分很複雜,其實之前我們討論的內容主要都是cpu運行的問題,比如各種指令和各種模式之間的切換。關於記憶體,我們先討論沒有引入guest os時的虛擬記憶體管理,然後再討論引入guest os之後的變化。
虛擬記憶體管理涉及的內容很多,這裡不討論各種記憶體分配演算法,如何降低缺頁率等等,只分析虛擬地址如何轉換成物理地址。我們知道,ARM架構是通過MMU+TLB來完成從VA(virtual address,虛擬地址)到PA(physical address,物理地址)的轉換,對於頁表的訪問實際上是由硬體自動完成的(如果不缺頁的話)。但是加入了虛擬化之後,這個轉換就複雜了,guest page table不完成從va到pa的轉換,只是負責從guest va到guest pa的轉換,而由hypervisor完成由guest pa到實際物理地址的轉換,這個轉換過程如下圖所示。

這個圖表現的很清晰,但是想實現是非常難的,因為只有一個頁表基址寄存器,所以硬體無法識別是從guest va到guest pa的轉換還是va到pa的轉換,在沒有硬體支持的情況下,只能通過影子頁表才能實現,影子頁表的原理也是把兩步轉換(guest va->guest pa->pa)轉換為一步,中間的同步用hash來做。影子頁表在構建的時候,每次對guest page table的訪問都需要trap,由hypervisor把guest pa轉換成實際物理地址。如果讀者想瞭解這一塊內容,我建議深入學習一下KVM以前關於影子頁表的實現(由於x86的硬體支持,目前KVM已經放棄影子頁表),這裡我們沒辦法深入探討影子頁表,但瞭解它大致是怎麼一回事兒之後,我們可以分析以下它的性能。首先,它的性能一定非常不好,因為每次對guest page table的訪問都需要trap,而且每次guest page table的修改還需要同步到影子頁表上面,雖然用hash的方式能提速,但是相比於native環境性能差距比較大(NOVA做過一個實驗,光訪問頁表的性能損失大約是23%),而且實現起來非常複雜。Intel和ARM對這一部分都提供了硬體支持,由硬體來完成這裡提到的兩級頁表轉換。其實根據程式運行時的局部性原理,如果每次訪問都能TLB hit的話,這種二級頁表轉換和一級頁表轉換差別不大,但是當TLB miss的時候,需要訪問two stage的頁表訪問的性能還是差別比較大的,儘管這部分由硬體來做。舉個例子,比如KVM在Linux-64位的情況下,是4級頁表轉換,從va到pa需要訪問5次頁表,那麼引入two stage之後,就需要5*5=25次訪問頁表,讀者可以思考一下這裡為什麼是相乘的關係。

ARM介紹

我們首先介紹ARM架構里的各個部分,介紹它們的目的是為了理解當arm引入virtualization extension之後對它們的影響。

ARM總體介紹

arm是一種精簡指令集(reduced instruction set computer,RISC)架構,精簡(reduced)的意思是每條彙編指令獨自完成所有的工作,而與之相對的複雜指令集則不是,它的一條彙編指令可能會翻譯成好幾條機器指令。大部分精簡指令集的指令都在單個時鐘周期內完成,它採用一種讀取和存儲分開的架構(load-store architecture),數據處理指令和I/O指令是分開的,數據處理指令是操作一個寄存器的值,和複雜指令集不同,關於複雜指令集的對應操作讀者請自行查閱資料。現在arm已經推出v8架構,關於v8架構我還不太熟,所以這裡以v7作為介紹(後續有時間我會研究下v8,在這裡進行補充)。v7架構包含16個32bit的通用寄存器,還有一些寄存器是和特定的處理器模式相關的,還有各種協處理器的寄存器,這些寄存器將會在後面展開敘述。

ARM協處理器介紹

ARM協處理器是ARM架構的重要擴展,ARM架構允許最多16個協處理器,其中cp15被保留完成各種控制。cp15作用非常強大,它控制整個系統配置,cache和TLB的管理,MMU的控制和系統性能監控,我們這裡主要討論cp15的記憶體管理功能。
當cpu想要訪問一個虛擬地址的時候,它首先去TLB裡面查這個虛擬地址和ASID,都匹配的話就可以直接返回物理地址,cpu通過物理地址去訪問物理記憶體就行了。如果TLB miss,MMU就會去訪問頁表,然後找到這個虛擬地址對應的物理地址,把這個虛擬地址、當前進程的PID、物理地址加入到TLB中去,然後返回物理地址給cpu去訪問記憶體。這裡面涉及的很多細節這裡不討論了,建議讀者去參看ARM官方介紹。

處理器模式和TrustZone

ARM v7包含8種處理器模式(在v8已經變成4種exception level了,從EL0到El3),其中包含1種非特權模式和7種特權模式:

  • 特權模式:FIQ、IRQ、supervisor、monitor、abort、undefined、system;
  • 非特權模式:user。

顯然,除了應用程式運行在user模式以外,其他全部運行在特權模式。ARM的virtualization extension需要處理器支持TrustZone extension,我們來看一下TrustZone是什麼。TrustZone將處理器的執行狀態分為兩個世界:

  • secure world:用於運行可信軟體;
  • non-secure world:用於運行不可信軟體。

這裡的兩個世界和處理器模式是重疊的,軟體可以在任何模式、任何世界上運行。那麼secure world和non-secure world的區別在哪呢?這裡的secure又從何而來?是這樣的,secure world有自己獨有的記憶體和外設,這部分內容只有運行在secure world的軟體可以訪問,運行在non-secure world的軟體是不可以訪問的。這裡引入了一個新的處理器模式,monitor mode,它運行在secure world,被用於做雙系統(secure and non-secure world)之間的切換,如下圖所示。

我們可以基於TrustZone去做虛擬化,因為它能夠隔離記憶體、中斷並且確保non-secure world的特權軟體也不可能訪問或者修改運行在secure world的軟體的配置信息。然後這樣做的缺陷是,在non-secure world只能運行一個guest os,在secure world運行一個hypervisor。Green Hill的INTEGRITY就是這樣做的,感興趣的讀者可以去Google一下。

中斷控制器

GIC(generic interrupt controller)是ARM里的中斷控制器,現在也已經支持virtualization extension。GIC可以分為兩部分:

  • Distributor(分發器):分發器負責接收中斷,設置這個中斷是否enable和它的優先順序,之後把它送到對應的cpu interface上去。
  • CPU interface(中斷介面):這部分負責屏蔽低優先順序中斷(相對於正在處理的中斷的優先順序),讓高優先順序的中斷搶占cpu。

當外設產生中斷的時候,這個中斷首先發送給Distributor,Distributor將這個中斷發送給對應的cpu interface。當cpu interface接受到這個中斷的時候,它會檢查這個中斷是否enable,如果enable再去比較這個中斷的優先順序和當前正在處理的中斷的優先順序,進而決定處理器是否立即處理這個中斷。

Virtualization Issues with the ARM Architecture

標準的ARM架構是不符合可虛擬化模型的,有很多敏感指令在非特權模式下執行卻不會產生trap。比如CPS指令,這條指令的作用是改變處理器狀態,當這條指令在用戶態執行時不會產生trap,甚至沒有任何效果,可以認為是簡單的跳過。即使所有的敏感指令都會產生trap,在ARM架構上用上述的trap-and-emulate技術也是很困難的,因為ARM的敏感指令非常多,只要和特權資源交互的指令都是敏感指令,比如虛擬記憶體子系統,中斷控制子系統和協處理器,用上述方式的話開銷太大,對系統性能有很大衝擊。比如,arm-v7架構不支持頁表訪問的虛擬化,那麼就需要影子頁表,每次訪問guest pa都需要trap,同樣地,中斷控制器也需要被模擬,當中斷很頻繁的時候(timer tick),這種模擬的開銷也是非常大的,為了剋服這種種弊端,ARM推出了virtualization extension。

ARM對虛擬化的硬體支持

在討論arm新增加的virtualization extension之前,我們知道對硬體虛擬化的支持主要有intel的VT-x和AMD的AMD-V,它們兩個十分類似,所以這裡我們只介紹VT-x,看看它對虛擬化做了怎樣的支持(為後面做對比)。

  • 將cpu的模式分為hypervisor(VMX root operation)和guest(VMX non-root operation)。
  • 可以配置一些敏感指令和事件,讓它們產生或者不產生trap。
  • (新增)提供擴展頁表(EPT,extended page table),通過這個頁表在硬體上完成second-stage of translation,其實就是常說的二級頁表翻譯。
  • (新增)在TLB上新增加了VM tag去標識每一個虛擬機,這樣可以避免每次VM-entry和VM-exit時的TLB flush操作(其實還增加了VPID,去標識VM里虛擬進程的進程id)。
  • (新增)在Intel的 VT-d里增加了對DMA操作的支持,而且是一種安全的DMA(具體怎麼實現的安全讀者可以自己分析下)。

接下來我們看看ARM對虛擬化的支持,這裡討論的虛擬化支持主要是針對v7架構,並且需要實現上文提到的TrustZone。利用硬體擴展實現pure virtualization的總體架構如下圖所示:

  • hypervisor運行在non-secure world里的hyp mode,這個hyp mode使hypervisor可以管理non-secure world里其他所有的模式(user mode和kernel mode)里運行的軟體。
  • guest os運行在non-secure world的特權模式(kernel mode),guest application運行在non-secure world的user mode。

上述內容是對虛擬化擴展的一個總體介紹,具體來說,ARM新增了以下幾個feature:

  • hyp mode:hyp mode是運行在non-secure world的最高特權級模式,如上圖所示。它負責管理guest os,hypervisor運行在這個新的模式里。這個模式將hypervisor和運行中的guest os分開,guest os運行在non-secure的kernel mode。
  • Second-stage of translation:由hypervisor負責把所有的gust pa轉換成實際物理地址,其實就是從物理上支持兩級頁表轉換,而不需要使用影子頁表。
  • 中斷控制:這部分後面展開敘述。
  • 模擬支持:當trap進hypervisor時,硬體向hypervisor提供一些額外的信息,消除了hypervisor取指令然後decode的開銷。因為對外設的模擬需要採用trap-and-emulate技術,削減這項技術的開銷可以有效的提升性能。
  • trap配置:不是所有的敏感指令或特權操作都需要trap進hypervisor進行處理,我們可以配置指令或操作是否trap,這樣可以減少不必要的trap,從而減少開銷,提升性能。

讀者可以自行對比Intel和ARM對虛擬化支持的相同點和不同點,分析他們為什麼這樣做。接下來展開敘述上文提到的中斷控制部分。

中斷控制

ARM創建了一個新的硬體模塊,virtual CPU interface,類比我們前面在介紹GIC時提到過的CPU interface,這個硬體模塊可以直接被map到guest os里,從而避免使用trap-and-emulate去模擬CPU interface,guest os可以直接操作這個virtual CPU interface,例如開、關中斷。當然關於GIC的另一個部分,Distributor,我們仍然需要通過trap-and-emulate去模擬,但是它對性能的影響不大,因為它只是在初始化的時候負責enable中斷,之後就不再修改了。
當中斷到來時,所有的中斷都首先被送到hypervisor里進行處理,由hypervisor通過virtual CPU interface發送給當前正在執行的guest os。虛擬中斷可以和物理中斷進行映射,這樣guest os就可以直接操作物理中斷而不需要通過hypervisor了。下圖所示是一個中斷處理流程:

  1. 外設產生一個中斷發送到Distributor
  2. Distributor把這個中斷發送給CPU interface
  3. CPU interface告訴hypervisor去處理這個中斷
  4. hypervisor對這個中斷進行檢查,發現這個中斷是送給guest os處理的,它會設置一個虛擬中斷,將物理中斷和虛擬中斷連接在一起,把這個虛擬中斷加入到virtual CPU interface。
  5. virtual CPU interface會根據hypervisor加入的虛擬中斷向guest os發送一個中斷
  6. guest os通過virtual CPU interface發來的中斷進行處理,處理之後返回
  7. virtual CPU interface發現這個虛擬中斷來自於一個物理中斷,就會在Dirtributor上清除這個物理中斷(表示處理完畢),整個虛擬中斷處理過程結束。
    這裡面還涉及一個硬體擴展,論文上把它稱為“priority drop”。正常情況下,當一個中斷正在處理的時候,低優先順序的中斷是不能夠搶占處理器的,但是在虛擬化環境卻不是這樣,比方說有兩個guest os,我們暫且稱之為os1和os2,假設os1正在處理一個高優先順序中斷,這時又有一個中斷是給os2處理的,這個中斷的優先順序低於os1的中斷的優先順序,但是它們應該互不影響才對。ARM加入這個硬體擴展就是為了處理上述問題,每個guest os都有自己的優先順序屏蔽策略,互不影響。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在ElasticSearch 2.4版本中,文檔存儲的介質分為記憶體和硬碟:記憶體速度快,但是容量有限;硬碟速度較慢,但是容量很大。同時,ElasticSearch進程自身的運行也需要記憶體空間,必須保證ElasticSearch進程有充足的運行時記憶體。為了使ElasticSearch引擎達到最佳性能,必... ...
  • 最近在學linux,使用ssh遠程登陸linux,記錄下來! 首先進入/etc目錄下,/etc目錄存放的是一些配置文件,比如passwd等配置文件,要想使用ssh遠程登陸,需要配置/etc/ssh/sshd_config文件里的配置信息,使用vim編輯,在命令行模式下輸入 vim /etc/ssh/ ...
  • 在本科期間學習過進程,網上也有很多關於進程的知識,但對於進程到底是什麼一直沒有讓我滿意的解答(以下截圖來自網路,感覺對進程理解有問題,說得也很虛)。 但今天找到這個博文感覺挺符合我的胃口:http://blog.csdn.net/yuqiang_ee_android/article/details/ ...
  • cat [功能說明] 建立文件 #cat命令用來串接文件或顯示文件內容的但是如果從標準輸入設備中讀入數據並將結果重定向到一個新的文件中,則可以到達建立新文件的目的。Cat命令只能在編輯新的文件時只能從鍵盤接收數據,不能靈活的對文件的內容編輯 因此靈活性遠遠不如專門的文本編輯工具,只能用來建立簡單的文 ...
  • 一、Apache 主要配置文件註釋Apache的主配置文件:/etc/httpd/conf/httpd.conf預設站點主目錄:/var/www/html/Apache伺服器的配置信息全部存儲在主配置文件/etc/httpd/conf/httpd.conf中,這個文件中的內容非常多,用wc命令統計一 ...
  • 常用的文件查看命令 cat 常用參數 -n 顯示行號 -E 顯示行尾結束符 $ -b 顯示非空行編號 -s 連續的空行合成一行 -A 顯示隱藏字元 通常用法 cat -An 當然還有cat 的雙胞胎,將文件倒著看的 tac more 分頁查看文件內容,空格鍵翻頁,q鍵退出。 3 head 查看文件首 ...
  • Docker包含三個概念:(1)遠程倉庫即遠程鏡像庫所有鏡像的聚集地(不可進入操作)。(2)本地鏡像即從遠程倉庫拉取過來的鏡像(3)運行起來的本地鏡像叫做容器(分層的可操作)Docker使用:1.首先通過第三方鏡像庫(c.163.com)拉取相應的信息比如centos,ubuntu,tomcat等作 ...
  • 以下內容參考自:http://www.cnblogs.com/hehexiaoxia/p/4042583.html 安裝好虛擬機以後,系統會新增兩個虛擬網卡。其中VMnet1網卡連接到Host-only模式的網路,VMnet8網卡連接到NAT模式的網路。 Bridged模式 該模式下,虛擬機的網卡直 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...