車輕了反而安全 到底是高科技還是忽悠?

看過F1比賽都知道,就算賽車被撞個稀巴爛,賽車手最後還是能夠安然無恙的從一堆廢鐵里走出來。這都是得益於高強度的材料和科學合理的車身結構。邁銳寶XL的安全性如何。邁銳寶XL比上代車型減重了120kg,人們也可能會懷疑現在的輕量化汽車的碰安全性是不是就比舊時皮糙肉厚的美系車要差很多。

隨着人們環保意識的增強,汽車的輕量化成為了幾乎所有車企的重要課題。雪佛蘭的邁銳寶XL就是輕量化的一個典型例子,邁銳寶XL比上代車型邁銳寶重量足足減少了120kg。那麼我們最關心的問題來了,輕量化之後重量降低了的汽車在安全性方面真的比所謂“皮糙肉厚”的傳統汽車要差嗎?認為不一定。

為啥要減重?

大家都知道汽車的油耗跟車輛前進時受到的阻力有莫大的關係,包括空氣阻力和摩擦阻力等等,而車的重量又是這些阻力的重要影響因素。國外有研究表明,若汽車整車重量降低10%,燃油效率可提高6%—8%。而邁銳寶XL,其減重之後百公里油耗低至6L,這對於一輛中型車來說已經十分優秀了。

除了省油,汽車輕量化的最大優點之一就是提升了車輛的操控性。舊時的人們認為車輛夠大,夠重,高速穩定性就好。但實際上一輛車身輕但是車身結構整體性好的汽車,其高速穩定性也十分好。除此以外,輕量化的車身在加速、轉向、剎車等個方面都會有更加優秀的表現,某程度上甚至加強了主動安全性能。

減重后的汽車安全性怎樣?

要看一輛車的安全性,不是怎麼吹噓,或者怎麼羅列一些數據就可以讓別人相信的,最直觀的體現還是要看碰撞測試的表現。邁銳寶XL在IIHS的五大測試項目中均取得“優”的評價,要知道這其中包括了堪稱最慘烈碰撞的25%正面偏置碰撞。

下圖為進行25%正面偏置碰撞后的邁銳寶XL,可以看出A柱變形程度極低,車內成員空間也幾乎沒有縮減,難怪會得到IIHS官方“優”的評價。

要說到汽車輕量化,F1賽車就是最典型的例子。在F1賽車上,每一克重量都是十分寶貴的,因此F1賽車採用極高強度的單殼體車身,但單殼體車身的重量僅為35公斤。看過F1比賽都知道,就算賽車被撞個稀巴爛,賽車手最後還是能夠安然無恙的從一堆廢鐵里走出來。這都是得益於高強度的材料和科學合理的車身結構。

邁銳寶XL的安全性如何?

邁銳寶XL比上代車型減重了120kg,人們也可能會懷疑現在的輕量化汽車的碰安全性是不是就比舊時皮糙肉厚的美系車要差很多?事實上並不是的。

車身材料和結構決定了該車的安全性,邁銳寶XL的車身材料採用40%的超高強度鋼,最高抗壓強度達到1500Mpa。

這40% 的超高強度鋼不是隨便加入到車身的,是工程師通過高精度的仿真迭代,找出關鍵的位置來進行強化的。所以我們從邁銳寶XL的動力總成誘導轉向柱和座椅調節導軌響應示意圖可以看出其車身扭曲形變的幅度在優化後有明顯的減少。

而在車身結構方面,邁銳寶XL的工程師也沒少下苦心,邁銳寶XL採用通用全新中高級車平台,其低阻整車架構讓邁銳寶XL更低也更輕了。而減少的這些重量也是工程師通過結構拓撲分析實現的。除此之外,與安全息息相關的車頭也採用了新型的合成設計空間,能在碰撞時提供更好的吸能緩衝表現,這也是為什麼邁銳寶XL的IIHS碰撞成績那麼好。

邁銳寶XL的超高強度鋼和優異的車身結構在被動安全方面給乘客保駕護航。而在主動安全方面,邁銳寶XL全系標配博世第9代电子安全系統,共有20項子系統,能夠全系標配博世的电子安全系統,足見邁銳寶XL對安全的重視。

除了主動和被動安全,邁銳寶XL更配備十項电子智能安全系統,包括ACC自適應巡航系統、FCA前碰撞預警、AHBA遠近光燈自動控制、SBZA側盲區報警系統等等,讓駕駛者更容易發現潛在的危險,最大程度的避免事故發生。

總結:其實汽車安全是由多個因素共同影響的,主動安全方面的电子安全系統能夠減少碰撞發生的概率;而高強度的材料和科學的結構能夠減輕碰撞時的傷害,邁銳寶XL在這兩點都做得很好。而最後也是最重要的一點,就是人的操控,目前來說,強度再大的車身在高速碰撞中都可能造成人員傷亡。所以盲目的批判減重后的汽車安全性會下降,其實並不科學的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

明(25)日 Redmi 即將發表 Redmi K40 系列雙旗艦新機,不過在這之前於小米中國官網悄悄推出了新款「小米GaN充電器 Type-C 33W」,這款 GaN 氮化鎵充電器擁有超小的體積、採用單 Type-C 接口、還佛心附贈一條 Type-C 充電線,售價僅約新台幣 340 元。充電方面除了最高充電功率可達 33W ,也適用於 iPhone 快速充電,可說是目前 iPhone 快充最小巧且便宜的快速充電器選擇。

小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

根據過去傳聞指出,即將發表的 Redmi K40 系列旗艦新機可能最高支持 33W 快速充電,不過這次 Redmi K40 可能會跟進小米11 將盒裝「輕裝上陣」的設計,因此當初外界推測 Redmi 可能也會取消附贈充電器。不過在 Redmi K40 系列新機發表會之前,在中國小米官網也率先推出了新款 33W GaN 氮化鎵充電器,極有可能就是為了接下來將發表的 Redmi K40 系列作準備。

小米 GaN 充電器 Type-C 33W 之所以體積比起以往 33W 功率的充電器相比大幅縮小,其主因在於採用 GaN(氮化镓)是一種新型半導體材料,擁有優異的導熱效率、耐高溫和耐酸鹼等特性,過去也廣泛應用在航太和軍事領域,近年也陸續在充電器採用。GaN(氮化镓)充電器不僅可以做到更小的體積和更輕盈的重量,在充電功率轉換效率也比起非 GaN 充電器更高。

對比原本 33W 的小米USB-C 充電器,新款的「小米GaN充電器 Type-C 33W」體積只有 33 立方公分,體積整整縮小 56% 更便於攜帶:

充電功率方面,小米GaN充電器 Type-C 33W 最高支持 33W 快速充電,輸出支持 5V3A、9V3A、11V3A、12V2.5A :

另外,小米GaN充電器 Type-C 33W 也可搭配 Type-C 轉 Lightning 快充線為支持快充的 iPhone(例如: iPhone 12)進行快速充電,在快速充電下可讓 iPhone 12 在 30 分鐘充電達到 60% 。考量其價格、體積和充電功率,對於想輕鬆讓 iPhone 12 升級快充的 iPhone 用戶來說,它也是相當具吸引力的快速充電器。

另外,這款充電器也支持智慧辨識輸出電流,能為 iPhone、 iPad、Android 手機、Android 平板、 Switch 遊戲機進行充電,不過因輸出電壓限制,小米 GaN 充電器 Type-C 33W 不支持為筆記型電腦、20W(含)以上的小米無線充電器充電。

充電安全部分,小米 GaN 充電器 Type-C 33W 支援多重安全保護,同時提升充電效率:

小米 GaN 充電器 Type-C 33W 體積就像顆小豆腐頭一般,不過如此小的充電器的插頭也就無法收折算是稍顯可惜的一點:

最後是大家關心的售價,小米 GaN 充電器 Type-C 33W 雖然售價只要人民幣 79 元(約合新台幣 340 元),不過仍標配 3A 大電流的 C to C 充電線。

圖片/消息來源:小米商城(中國)

延伸閱讀:
小米米家筋膜槍眾籌推出:強勁動力有效放鬆筋膜,眾籌價約 1,942 元

星巴克 STAY COFFEE TOGETHER 數位體驗活動,連續 3 週飲料買一送一!(2/22-3/12)

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

不知不覺 Apple 在 MacBook Pro 加入 Touch Bar 觸控列已經也有近五年時間,不過上個月才傳出 2021 年 MacBook Pro 可能會取消 Touch Bar 並回歸實體鍵盤,當時就有傳聞指出有可能 MagSafe 磁吸充電和更多 I/O 介面也可能回歸。近日,根據天風證券分析師郭明錤(Ming-Chi Kuo)的報告預測,今年下半年登場的 2021 年款全新 MacBook Pro  幾有多個設計和規格的變化,將配備 SD 卡插槽與 HDMI 接口。

▲圖片來源:MacRumors

傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

根據天風證券分析師郭明錤(Ming-Chi Kuo)的預測, Apple 預計在今年下半年推出全新設計的 14 吋與 16 吋兩款 MacBook Pro ,在設計上將有許多對使用者實用的改變,包括之前曾提到將採用 mini-LED 顯示器、回歸經典的 MagSafe 磁吸充電、取消 Touch Bar 觸控列。另外,近日也提到新款的 MacBook Pro 將恢復在機身上配置 SD 記憶卡插槽和 HDMI 接口,這對於經常有外接螢幕、記憶卡傳輸需求的用戶來說,未來也將不必再另外攜帶 USB Hub 轉接器。
郭明錤也預測,新款 MacBook Pro 將配備 SD 卡插槽與 HDMI 接口,而此實用改變將有助於推升換機需求。

▲圖片來源:Apple Hub (Twitter/ @theapplehub)

然而,對於目前已經逐漸習慣以 USB-C 轉接其他像是 SD 記憶卡、 HDMI、 USB-A 、 RJ45 等介面的 MacBook Pro 使用者來說,只在有需要時才將 USB-C Hub 轉接器接上使用,平時也能減少接口未使用入塵的機會,也不完全是件壞事。另外,考量到機身設計最實際要面對的就是機身厚度限制,若回歸這些介面勢必會讓機身厚度再次增加。那麼,各位是否真的會希望 Apple 在新版 MacBook Pro 加回 HDMI 接口與 SD 記憶卡插槽呢?

消息來源:MacRumors

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

LINE 24 款免費貼圖整理:多款 LINE 貼圖免費下載!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

新一代華為 MateBook 14 輕薄筆電在台推出,美感、效能一步到位

華為旗下的筆電產品以及精緻、便利與高 CP 在台灣有一定的愛用者,今日(2/24)更宣布最新 MateBook 14 在台灣推出,延續過去兼顧精工雕琢的外表與效能平衡兩大方面,讓生產力與娛樂齊頭並進,為消費者開啟全新的 2K 輕薄螢幕新視野,多工處理體驗一次到位。

新一代華為 MateBook 14 輕薄筆電在台推出,美感、效能一步到位

MateBook 14 擁有鋁合金搭配 CNC 鑽切工藝,加上精密噴砂與鑽石倒腳切割,打造出手感細膩的高質感輕薄金屬機身,重量僅 1.49kg 恰到好處,低調簡約卻有耐看百搭。

搭配 3:2 顯示比例的 2K 超高解析度全螢幕,螢幕佔比達到 90%,並且覆蓋 100% sRGB 寬廣色域,通過德國萊茵低藍光護眼測試,視野清晰一覽無遺。

搭載 7nm 製程的 AMD Ryzen 4000H 系列行動處理器,與 AMD Radeon 系列顯示晶片,16GB 雙通道記憶體搭配 512GB PCIe NVMe SSD,小小的身軀裡面卻蘊含能涵蓋足以支援生產力、視訊短片與娛樂遊戲各方面的高效能。改良的全新鯊魚鰭熱系統,將葉片厚度減少 40%,葉片數量提升 49%,以增加 39% 風量、低噪音的雙風扇、雙導管來維持整體系統穩定性。

在鍵盤右上方則是電源搭配指紋辨識器:這款新機同樣地將視訊鏡頭隱藏於鍵盤之中,不用時就收起來,確保個人隱私無虞。華為筆電的另一個招牌功能「一碰傳」在這款同樣基因的產品上當然也沒有省略,加大觸控板碰傳區域,隨手傳超便利。內建 56Wh 大容量電池,配合華為自家的智慧省電技術,只需充電 15 分鐘就能使用 2.5 小時,隨附的 65W 口袋充電器重量輕巧僅 160g,隨時補充電量不給你負擔。

華為 MateBook 14 售價定為 30,990 元,上市期間購機就送 Office 365 個人版與羅技 M235 無線滑鼠,數量有限送完為止。另外,即日起至 2/28 止,只要在燦坤指定店點(請見下圖)、線上賣場與華為品牌店完成預購,就送市價 6,988 元的 ViewSonic 27 吋 2K 薄型顯示器一部。

整體算下來這台搭載 AMD 4600H處理器、16GB RAM、512GB SSD、全金屬機身、長效續航的筆電只賣30990已經相當高性價比了,如果扣掉價值近7000元的27吋螢幕、OFFICE365與滑鼠,等於這樣規格的筆電不到23000!相當划算,有興趣的朋友可以到華為品牌店庾燦坤門市購買(請點我):

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

小米10T Lite 將於 2/26 在台發表:搭載 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機

除了本週的 2021 小米元宵節活動有同步開賣 Redmi 9T 入門手機,今(24)日稍早小米台灣也正式宣佈即將在 26 日 12:30 正式在台推出「小米10T Lite」中階新機。小米10T Lite 搭載 120Hz 更新率變速螢幕、高通 Snapdragon 750G 5G 處理器、 6400 萬像素四鏡頭主相機、4820mAh大電池和 33W 快速充電,也將對於重視 CP 值的消費者再添一款平價 5G 可選擇!

▲圖片來源:小米香港官網

小米10T Lite 將於 2/26 在台發表:搭載 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機

去年原本小米都未在台灣推出手機,不過在去年底至今短短兩個個月陸續推出包括小米10 Lite 、小米10T、小米10T Pro、Redmi 9T、Redmi Note 9T 等新機,今(24)日稍早也宣佈將於 26 日直播活動在台推出「小米10T Lite」5G 中階新機。由於小米10T Lite 其實已經率先在香港推出,在台灣正式發表前也先帶各位「預習」一下小米10T Lite 的規格重點。

▲圖片來源:小米台灣 Xiaomi Taiwan (Facebook)

螢幕方面,小米10T Lite 搭載 6.67 吋 20:9 顯示比例、 FHD+ 解析度的居中挖孔全螢幕,機身前後採用康寧第五代大猩猩玻璃保護:

▲圖片來源:小米香港官網

顯示方面,小米10T Lite 除了支持 1500:1 對比度、 NTSC 84% 色域、 120Hz 螢幕更新率、 240Hz 觸控採樣率,螢幕也通過德國萊茵低藍光認證。另外,小米10T Lite 前後也配備智慧環境光感應器,能提供最舒適的視覺體驗。螢幕採用陽光螢幕 3.0 ,在陽光直射下自動增強對比度以確保畫面清晰可見:

▲圖片來源:小米香港官網

小米10T Lite 搭載 6400 萬像素四鏡頭主相機,採用 6400 萬像素 F/1.89 光圈主鏡頭(SONY IMX682 感光元件)、800 萬像素 120° 超廣角鏡頭、200 萬像素 4cm 微距鏡頭以及 200 萬像素景深鏡頭的搭配。

▲圖片來源:小米香港官網

前相機則配備 1600 萬像素前置自拍鏡頭:

▲圖片來源:小米香港官網

小米10T Lite 相機應用功能也相當豐富,像是也支持前後鏡頭同時錄影:

▲圖片來源:小米香港官網

硬體規格方面,小米10T Lite 搭載 Qualcomm Snapdragon 750G 5G 處理器,香港版本推出 6GB RAM 和 128GB ROM 單一規格,台灣版本確切規格則得等待小米台灣官方正式發佈為準。

▲圖片來源:小米香港官網

小米10T Lite 內建 4820mAh 大電量電池,支持 33W 快速充電能在 59 分鐘充電至 100% :
▲圖片來源:小米香港官網

▲圖片來源:小米香港官網

其他方面,小米10T Lite 支援雙頻 GPS、多功能 NFC、1216 線性揚聲器,生物辨識則採用結合電源鍵的側面指紋解鎖方案。

▲圖片來源:小米香港官網

小米10T Lite 也將於 2 月 26 日 12:30 的「小米5G手機超值選」直播正式登場,當日觀看直播還有機會抽小米手機(點我前往):

▲圖片來源:小米台灣官網

消息來源:小米台灣 Xiaomi Taiwan (Facebook)|小米台灣官網

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

小米米家筋膜槍眾籌推出:強勁動力有效放鬆筋膜,眾籌價約 1,942 元

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

Anker 率先為 iPhone 12 系列推出「磁吸無線充電行動電源」,外出充電更方便

前段時間我們曾報導過 iOS 14.5 Beta 2 暗示將推出 MagSafe 充電配件的消息,近期 Anker 這間致力於研發充電配件品牌也針對 iPhone 12 系列推出磁吸式無線充電行動電源「Anker PowerCore Magnetic 5K Wireless Power Bank」。凡是適用支援 MagSafe 的 iPhone 12 系列手機,就能直接將行動電源吸附在機身背面直接進行無線充電。

Anker 率先為 iPhone 12 系列推出「磁吸無線充電行動電源」,外出充電更方便

在 CES 2021 首次亮相的 Anker PowerCore 系列磁吸無線充電行動電源,能通過磁力吸附固定在 iPhone 12 系列背蓋上進行無線充電。近日這款行動電源也搶先 Apple 原廠率先推出、悄悄在 Amazon 上架。
有別於 Apple 原廠的聰穎電池護殼或第三方配件品牌的充電保護殼,這類磁吸無線充電行動電源將能讓使用者在未充電狀態能將行動電源收在包包,只在需要充電期間直接吸附上去即可進行充電。

Anker 這款行動電源內建 5,000mAh 的電池容量,可為 iPhone 12 mini 從 0% 充電至 100% 、 iPhone 12 和 iPhone 12 Pro 從 0% 充電至 95% 、 iPhone 12 Pro Max 從 0% 充電至 75% 。雖然行動電源電池容量不大、僅能作為電力補給使用,但對於不想攜帶充電線、作為一整天下來最後緊急補給電力使用,這款行動電源還是能滿足需求。除了能透過無線充電進行充電,這款行動電源也支持 USB-C 雙向輸出充電:

行動電源支持最高 5W 無線充電、磁性也夠強,能牢牢吸附在 iPhone 12 系列機身背蓋邊充電邊使用:

除了裸機能充電,這款行動電源也可搭配 MagSafe 保護殼的 iPhone 12 系列進行磁吸無線充電:

充電安全方面,也支持溫度保護、過充保護和異物檢測等安全機制:

售價方面, Anker PowerCore 磁西無線充電行動電源 5000mAh 的售價為 39.99 美元(約合新台幣 1,117 元),目前已在 Amazon 上架。

圖片/消息來源:Amazon

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

Go文件操作

目錄

  • 示例1: 打開和關閉文件
  • 示例2: 打開文件並讀取內容
  • 示例3: 一次性讀取文件
  • 示例4: 帶緩衝的Reader讀文件
  • 示例5: 創建文件並寫入內容
  • 示例6: 寫文件的四種方式
  • 示例7: 把一個文件內容寫入到另一個文件
  • 示例8:使用bufio獲取用戶輸入
  • 示例9: 判斷文件或目錄是否存在
  • 示例10: 拷貝文件、圖片音視頻
  • 示例11: 遍歷目錄
    • 遍歷目錄
    • 僅遍歷目錄,忽略文件
  • 示例12: 修改文件名
  • 示例13:創建目錄
  • 示例14:刪除文件

對於文件,我們並不陌生,文件是數據源(保存數據的地方)的一種,比如大家經常使用的word文檔,txt文件,Excel文件…等等都是文件。文件最主要的作用就是保存數據,它既可以保存一張圖片,也可以保存視頻,聲音……
文件在程序中是以流的形式來操作的。

:數據在數據源(文件)和程序(內存)之間經歷的路徑
輸出流:數據從程序(內存)到數據源(文件)的路徑
輸入流:數據從數據源(文件)到程序(內存)的路徑
輸入與輸出都是相對於內存而言的,從內存向外流就是輸出,從外部向內存流就是輸入

在Go中,我們操作文件的方法在os包中,會經常使用到os.File結構體 Go語言標準庫文檔

示例1: 打開和關閉文件

package main

import (
    "fmt"
    "os"
)

func main() {

    //打開文件(/Users/xxx/Go/src/file.txt)
    //概念說明:file的叫法
    //1.file 叫 file對象
    //2.file 叫 file指針
    //3.file 叫 file文件句柄
    file, err := os.Open("/Users/itbsl/Go/src/file.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
        //os.Exit(0)
    }
    defer func() {
        //文件及時關閉
        err = file.Close()
        if err != nil {
            fmt.Println("文件關閉失敗,原因是", err)
        }
    }()
}

示例2: 打開文件並讀取內容

使用Read()函數按照字節讀

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Printf("open file failed, err:%v\n", err)
		return
	}
	defer func() {
		err = file.Close()
		if err != nil {
			fmt.Printf("close file failed, err:%v\n", err)
		}
	}()

	var content []byte
	var tmp = make([]byte, 128)
	for {
		n, err := file.Read(tmp)
		//為什麼是tmp[:n]而不是tmp[:]?
		//因為當讀取到最後一行的內容長度不足tmp的長度的時候
		//新讀取的內容只會覆蓋前半部分上次讀取到的tmp的內容,
		//後半部分還是上一次讀取的內容,如果用tmp[:]就會導致
		//後半部分久內容又會被重新賦值一次,這其實是錯誤的
		content = append(content, tmp[:n]...)
		if err == io.EOF {//讀到文件末尾
			break
		}
	}
	fmt.Printf("讀取出來的內容為:\n")
	fmt.Printf("%q\n", string(content))
}

示例3: 一次性讀取文件

讀取文件內容並显示在終端,將文件內容一次性讀取到終端,適用於文件不大的情況。

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {

    //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt)
    file, err := ioutil.ReadFile("src/file.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
    }

    fmt.Printf("%s", string(file))
}

示例4: 帶緩衝的Reader讀文件

讀取文件的內容並显示在終端(帶緩衝區的方式),使用os.Open, file.Close,bufio.NewReader,reader.ReadString函數和方法。適合讀取大文件
1.使用ReadBytes方法
代碼1:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()

	//定義變量result用來存儲讀取結果
	var result string
	//創建一個帶有緩衝區的reader
	reader := bufio.NewReader(file)
	for {
		buf, err := reader.ReadBytes('\n')
		if err != nil && err == io.EOF { //EOF代表文件的末尾
			//注意:為什麼要判斷err是否等於io.EOF?
			//因為存在這種情況,文件有內容的最後那一行尾部沒有換行
			//當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種情況時就會報io.EOF錯誤
			//此時buf是讀取到了內容的,如果忽略掉了,那麼最終的讀取結果會少了最後一行的內容
			result += string(buf)
			break
		}
		result += string(buf)
	}
	fmt.Println(result)
}

代碼2:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()

	//定義變量result用來存儲讀取結果
	var result string
	//創建一個帶有緩衝區的reader
	reader := bufio.NewReader(file)
	for {
		buf, err := reader.ReadBytes('\n')
		if err != nil {
			if err == io.EOF { //EOF代表文件的末尾
			//注意:為什麼要判斷err是否等於io.EOF?
			//因為存在這種情況,文件有內容的最後那一行尾部沒有換行
			//當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種情況時就會報io.EOF錯誤
			//此時buf是讀取到了內容的,如果忽略掉了,那麼最終的讀取結果會少了最後一行的內容
				result += string(buf)
				break
			} else {
				log.Fatalf("ReadBytes failed, err: %v\n", err)
			}
		}
		result += string(buf)
	}
	fmt.Println(result)
}

2.ReadString方法

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {

    //打開文件
    file, err := os.Open("./files/test.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
        return
    }

    //當函數退出時,要及時的關閉file
    defer func() {
        //文件及時關閉
        err = file.Close()
        if err != nil {
            fmt.Println("文件關閉失敗,原因是", err)
        }
    }()

    //創建一個 *Reader,是帶緩衝的
    reader := bufio.NewReader(file)
    var result string
    //循環讀取文件內容
    for {
        str, err := reader.ReadString('\n') //讀到一個換行就結束
        result += str
        if err == io.EOF {//io.EOF代表文件的末尾
            //注意:如果文件最後一行文字沒有換行,則會一直讀取到文件末尾,
            //所以即使是判斷讀到了文件末尾,也要把讀取的內容輸出一下
            break
        }
    }
    fmt.Println(result)
}

示例5: 創建文件並寫入內容

第二個參數:文件代開模式(可以組合);第三個參數:權限控制(如0755)

package main

import (
	"fmt"
	"os"
)

func main() {

	//1.創建文件file.txt
	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE, 0755)
	if err != nil {
		fmt.Println("文件打開/創建失敗,原因是:", err)
		return
	}

	defer func() {
		err  = file.Close()
		if err != nil {
			fmt.Println("文件關閉失敗,原因是:", err)
		}
	}()

	//寫入數據
	var str = "暗黑西遊獅駝嶺,斗戰勝佛孫悟空。\n"

	for i := 0; i < 5; i++ {
		file.WriteString(str)
	}
}

示例6: 寫文件的四種方式

1.使用WriteAt()搭配Seek()方法實現寫文件功能

package main

import (
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()
    //Seek(): 修改文件的讀寫指針位置.
    //參數1: 偏移量. 正:向文件尾部偏移, 負:向文件頭部偏移
    //參數2: 偏移起始位置
    //       io.SeekStart: 文件起始位置
    //       io.SeekCurrent: 文件當前位置
    //       io.SeekEnd: 文件結尾位置
    //返回值:表示從文件起始位置,到當前文件讀寫指針位置的偏移量。
    //WriteAt(): 在文件指定偏移位置,寫入[]byte,通常搭配Seek()
    //參數1: 待寫入的數據
    //參數2: 偏移量
    //返回: 實際寫出的字節數
	for i := 0; i < 5; i++ {
		offset, _ := file.Seek(-3, io.SeekEnd)
		_, _ = file.WriteAt([]byte("你好"), offset)
	}
}

注意: 由於使用的OpenFile函數打開的文件,所以在選擇打開模式的時候不能選擇os.O_APPEND模式,因為該模式表示的是在文件末尾追加,這與WriteAt在指定的位置寫是想衝突的,雖然我在測試的時候加上os.O_APPEND模式並沒有報錯,但是代碼執行完之後發現,想要寫入的內容並沒有真正的寫入到文件中。
寫入前

寫入后

2.一次性寫文件

package main

import (
	"io/ioutil"
	"log"
)

func main() {
	str := "hello樹先生"
	//如果文件已存在,則會清空原來的內容,寫入新內容,如果文件不存在,則會創建文件並寫入內容
	err := ioutil.WriteFile("./test.txt", []byte(str), 0755)
	if err != nil {
		log.Fatalf("寫入文件錯誤,錯誤為:%v\n", err)
	}
}

3.使用帶緩衝的方式寫文件

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {

	//1.創建文件file.txt
	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0755)
	defer func() {
		err  = file.Close()
		if err != nil {
			fmt.Println("文件關閉失敗,原因是:", err)
		}
	}()

	if err != nil {
		fmt.Println("文件創建失敗,原因是:", err)
		return
	}

	//寫入數據
	var str = "你好,世界\n"

	//寫入時,使用帶緩存的*Writer
	writer := bufio.NewWriter(file)

	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}

	//因為writer是帶緩存的,因此在調用writeString方法時,其實內容是先寫入到緩存
	//因此需要調用Flush方法,將緩存數據寫入到文件中,否則文件中會丟失數據
	writer.Flush()
}

示例7: 把一個文件內容寫入到另一個文件

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {

    //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt)
    data, err := ioutil.ReadFile("src/1.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
    }

    err = ioutil.WriteFile("src/2.txt", data, 0755)

    if err != nil {
        fmt.Println("文件寫入失敗,原因是:", err)
    }
}

示例8:使用bufio獲取用戶輸入

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    var s string
    var reader = bufio.NewReader(os.Stdin)
    s, _ = reader.ReadString('\n')
    fmt.Printf("讀取到的內容為:%s\n", s)
}

示例9: 判斷文件或目錄是否存在

Go判斷文件或文件夾是否存在的方法為使用os.Stat()函數返回的錯誤值進行判斷:
(1)如果返回的錯誤為nil,說明文件或文件夾存在
(2)如果返回的類型使用os.IsNotExist()判斷為true,說明文件或文件夾不存在
(3)如果返回的錯誤為其它類型,則不確定是否存在

package main

import (
	"fmt"
	"os"
)

func main() {

	isExist, err := isFileExists("src/sfile.txt")
	if err != nil {
		fmt.Println("發生錯誤:", err)
	}

	if isExist {
		fmt.Println("存在")
	} else {
		fmt.Println("不存在")
	}
}

//判斷文件或者目錄是否存在
func isFileExists(path string) (bool, error) {

	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

示例10: 拷貝文件、圖片音視頻

io.Copy方法

package main

import (
	"fmt"
	"io"
	"os"
)
func CopyFile(srcFileName string, dstFileName string) (int64, error) {

	//源文件處理
	srcFile, err := os.Open(srcFileName)
	defer func() {
		err = srcFile.Close()
		if err != nil {
			fmt.Println("源文件關閉失敗,原因是:", err)
		}
	}()

	if err != nil {
		fmt.Println("源文件打開失敗,原因是:", err)
		return 0, err
	}

	//目標文件處理
	dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0755)
	defer func() {
		err = dstFile.Close()
		if err != nil {
			fmt.Println("目標文件關閉失敗,原因是:", err)
		}
	}()
	if err != nil {
		fmt.Println("目標文件打開失敗,原因是:", err)
		return 0, err
	}

	return io.Copy(dstFile, srcFile)
}

func main() {

	result, err := CopyFile("src/dst.jpeg", "src/哈哈.jpeg")

	if err == nil {
		fmt.Println("拷貝成功!拷貝的字節數為: ", result)
	}
}

對於大文件,我們還可以採用下面的方式

package main

import (
	"io"
	"log"
	"os"
)

func CopyFile(srcFileName string, dstFileName string) {
	//打開源文件
	srcFile, err := os.Open(srcFileName)
	if err != nil {
		log.Fatalf("源文件讀取失敗,原因是:%v\n", err)
	}
	defer func() {
		err = srcFile.Close()
		if err != nil {
			log.Fatalf("源文件關閉失敗,原因是:%v\n", err)
		}
	}()

	//創建目標文件,稍後會向這個目標文件寫入拷貝內容
	distFile, err := os.Create(dstFileName)
	if err != nil {
		log.Fatalf("目標文件創建失敗,原因是:%v\n", err)
	}
	defer func() {
		err = distFile.Close()
		if err != nil {
			log.Fatalf("目標文件關閉失敗,原因是:%v\n", err)
		}
	}()
	//定義指定長度的字節切片,每次最多讀取指定長度
	var tmp = make([]byte, 1024*4)
	//循環讀取並寫入
	for {
		n, err := srcFile.Read(tmp)
		n, _ = distFile.Write(tmp[:n])
		if err != nil {
			if err == io.EOF {//讀到了文件末尾,並且寫入完畢,任務完成返回(關閉文件的操作由defer來完成)
				return
			} else {
				log.Fatalf("拷貝過程中發生錯誤,錯誤原因為:%v\n", err)
			}
		}
	}
}

func main() {
	CopyFile("./worm.mp4", "./dist.mp4")
}

示例11: 遍歷目錄

遍歷目錄

package main

//我們讀寫的文件一般存放於目錄中.因此,有時需要指定到某一個目錄下,根據目錄存儲的狀況
//再進行文件的特定操作.接下來我們看看目錄的基本操作方法.
import (
	"fmt"
	"log"
	"os"
)
//打開目錄
//打開目錄我們也使用OpenFile函數,但要指定不同的參數來通知系統,要打開的是一個目錄文件.
//func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
//參數1: name,表示要打開的目錄名稱.使用絕對路徑較多
//參數2: flag,表示打開文件的讀寫模式
//參數3: perm,表示打開權限.但對於目錄來說有所不同,通常傳os.ModeDir.
//返回值:由於是操作目錄,所以file是指向目錄的文件指針.err中保存錯誤信息

//讀目錄內容
//這與讀文件有所不同.目錄中存放的是文件名和子目錄名.所以使用Readdir函數
//func (f *File) Readdir(n int) (fi []FileInfo, err error)
//如果n>0,Readdir函數會返回一個最多n個成員的切片。這時,如果Readdir返回一個空切片,
//它會返回一個非nil的錯誤說明原因。如果到達了目錄f的結尾,返回值err會是io.EOF。
//
//如果n<=0,Readdir函數返回目錄中剩餘所有文件對象的FileInfo構成的切片。
//此時,如果Readdir調用成功(讀取所有內容直到結尾),它會返回該切片和nil的錯誤值。
//如果在到達結尾前遇到錯誤,會返回之前成功讀取的FileInfo構成的切片和該錯誤。

func main() {
	//不推薦,因為通過查看ioutil.ReadDir()函數可知,官方使用的是os.Open()函數打開的目錄
	//file, err := os.OpenFile("./dir", os.O_RDWR, os.ModeDir)
	file, err := os.Open("./dir")
	if err != nil {
		log.Fatalf("文件打開失敗,原因是:%v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("文件關閉失敗,原因是:%v\n", err)
		}
	}()
	//Readdir方法返回一個FileInfo接口類型的切片和一個error類型的錯誤
	infos, err := file.Readdir(-1)
	for _, info := range infos {
		fmt.Printf("%v, %v\n", info.Name(), info.IsDir())
	}
}

僅遍歷目錄,忽略文件

方法1:使用os包

package main

import (
    "fmt"
    "os"
)

var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    fileInfo, err := file.Readdir(0)
    if err != nil {
        return err
    }

    for _, value := range fileInfo {
        if value.IsDir() {
            dirNames = append(dirNames, value.Name())
            err = traverseDir(filePath+pathSeparator+value.Name())
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {

    var filePath = "./dir"
    err := traverseDir(filePath)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(dirNames)
}

方法2:使用ioutil包

package main

import (
    "fmt"
    "io/ioutil"
    "os"
)

var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
    fileInfos, err := ioutil.ReadDir(filePath)
    if err != nil {
        return err
    }
    for _, fileInfo :=range fileInfos {
        if fileInfo.IsDir() {
            dirNames = append(dirNames, fileInfo.Name())
            err =  traverseDir(filePath+pathSeparator+fileInfo.Name())
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {

    var filePath = "./dir"
    err := traverseDir(filePath)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(dirNames)
}

示例12: 修改文件名

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

var pathSeparator = string(os.PathSeparator)
//重命名文件
func renameFileName(filePath string, old string, new string) error {
    files, err := ioutil.ReadDir(filePath)
    if err != nil {
        return err
    }
    for _, fileInfo := range files {
        if !fileInfo.IsDir() {
            err = os.Rename(filePath + pathSeparator + fileInfo.Name(),
                filePath + pathSeparator + strings.Replace(fileInfo.Name(), old, new, -1),
            )
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {
    var filePath = "./dir"
    err := renameFileName(filePath, "f", "kkk")
    if err != nil {
        fmt.Printf("錯誤: %v\n", err)
    }
}

示例13:創建目錄

package main

import (
	"fmt"
	"os"
)

func main() {
	//Mkdir使用指定的權限和名稱創建一個目錄。如果出錯,會返回*PathError底層類型的錯誤。
	err := os.Mkdir("./foo", 0755)
	if os.IsExist(err) {
		fmt.Println("目錄已存在")
		return
	}

	//MkdirAll使用指定的權限和名稱創建一個目錄,包括任何必要的上級目錄,並返回nil,否則返回錯誤。
	//權限位perm會應用在每一個被本函數創建的目錄上。如果path指定了一個已經存在的目錄,MkdirAll不做任何操作並返回nil。
	err = os.MkdirAll("./foo/bar", 0755)
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
}

示例14:刪除文件

package main

import (
	"fmt"
	"os"
)

func main() {
	//Remove刪除name指定的文件或目錄。如果出錯,會返回*PathError底層類型的錯誤。
	//該方法不能刪除非空目錄,如果想刪除目錄以及目錄下的所有文件,可以使用RemoveAll
	err := os.Remove("./def")
	if os.IsNotExist(err) {
		fmt.Println("您要刪除的文件或目錄不存在")
		return
	}
	if err != nil {
		fmt.Println(err)
	}

	//RemoveAll刪除path指定的文件,或目錄及它包含的任何下級對象。
	//它會嘗試刪除所有東西,除非遇到錯誤並返回。
	//如果path指定的對象不存在,RemoveAll會返回nil而不返回錯誤。
	err = os.RemoveAll("./def")
	if err != nil {
		fmt.Println(err)
	}
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

【原創】Linux中斷子系統(二)-通用框架處理

背景

  • Read the fucking source code! –By 魯迅
  • A picture is worth a thousand words. –By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

【原創】Linux中斷子系統(一)-中斷控制器及驅動分析講到了底層硬件GIC驅動,以及Arch-Specific的中斷代碼,本文將研究下通用的中斷處理的過程,屬於硬件無關層。當然,我還是建議你看一下上篇文章。

這篇文章會解答兩個問題:

  1. 用戶是怎麼使用中斷的(中斷註冊)?
  2. 外設觸發中斷信號時,最終是怎麼調用到中斷handler的(中斷處理)?

2. 數據結構分析

先來看一下總的數據結構,核心是圍繞着struct irq_desc來展開:

  • Linux內核的中斷處理,圍繞着中斷描述符結構struct irq_desc展開,內核提供了兩種中斷描述符組織形式:

    1. 打開CONFIG_SPARSE_IRQ宏(中斷編號不連續),中斷描述符以radix-tree來組織,用戶在初始化時進行動態分配,然後再插入radix-tree中;
    2. 關閉CONFIG_SPARSE_IRQ宏(中斷編號連續),中斷描述符以數組的形式組織,並且已經分配好;
    3. 不管哪種形式,最終都可以通過linux irq號來找到對應的中斷描述符;
  • 圖的左側灰色部分,主要在中斷控制器驅動中進行初始化設置,包括各個結構中函數指針的指向等,其中struct irq_chip用於對中斷控制器的硬件操作,struct irq_domain與中斷控制器對應,完成的工作是硬件中斷號到Linux irq的映射;

  • 圖的上側灰色部分,中斷描述符的創建(這裏指CONFIG_SPARSE_IRQ),主要在獲取設備中斷信息的過程中完成的,從而讓設備樹中的中斷能與具體的中斷描述符irq_desc匹配;

  • 圖中剩餘部分,在設備申請註冊中斷的過程中進行設置,比如struct irqactionhandler的設置,這個用於指向我們設備驅動程序中的中斷處理函數了;

中斷的處理主要有以下幾個功能模塊:

  1. 硬件中斷號到Linux irq中斷號的映射,並創建好irq_desc中斷描述符;
  2. 中斷註冊時,先獲取設備的中斷號,根據中斷號找到對應的irq_desc,並將設備的中斷處理函數添加到irq_desc中;
  3. 設備觸發中斷信號時,根據硬件中斷號得到Linux irq中斷號,找到對應的irq_desc,最終調用到設備的中斷處理函數;

上述的描述比較簡單,更詳細的過程,往下看吧。

3. 流程分析

3.1 中斷註冊

這一次,讓我們以問題的方式來展開:
先來讓我們回答第一個問題:用戶是怎麼使用中斷的?

  1. 熟悉設備驅動的同學應該都清楚,經常會在驅動程序中調用request_irq()接口或者request_threaded_irq()接口來註冊設備的中斷處理函數;
  2. request_irq()/request_threaded_irq接口中,都需要用到irq,也就是中斷號,那麼這个中斷號是從哪裡來的呢?它是Linux irq,它又是如何映射到具體的硬件設備的中斷號的呢?

先來看第二個問題:設備硬件中斷號到Linux irq中斷號的映射

  • 硬件設備的中斷信息都在設備樹device tree中進行了描述,在系統啟動過程中,這些信息都已經加載到內存中並得到了解析;
  • 驅動中通常會使用platform_get_irqirq_of_parse_and_map接口,去根據設備樹的信息去創建映射關係(硬件中斷號到linux irq中斷號映射);
  • 【原創】Linux中斷子系統(一)-中斷控制器及驅動分析提到過struct irq_domain用於完成映射工作,因此在irq_create_fwspec_mapping接口中,會先去找到匹配的irq domain,再去回調該irq domain中的函數集,通常irq domain都是在中斷控制器驅動中初始化的,以ARM GICv2為例,最終回調到gic_irq_domain_hierarchy_ops中的函數;
  • 如果已經創建好了映射,那麼可以直接進行返回linux irq中斷號了,否則的話需要irq_domain_alloc_irqs來創建映射關係;
  • irq_domain_alloc_irqs完成兩個工作:
    1. 針對linux irq中斷號創建一個irq_desc中斷描述符;
    2. 調用domain->ops->alloc函數來完成映射,在ARM GICv2驅動中對應gic_irq_domain_alloc函數,這個函數很關鍵,所以下文介紹一下;

gic_irq_domain_alloc函數如下:

  • gic_irq_domain_translate:負責解析出設備樹中描述的中斷號和中斷觸發類型(邊緣觸發、電平觸發等);
  • gic_irq_domain_map:將硬件中斷號和linux中斷號綁定到一個結構中,也就完成了映射,此外還綁定了irq_desc結構中的其他字段,最重要的是設置了irq_desc->handle_irq的函數指針,這個最終是中斷響應時往上執行的入口,這個是關鍵,下文講述中斷處理過程時還會提到;
  • 根據硬件中斷號的範圍設置irq_desc->handle_irq的指針,共享中斷入口為handle_fasteoi_irq,私有中斷入口為handle_percpu_devid_irq

上述函數執行完成后,完成了兩大工作:

  1. 硬件中斷號與Linux中斷號完成映射,併為Linux中斷號創建了irq_desc中斷描述符;
  2. 數據結構的綁定及初始化,關鍵的地方是設置了中斷處理往上執行的入口;

再看第一個問題:中斷是怎麼來註冊的?

設備驅動中,獲取到了irq中斷號后,通常就會採用request_irq/request_threaded_irq來註冊中斷,其中request_irq用於註冊普通處理的中斷,request_threaded_irq用於註冊線程化處理的中斷;

在講具體的註冊流程前,先看一下主要的中斷標誌位:

#define IRQF_SHARED		0x00000080              //多個設備共享一个中斷號,需要外設硬件支持
#define IRQF_PROBE_SHARED	0x00000100              //中斷處理程序允許sharing mismatch發生
#define __IRQF_TIMER		0x00000200              //時鐘中斷
#define IRQF_PERCPU		0x00000400              //屬於特定CPU的中斷
#define IRQF_NOBALANCING	0x00000800              //禁止在CPU之間進行中斷均衡處理
#define IRQF_IRQPOLL		0x00001000              //中斷被用作輪訓
#define IRQF_ONESHOT		0x00002000              //一次性觸發的中斷,不能嵌套,1)在硬件中斷處理完成后才能打開中斷;2)在中斷線程化中保持關閉狀態,直到該中斷源上的所有thread_fn函數都執行完
#define IRQF_NO_SUSPEND		0x00004000              //系統休眠喚醒操作中,不關閉該中斷
#define IRQF_FORCE_RESUME	0x00008000              //系統喚醒過程中必須強制打開該中斷
#define IRQF_NO_THREAD		0x00010000              //禁止中斷線程化
#define IRQF_EARLY_RESUME	0x00020000              //系統喚醒過程中在syscore階段resume,而不用等到設備resume階段
#define IRQF_COND_SUSPEND	0x00040000              //與NO_SUSPEND的用戶共享中斷時,執行本設備的中斷處理函數

  • request_irq也是調用request_threaded_irq,只是在傳參的時候,線程處理函數thread_fn函數設置成NULL;
  • 由於在硬件中斷號和Linux中斷號完成映射后,irq_desc已經創建好,可以通過irq_to_desc接口去獲取對應的irq_desc
  • 創建irqaction,並初始化該結構體中的各個字段,其中包括傳入的中斷處理函數賦值給對應的字段;
  • __setup_irq用於完成中斷的相關設置,包括中斷線程化的處理:
    1. 中斷線程化用於減少系統關中斷的時間,增強系統的實時性;
    2. ARM64默認開啟了CONFIG_IRQ_FORCED_THREADING,引導參數傳入threadirqs時,則除了IRQF_NO_THREAD外的中斷,其他的都將強制線程化處理;
    3. 中斷線程化會為每个中斷都創建一個內核線程,如果中斷進行共享,對應irqaction將連接成鏈表,每個irqaction都有thread_mask位圖字段,當所有共享中斷都處理完成后才能unmask中斷,解除中斷屏蔽;

3.2 中斷處理

當完成中斷的註冊后,所有結構的組織關係都已經建立好,剩下的工作就是當信號來臨時,進行中斷的處理工作。

來回顧一下【原創】Linux中斷子系統(一)-中斷控制器及驅動分析中的Arch-specific處理流程:

  • 中斷收到之後,首先會跳轉到異常向量表的入口處,進而逐級進行回調處理,最終調用到generic_handle_irq來進行中斷處理。

generic_handle_irq處理如下圖:

  • generic_handle_irq函數最終會調用到desc->handle_irq(),這個也就是對應到上文中在建立映射關係的過程中,調用irq_domain_set_info函數,設置好了函數指針,也就是handle_fasteoi_irqhandle_percpu_devid_irq
  • handle_fasteoi_irq:處理共享中斷,並且遍歷irqaction鏈表,逐個調用action->handler()函數,這個函數正是設備驅動程序調用request_irq/request_threaded_irq接口註冊的中斷處理函數,此外如果中斷線程化處理的話,還會調用__irq_wake_thread()喚醒內核線程;
  • handle_percpu_devid_irq:處理per-CPU中斷處理,在這個過程中會分別調用中斷控制器的處理函數進行硬件操作,該函數調用action->handler()來進行中斷處理;

來看看中斷線程化處理后的喚醒流程吧__handle_irq_event_percpu->__irq_wake_thread

  • __handle_irq_event_percpu->__irq_wake_thread將喚醒irq_thread中斷內核線程;
  • irq_thread內核線程,將根據是否為強制中斷線程化對函數指針handler_fn進行初始化,以便後續進行調用;
  • irq_thread內核線程將while(!irq_wait_for_interrupt)循環進行中斷的處理,當滿足條件時,執行handler_fn,在該函數中最終調用action->thread_fn,也就是完成了中斷的處理;
  • irq_wait_for_interrupt函數,將會判斷中斷線程的喚醒條件,如果滿足了,則將當前任務設置成TASK_RUNNING狀態,並返回0,這樣就能執行中斷的處理,否則就調用schedule()進行調度,讓出CPU,並將任務設置成TASK_INTERRUPTIBLE可中斷睡眠狀態;

3.3 總結

中斷的處理,總體來說可以分為兩部分來看:

  1. 從上到下:圍繞irq_desc中斷描述符建立好連接關係,這個過程就包括:中斷源信息的解析(設備樹),硬件中斷號到Linux中斷號的映射關係、irq_desc結構的分配及初始化(內部各個結構的組織關係)、中斷的註冊(填充irq_desc結構,包括handler處理函數)等,總而言之,就是完成靜態關係創建,為中斷處理做好準備;
  2. 從下到上,當外設觸發中斷信號時,中斷控制器接收到信號併發送到處理器,此時處理器進行異常模式切換,並逐步從處理器架構相關代碼逐級回調。如果涉及到中斷線程化,則還需要進行中斷內核線程的喚醒操作,最終完成中斷處理函數的執行。

歡迎關注個人公眾號,不定期分享Linux內核機制文章

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

為.netcore助力–WebApiClient正式發布core版本

1 前言

WebApiClient已成熟穩定,發布了WebApiClient.JIT和WebApiClient.AOT兩個nuget包,累計近10w次下載。我對它的高可擴展性設計相當滿意和自豪,但WebApiClient並不因此而停下腳步,在一年前,我產生了編寫其core版本的想法,將asp.netcore服務端先進的思想融入到core版本,在性能與擴展性上得到進一步升華。
對應的,給它叫了WebApiClientCore的名字,為了對得起名字裏面的Core字,我在框架設計、性能優化上佔用整體開發時間一半以上。

2 框架設計

IActionInvoker

WebApiClient時還沒有IActionInvoker概念,對應的執行邏輯直接在ApiActionContext上實現。現在我覺得,Context應該是一個狀態數據類,而不能也成為一個執行者,因為一個執行者的實例可以無限次地執行多個Context實例。

Refit則更簡單粗暴,將所有實現都在一個RequestBuilderImplementation的類上:你們只要也只能使用我內置的Attribute聲明,一切執行在我這個類裡面包辦,因為我是一個萬能類。

Core版本增加了IActionInvoker概念,從中Context分開,用於執行Context,職責分明。在實現上又分為多種Invoker:Task聲明返回執行者ActionInvoker、ITask聲明返回處理處理者ActionTask,以及聚合的MultiplexedActionInvoker。

Middleware思想

WebApiClient時在處理各個特性、參數驗證、返回值驗證時沒有使用Middleware思想,特別是在處理響應結果和異常短路邏輯難以維護。

Refit還是簡單粗暴,將所有特性的解釋實現都在這個RequestBuilderImplementation的類上,因為我還是一個萬能類。

Core版本增加中間件Builder,將請求前的相關Attribute的執行編排Build為一個請求處理委託,將請求后相關Attribute的執行編排Build為一個響應處理委託,然後把兩個委託與真實http請求串在一起,Build出一個完整的請求響應委託。

得益於Middleware,流程中的請求前參數值驗證、結果處理特性短路、異常短路、請求后結果值驗和無條件執行IApiFilterAtrribue等這些複雜的流程變成簡單的管道處理;另外接口也變成支持服務端響應多種格式內容,每種格式內容在一個IApiReturnAttribute上實現和處理,比如請求為Accept: application/json, application/xml,不管服務器返回xml或json都能處理。

/// <summary>
/// 創建執行委託
/// </summary>
/// <param name="apiAction">action描述器</param>
/// <returns></returns>
public static Func<ApiRequestContext, Task<ApiResponseContext>> Build(ApiActionDescriptor apiAction)
{
    var requestHandler = BuildRequestHandler(apiAction);
    var responseHandler = BuildResponseHandler(apiAction);

    return async request =>
    {
        await requestHandler(request).ConfigureAwait(false);
        var response = await HttpRequest.SendAsync(request).ConfigureAwait(false);
        await responseHandler(response).ConfigureAwait(false);
        return response;
    };
}

Context思想

WebApiClient只有一個ApiActionContext,其Result和Exception屬性在請求前就可以訪問或設置,但實際上就算設置了值,流程也不會短路和中斷,屬於設計失誤。

Refit沒有相關Context概念,因為它不提供給用戶自定義擴展Attribute的能力,它內置的Attribute也沒有執行能力,一個RequestBuilderImplementation類夠了。

Core版本將設計了多個Context概念,不同階段有不同的Context,如同asp.netcore不同Filter的Context也不同一樣。對於一個Action,請求階段對應是ApiRequestContext,響應階段是ApiResponseContext;對於Action的參數,對應是ApiParameterContext。每種Context裏面都包含核心的HttpContext屬性,HttpContext包含請求消息、響應消息和接口配置選項等。

Interface思想

輸入WebApiClientCore命名空間,會發現定義了很多Interface,這些Interface都是為了用戶實現自定義特性用的,當然內置的所有特性,都是實現了這些接口而已。如果一個特性實現了多個接口,它就有多種能力,比如內置的HeaderAttribute,它既可以修飾於Interface和Method,也可以修飾參數。

WebApiClientCore的Attribute描述的邏輯,是由Attribute自我實現,所以整個請求的數據裝配邏輯是分散為各個Attribute上,用什麼Attribute就有什麼邏輯,包含框架之外的非內置的自定義Attribute。

Refit的內置Attribute只有欲描述邏輯,沒有實現邏輯,實現邏輯由RequestBuilderImplementation包辦,所以它不需要接口也沒有接口。

3 性能優化

更快的字符串替換

像[HttpGet(“objects/{id}”)]這樣的path參數,在restful中常常遇到,通過Span優化,Core版本在替換path參數值cpu佔用時間降低為原版本的十分之一。

更快的json序列化

得益於Sytem.Text.Json,json序列化和反序列化上性能顯明提升。

更少的緩衝區分配

WebApiClientCore使用了可回收復用的IBufferWriter,在json序列化得到json、json裝配為HttpContent只申請一次Buffer,而且HttpContent在發送之後,這個Buffer被回收復用。IBufferWriter還於用表單的uri編碼,編碼產生的Buffer不用申請新的內存內容,直接寫入表單的HttpContent。

更少的編碼操作

WebApiClientCore的json不再使用utf16的string中間類型,直接將對象序列化為網絡請求需要的utf8編碼二進制json;表單的key和Value編碼時,也不產生string中間類型,而是編碼后的二進制數據內容,然後寫入表單的IBufferWriter。

更快的緩存查找

WebApiClient創建代理類實例來執行一個請求時,要查找兩次緩存:通過接口類型查找字典緩存的接口代理類,然後實例化代理類;在ApiInterceptor裏面通過MethodInfo查找字典緩存的ApiActionDescriptor。

Refit執行同樣邏輯也使用了兩次字典緩存,接口和接口代理類安全字典緩存TypeMapping,接口和接口方法描述的字典緩存interfaceHttpMethods。

WebApiClientCore取消了字典緩存,使用靜態泛型類的字段作緩存,因為字段訪問遠比字典查找高效。同時通過巧妙的設計,在代理類攔截方法執行時,直接回傳IActionInvoker替換原來的MethodInfo,IActionInvoker包含了ApiActionDescriptor,而IActionInvoker與代理類型都一起緩存在靜態泛型類的字段,減少了一次必須的字典緩存查找過程。

性能對比

排除掉真實的網絡請求IO等待時間,WebApiClientCore使用的cpu時間僅僅為WebApiClient.JIT和Refit的三分之一。

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.836 (1903/May2019Update/19H1)
Intel Core i3-4150 CPU 3.50GHz (Haswell), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.1.202
  [Host]     : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
  DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
Method Mean Error StdDev
HttpClient_GetAsync 3.146 μs 0.0396 μs 0.0370 μs
WebApiClientCore_GetAsync 12.421 μs 0.2324 μs 0.2174 μs
Refit_GetAsync 43.241 μs 0.6713 μs 0.6279 μs
Method Mean Error StdDev
HttpClient_PostJsonAsync 5.263 μs 0.0784 μs 0.0733 μs
WebApiClientCore_PostJsonAsync 13.823 μs 0.1874 μs 0.1753 μs
Refit_PostJsonAsync 45.218 μs 0.8166 μs 0.7639 μs

4 Nuget包與文檔

Nuget包

<PackageReference Include="WebApiClientCore" Version="1.0.*" />

項目地址與文檔

點擊項目鏈接,帶你GET到N種使用技能,不求star,只求提供良好建議。

https://github.com/dotnetcore/WebApiClient

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

【JVM故事】了解JVM的結構,好在面試時吹牛


class文件格式

參考上一篇文章《【JVM故事】一個Java字節碼文件的誕生記》,後續還會專門講解class文件的內部結構。

數據類型

jvm包括兩種數據類型,基本類型和引用類型。

基本類型包括,數值類型,boolean類型,和returnAddress類型。

數值類型包括,整型,浮點型,和char類型。

boolean類型同樣只有true和false。

returnAddress類型是一個指針,指向jvm指令的操作碼,在Java中沒有與之對應的類型。

boolean類型的操作會被轉化為int類型的操作進行,boolean數組會當成byte數組去操作。1表示true,0表示false。

引用類型包括三種,類類型,數組類型,和接口類型。

它們的值是動態創建的類實例,數組,或實現接口的類實例。

數組有component類型和element類型,component類型就是數組去掉最外層維度后剩下的類型,可能還是一個數組類型(對於多維數組)。

element類型就是數組裡面存儲的最小數據的類型,它必須是一個基本類型,類類型,或接口類型。

對於一維數組的話,component類型和element類型是相同的。

引用類型還有一個特殊值,就是null,表示沒有引用任何對象。

運行時公有數據區

jvm有一個堆,在所有jvm線程間共享,堆是一個運行時數據區域,所有為類實例和數組分配的內存都來自於它。

堆在jvm啟動時創建,堆中對象不用顯式釋放,gc會幫我們釋放並回收內存。

方法區

jvm有一個方法區,在所有jvm線程間共享,它存儲每一個類的結構。

像運行時常量池,字段和方法數據,方法和構造函數的代碼,還有特殊的方法用於類和實例的初始化,以及接口的初始化。

方法區在jvm啟動時創建,雖然方法區在邏輯上是堆的一部分。

但簡單實現時可以選擇不進行gc和壓縮,本規範沒有強制要求方法區的位置,也沒有要求管理已編譯代碼的策略。

運行時常量池

運行時常量池就是類或接口的字節碼文件里的常量池的運行時表示形式,它包含幾種常量。

如在編譯時就已經知道的数字字面量值,和必須在運行時解析的方法和字段的引用,運行時常量池的功能類似於傳統語言的符號表,不過它包含的數據會更加寬泛。

運行時常量池分配在jvm的方法區,類或接口的運行時常量池在類或接口被jvm創建時才會構建。

運行時私有數據區

pc寄存器

jvm支持一次運行多個線程,每個線程都有自己的pc寄存器,任何時候一個線程只能運行一個方法的代碼。

如果方法不是native的,pc寄存器包含當前正在被執行的jvm指令地址,如果方法是native的,pc寄存器的值是未定義的。

jvm棧

每一個jvm線程都有一個私有的jvm棧,隨着線程的創建而創建,棧中存儲的是幀。

jvm棧和傳統語言如C的棧相似,保存局部變量和部分計算結果,參与方法的調用和返回。jvm棧主要用於幀的出棧和入棧,除此之外沒有其它操作,

幀可能是在堆上分配的,所以jvm棧使用的內存不必是連續的。

native方法棧

native方法不是用Java語言寫的,為了支持它需要使用傳統棧,如C語言棧。不過jvm不能加載native方法,所以也不需要提供native方法需要的棧。

每次當一個方法被調用時一個新的幀會被創建。當方法調用完成時,與之對應的幀會被銷毀,無論是正常完成還是拋異常結束。

所以幀是方法調用的具體體現形式,或稱方法調用是以幀的形式進行的。幀用來存儲數據和部分計算結果,和執行動態鏈接,方法返回值,分發異常。

幀分配在創建幀的線程的jvm棧上,每一個幀都有自己的本地變量數組,自己的操作數據棧,和一個對當前方法所在類的運行時常量池的引用。

本地變量數組和操作數棧的大小在編譯時就確定了,它們隨着和幀關聯的方法編譯后的代碼一起被提供,因此幀這種數據結構的大小隻依賴於jvm的實現,這些結構所需的內存可以在方法調用時同時被分配。

在一個線程執行的任何時刻,都只會有一個幀是處於激活的。這個幀被稱為當前幀,與之對應的方法被稱為當前方法,方法所在的類被稱為當前類,此時用到的本地變量數組和操作數棧也都是當前幀的。

一個幀將不在繼續是當前幀,如果它的方法調用了另一個方法,或者它的方法結束了。

當一個方法被調用,一個新的幀被創建,當執行控制由原來的方法傳遞到新的方法時,這個新的幀變為當前幀。

當方法返回時,當前幀把方法執行的結果傳回到上一幀,當上一幀被激活的同時當前幀會被丟棄。

本地變量數組

每一幀都包含一個變量數組,就是都熟知的本地變量存儲的地方。這個本地變量數組的長度在編譯時確定,隨着編譯后的方法代碼一起提供。

通常一個本地變量(的位置)能夠存儲一個類型的值,但是long和double類型卻需要兩個本地變量(的位置)才能存一個值。

本地變量按索引尋址,第一個本地變量的索引是0。long和double需要消耗兩個連續的索引,但卻是按照較小的這個索引尋址的。不能按照較大的那個索引去讀數據,但是可以寫入,當然這樣將使本地變量內容錯亂。

在方法被調用時,jvm使用本地變量來接收傳遞進來的參數值。在類(靜態)方法調用時,所有參數被傳入從索引0開始的連貫的本地變量數組裡。

在實例(非靜態)方法調用時,索引0處總是傳入正在其上執行方法調用的那個對象的引用,(就是Java中的this了),所有參數被傳入從1開始的連貫的本地變量數組裡。

操作數棧

每個幀包含一個後進先出的棧,用於存儲正在執行的jvm指令的操作數,就是都熟知的操作數棧,這個棧的最大深度在編譯時就已確定,隨着編譯后的方法代碼一起提供。

當幀被創建時,操作數棧是空的,jvm提供一些指令用於加載常量值,本地變量值,字段值到操作數棧上,另一些jvm指令採用操作數棧上的操作數進行操作,並把結果放回到操作數棧上。

操作數棧也用於準備將要傳遞給方法調用的參數和接收方法調用返回的結果。

long和double類型的值佔用兩個單位的棧深度,其它類型的值佔用一個單位的棧深度。

動態鏈接

每一個幀都包含了對當前方法所屬類型的運行時常量池的引用。目的是為了支持方法代碼的動態鏈接。class文件中描述一個方法引用被調用的方法和被訪問的變量的代碼,是採用符號引用的形式實現的。

符號引用的形式可以粗略的認為是字符串的形式,就是用字符串標明需要調用哪個類的哪個方法或訪問哪個字段或變量。就像符號引用這個名字一樣,這些僅僅是符號,是拿不到具體值的,所以必須要進行轉換。

動態鏈接就是把這些符號方法引用轉換為具體的方法引用,在必要時加載類來解析尚未明確的符號,把符號變量的訪問轉換為這些變量運行時所在存儲結構的適合的偏移量(索引)。這樣的方式又稱為後期綁定。

方法調用

一個方法調用正常完成(即沒有拋異常)時,會根據所返回的值的類型執行一個適合的return指令,當前幀會去恢復調用者的狀態,包括它的本地變量和操作數棧,使調用者的程序計數器適合的遞增來跳過剛剛的那個方法調用指令。

返回值會被放到調用者幀的操作數棧上,然後繼續執行調用者方法的幀。

一個方法在調用時拋出了異常,且這個異常沒有在這個方法內被捕獲處理,將會導致這個方法調用的突然結束,這種情況下永遠不會向方法的調用者返回一個值。

特殊方法

站在jvm的級別,每一個用Java寫的構造函數都以一個實例初始化方法出現,且都是特殊的名字,就是<init>,這個名字是編譯器提供的。

實例初始化方法只能在jvm內部使用invokespecial這個指令調用,且只能在尚未初始化的類實例上調用。

一個類或接口最多可以有一個類或接口初始化方法,通過調用這個方法被初始化。類或接口的初始化方法也有特殊的名字,就是<clinit>,該方法沒有參數,且返回值是void。

方法名稱也是由編譯器提供的,從Java7開始,在字節碼中這個方法必須被標記為靜態的才行。

這個初始化方法是被jvm隱式調用的,它們絕對不會直接被用任何jvm指令調用,僅作為類初始化進程的一部分被間接的調用。

Java類庫

jvm必須為Java類庫的實現提供足夠的支持。一些類庫中的類如果沒有jvm協助是無法實現的。

反射,就是在運行時獲取某個類的類型相關信息,如它的字段信息,方法信息,構造函數信息,父類信息,實現的接口信息。

這些信息都必須是把一個類加載完之後才可以知道的,只有jvm才可以加載類。如java.lang.reflect這個包下的類和Class這個類。

在Java中加載一個類或接口用類加載器,即ClassLoader,背後還是委託給jvm來實現的。

鏈接和初始化一個類或接口。

安全,如java.security包下的類,還有其它類像SecurityManager。

多線程,如線程這個類Thread。

弱引用,像java.lang.ref包下的類。

公有設計,私有實現

以上內容只是jvm的一個“相對寬泛”的規範,它並不是實現方案,也不是實現細節。

實現者可以根據自身的需要來實現jvm,如運行在後端服務器上的jvm和運行在移動設備上的jvm肯定側重點有所不同。

從事Java的人都知道,事實上jvm是有較多的實現版本。

由於jvm是處在Java語言和操作系統之間的,所以它要向上提供對Java的支持,向下與操作系統良好交互。

寫在最後

高級語言(Java,C#)中的很多操作如文件操作,網絡操作,內存操作,線程操作,I/O操作等,都不是高級語言自身能夠實現的。

也不是它們的虛擬機(JVM,CLR)能夠實現的,實際最終是由操作系統實現的,因為這些都是系統資源,只有操作系統才有權限訪問。

如果你用Java或C#代碼創建了一個文件,千萬不要以為是Java或C#創建了這個文件,它們只是層層向下調用了操作系統的API,然後到文件系統API,最後可能到磁盤驅動程序。

由此可以看出,要想設計一門語言,不單單是關鍵字、語法、編譯器,類庫,虛擬機這些,還要深度了解操作系統,甚至是硬件,如CPU架構和CPU指令集等。

所以,和語言相關的事情,每一項都是異常的繁瑣複雜,都需要投入大量的人力、財力、時間去研究,最後即使研究成功了,可能沒有生態,沒人使用,自然也無法賺錢。

因此,國人現在還沒有一門屬於自己的真正語言。

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

【面試】我是如何面試別人List相關知識的,深度有點長文

我是如何在畢業不久只用1年就升為開發組長的

爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

【面試】我是如何在面試別人Spring事務時“套路”對方的

【面試】Spring事務面試考點吐血整理(建議珍藏)

【面試】我是如何在面試別人Redis相關知識時“軟懟”他的

【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)

【面試】如果你這樣回答“什麼是線程安全”,面試官都會對你刮目相看(建議珍藏)

【面試】迄今為止把同步/異步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)

【面試】一篇文章幫你徹底搞清楚“I/O多路復用”和“異步I/O”的前世今生(深度好文,建議珍藏)

【面試】如果把線程當作一個人來對待,所有問題都瞬間明白了

Java多線程通關———基礎知識挑戰

品Spring:帝國的基石

 

作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號的二維碼,歡迎關注!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!