• 學習嵌入式C語言的6個層級,你在哪一層?

    C語言可以説是一中經典的編程語言,沒有C語言就沒有今天的各種操作系統。 C語言是基礎,你掌握了多少? 1 新手級別學習目的:過計算機二級,考證,應付期末考試。 需要掌握的程度:掌握C語言的基本語法,會雞兔同籠100條腿的編程,會冒泡排序等。 2 入門級別學習目的:學會使用C語言編寫程序、開發項目。 需要掌握的程度:掌握一個模塊的封裝與調用、函數接口的聲明與定義、C語言的多文件編程,能做一些簡單的C語言項目,但需要參考一些案例,編程的時候大多數時候需要翻書、百度、Google,去尋求程序應該怎麼寫,對語句、語法的掌握不精通。 3 老鳥級別能夠熟練掌握C語言編程,精通C語言的各種語法,編寫程序時不再需要翻書、百度去查看某個for、switch語句該如何使用,如何編寫。 知道C語言編程中可能遇到的各種坑:內存泄漏、段錯誤,熟練掌握指針、數組、二級指針、指針數組、數組指針等複雜語法的使用。 4 高手級別經過多年編程實戰,已經對C語言編程瞭如指掌,深諳C語言各種編程技巧和語法,並從以往的編程經驗中不斷反思、總結、提煉,編程能力大幅提升。 知道如何使用C語言進行大型項目開發、大型項目管理,學會從用户需求、軟件工程、項目管理的角度去看待C語言,深諳各種編程語言的優劣。 掌握各種開發環境和調試技能,遇到工程問題,能快速定位,具有獨立分析問題、快速解決問題的能力。 5 專家級別在C語言、嵌入式開發領域浸淫多年,看問題的視角不再僅僅侷限於C語言本身,而是C語言背後的底層運行機制、硬件工作原理。 一般會熟悉一種或幾種CPU架構,掌握幾種架構的彙編語言,學會使用反彙編去分析C語言底層運行過程,熟悉編譯原理、操作系統、算法、CPU體系結構等全棧知識體系。 在一個公司的項目開發過程中,當工程師遇到久久解決不了的技術難題時,一般就需要這種級別的大佬出來救場了。 6 大神級別專家常有,而大神不常有。 到了這個層次,除了工作經驗的積累、勤奮持續地學習和自我激勵,一般還需要興趣和天分這種buffer加持。 此時,學習嵌入式、C語言不再是應付工作,或者項目需要,而是興趣使然,興趣與熱愛促使一個人願意花更多的時間和精力在編程上面,而忽略了身邊其他的東西。 他們可能不怎麼注意衞生,情商也不太高,但假以時日,他們會從一羣人中脱穎而出,漸漸在圈內小有名氣,併成為周圍人膜拜的對象。他們不愁工作,彷彿自帶光圈,到哪裏,哪裏一片光明,他們工作的氣場很強大,已經超越了嵌入式、C語言本身,而且説不定會感染到你,讓你渾身也充滿了能量,躍躍欲試。 和他們在一起工作的心情是愉快的,彷彿在打一場王者高端局,執行到位,配合嫺熟,時不時來一波小高潮,讓人酣暢淋漓。 所以要珍惜身邊這樣的人,或者具有這種潛力的人。 版權申明:內容來源網絡,版權歸原創者所有。除非無法確認,我們都會標明作者及出處,如有侵權煩請告知,我們會立即刪除並表示歉意。謝謝!

    C語言編程 嵌入式 C語言

  • 繼續硬肝內存,你真的理解內存分配嗎?

    內存是計算機中必不可少的資源,因為 CPU 只能直接讀取內存中的數據,所以當 CPU 需要讀取外部設備(如硬盤)的數據時,必須先把數據加載到內存中。 我們來看看可愛的內存長什麼樣子的吧,如圖所示: 一、內存申請 通常使用高級語言(如Go、Java 或 Python 等)都不需要自己管理內存(因為有垃圾回收機制),但 C/C 程序員就經常要與內存打交道。 當我們使用 C/C 編寫程序時,如果需要使用內存,就必須先調用 malloc 函數來申請一塊內存。但是,malloc 真的是申請了內存嗎? 我們通過下面例子來觀察 malloc 到底是不是真的申請了內存: 1#include 2 3int main(int argc, char const *argv[]) 4{ 5 void *ptr; 6 7 ptr = malloc(1024 * 1024 * 1024); // 申請 1GB 內存 8 9 sleep(3600); // 睡眠3600秒, 方便調試 10 11 return 0; 12} 上面的程序主要通過調用 malloc 函數來申請了 1GB 的內存,然後睡眠 3600 秒,方便我們查看其內存使用情況。 現在,我們編譯上面的程序並且運行,如下: 1$ gcc malloc.c -o malloc 2$ ./malloc 並且我們打開一個新的終端,然後查看其內存使用情況,如圖 2 所示: 圖2 中的 VmRSS 表示進程使用的物理內存大小,但我們明明申請了 1GB 的內存,為什麼只顯示使用 404KB 的內存呢?這裏就涉及到 虛擬內存 和 物理內存 的概念了。 二、物理內存與虛擬內存 下面先來介紹一下 物理內存 與 虛擬內存 的概念: 物理內存:也就是安裝在計算機中的內存條,比如安裝了 2GB 大小的內存條,那麼物理內存地址的範圍就是 0 ~ 2GB。 虛擬內存:虛擬的內存地址。由於 CPU 只能使用物理內存地址,所以需要將虛擬內存地址轉換為物理內存地址才能被 CPU 使用,這個轉換過程由 MMU(Memory Management Unit,內存管理單元) 來完成。虛擬內存 大小不受 物理內存 大小的限制,在 32 位的操作系統中,每個進程的虛擬內存空間大小為 0 ~ 4GB。 程序中使用的內存地址都是虛擬內存地址,也就是説,我們通過 malloc 函數申請的內存都是虛擬內存。實際上,內核會為每個進程管理其虛擬內存空間,並且會把虛擬內存空間劃分為多個區域,如 圖3 所示: 我們來分析一下這些區域的作用: 代碼段:用於存放程序的可執行代碼。 數據段:用於存放程序的全局變量和靜態變量。 堆空間:用於存放由 malloc 申請的內存。 棧空間:用於存放函數的參數和局部變量。 內核空間:存放 Linux 內核代碼和數據。 三、brk指針 由此可知,通過 malloc 函數申請的內存地址是由 堆空間 分配的(其實還有可能從 mmap 區分配,這種情況暫時忽略)。在內核中,使用一個名為 brk 的指針來表示進程的 堆空間 的頂部,如 圖4 所示: 所以,通過移動 brk 指針就可以達到申請(向上移動)和釋放(向下移動)堆空間的內存。例如申請 1024 字節時,只需要把 brk 向上移動 1024 字節即可,如 圖5 所示: 事實上,malloc 函數就是通過移動 brk 指針來實現申請和釋放內存的,Linux 提供了一個名為 brk() 的系統調用來移動 brk 指針。 四、內存映射 現在我們知道,malloc 函數只是移動 brk 指針,但並沒有申請物理內存。前面我們介紹虛擬內存和物理內存的時候介紹過,虛擬內存地址必須映射到物理內存地址才能被使用。如 圖6 所示: 如果對沒有進行映射的虛擬內存地址進行讀寫操作,那麼將會發生 缺頁異常。Linux 內核會對 缺頁異常 進行修復,修復過程如下: 獲取觸發 缺頁異常 的虛擬內存地址(讀寫哪個虛擬內存地址導致的)。 查看此虛擬內存地址是否被申請(是否在 brk 指針內),如果不在 brk 指針內,將會導致 Segmention Fault 錯誤(也就是常見的coredump),進程將會異常退出。 如果虛擬內存地址在 brk 指針內,那麼將此虛擬內存地址映射到物理內存地址上,完成 缺頁異常 修復過程,並且返回到觸發異常的地方進行運行。 從上面的過程可以看出,不對申請的虛擬內存地址進行讀寫操作是不會觸發申請新的物理內存。所以,這就解釋了為什麼申請 1GB 的內存,但實際上只使用了 404 KB 的物理內存。 五、總結 本文主要解釋了內存申請的原理,並且瞭解到 malloc 申請的只是虛擬內存,而且物理內存的申請延遲到對虛擬內存進行讀寫的時候,這樣做可以減輕進程對物理內存使用的壓力。

    程序喵大人 CPU 內存 解內存分配

  • 程序員的35歲人生

    如果你在網上搜索35歲 程序員,相信你搜出來的結果將是滿屏的焦慮,35歲程序員不但要面對來自年輕人的挑戰,還要面對資本家的挑剔,工作的壓力,生活的壓力,彷彿我們一旦走到這個年紀,我們的人生就到了一個坎,一個我們怎麼也跨不過去的坎。 35歲本應該是人生最好的年紀,有閲歷,有衝勁,抗壓能力強,也更成熟穩重。 而現在,35歲的意味着你超出了公務員招聘的年齡線,是隨時等待被公司“優化”的對象,是有房貸、有車貸、還有娃嗷嗷待哺,所有人才政策都被卡在外,或許四十歲人真的可以達到不惑,但是35歲時,卻是互聯網圈誰也逃不脱的危機歲月。 最近一條名為:36歲程序員應聘遭拒的新聞又爬上新聞頭條了。 杭州一公司領導在 19 樓 APP 發帖稱,自己拒絕了一位 36 歲應聘 Java 程序員的候選人,表示拒絕他不是專業能力的問題,對這種 35 以上的中年員工,領導和老闆的顧慮太多。帖子一發出就引發熱議,程序員到35歲好像就不是好程序員了,這種新聞隔三差五的就會出現一次,但是評論區也有網友評論,樓主説的“性價比不高”其實是中年員工沒有年輕人好忽悠了;還有網友以專業人士的身份反駁了樓主的説法,表示35歲以上的程序員對工作的理解要比年輕人好得多,不會製造那麼多 bug。 今天想跟大家聊聊“程序員到了35歲,都去幹嘛了?” 前方警告,本文沒有任何學習方法,或者教你怎麼未雨綢繆。因為作為一個不到30歲的程序員,以我的經歷還沒那個資格指導前輩,不過歡迎35 前輩們在今天的評論區裏留言,給大家指指路。 當然很大一部分都還是做程序員啊!!!如果我們把這句話的主語換成其他職業,35歲的老師都在做什麼?35歲的醫生都在做什麼?好像都有點明知故問的意味。同理,現在天天説的35歲程序員,還沒做領導管理團隊,就在市場上沒有優勢了, 我覺得也沒必要捧高踩低,所有程序員都有35歲的那一天,不可能所有人都當上總經理,出任CEO,年薪過百萬呀。 就和35歲沒當上校長的老師就不是好老師,35歲沒當上院長的醫生不是好大夫一樣,但你不得不承認老師、醫生和一些技術工作都是年紀越大越有經驗,也越吃香,程序員怎麼就成吃青春飯的了,國外六七十歲還在編程的老頭子一大把一大把的! 35歲的程序員哪裏不行了? 看了很多網上的文章,我發現大家唱衰35歲程序員的原因大多在於以下幾點: 大多數互聯網公司加班強度高,隨着年齡增長,家庭生活分散的精力,大齡程序員的體力跟不上。 互聯網行業迭代速度快,大齡程序員跟不上更新速度。 大齡程序員一定要當管理層 第一點讓我想起了最近的我親身經歷的一件事情,有我微信的朋友們可能都知道我喜歡吃海底撈,基本一個月至少3次吧,前兩天老婆做美甲,我在旁邊陪着,閒着也是閒着,就問美甲師,海底撈的員工是不是都要培訓很久,才能個個都這麼優秀,美甲師一句話就解答了我的問題——不適合的人他們自己自然就走了。海底撈的培訓時間只有3天,培訓後立即上崗,在培訓和工作的初期,不能適應的人要麼自己選擇走了,要麼通不過考核被動離開了。 是啊,任何行業都是這樣,優勝劣汰,程序員競爭這麼激烈,自然也會淘汰掉大把的人,那這些千錘百煉留下來的,是不是也説明這羣人“適合”這個行業呢。至少35歲以上的程序員大部分都是自身對編程比較執著,工作經驗相對豐富。 試想,當我們經歷了考研、996、內卷、資本打壓、同事甩鍋,好不容易到了35歲,積攢了一些經驗,培養了點大局觀,結果你告訴我,你體力不行,腦力不行,沒當過領導,你這個人肯定不對勁。 35歲的程序員都怎麼樣了? 據100offer調查顯示,Java、架構、C/C 、前端和技術管理是35歲 程序員求職方向的前五位。在這些求職方向中,架構和技術管理的總佔比約為 22%。 從薪資上看,架構和技術管理方向也是開出薪酬最高的崗位。

    程序喵大人 互聯網 程序員

  • 復旦團隊“人體經絡圖”火了!耗時9年,證明茶可疏通經絡,網友:啊這都可以發論文

    楊淨 子豪 發自 凹非寺 量子位 報道 | 公眾號 QbitAI 一張“人體經絡圖”,最近在全網火了。 經絡,我們都聽過,但它究竟是啥?看不見又摸不着,一直沒被科學證實。 這一次,研究團隊依靠茶葉,號稱拍攝到了出了幾近完整的人體經絡影像。 而且還有復旦大學的title在這,因此不少網友激動地表示,經絡終於被科學正名了! 我真的覺得這個太厲害了,真的太厲害了。 但與此同時,質疑聲也不少:啊這都可以發論文? 經驗=科學? 什麼時候經驗也能被稱作科學了? 出於好奇,我們就去研究了下論文的一些細節。 發現的確沒有那麼簡單。 這是一個什麼研究? 簡單來説,就是在人們飲茶之後,身體的不同部位會迅速發熱、甚至流汗,讓經絡自己活躍起來,從而顯形,這時候拍攝身體各部位的紅外影像。 很多草本藥食在服用之後,雖然相應的經絡和肺腑部位會有明顯的發熱和酥麻感,但感覺不明顯,也很難拍攝出影像。 2012年,研究團隊在試驗了幾百種藥食以後,發現感受最強的是茶葉。 喝了不同的茶以後,身體的不同部位會迅速發熱,甚至大量流汗。 為了證明這種感受是客觀、可重複的,研究團隊採用了雙盲實驗。 24位志願者在不知道茶葉種類的情況下,飲用同一種茶,並記錄自己發熱的部位。 在飲用68種茶之後,結果顯示,所有志願者報告達到了96%的一致性。 由此發現,不同茶葉對應的歸經有着明顯的規律: (歸經:藥物作用對機體某部分的選擇性、對應關係) 比如,綠茶對應太陽脈,青茶對應陽明脈,紅茶對應少陽脈,白茶對應太陰脈,黑茶對應厥陰脈。 不過有一種茶不太清晰,那就是黃茶。 但就在5年後,研究人員在貴州梵淨山一帶找到了含黃酮醇高的茶樹,發現了黃茶對應少陰脈。 根據經絡假説,大多數經絡會經過不同的手指和器官。 為驗證手指與器官之間是否存在對應關係,研究團隊從中國南方各省以及日本、新西蘭等國家收集了512種茶,從中選出了效果最強的13種。 一共有42名志願者到福建太姥山上參與重複試驗,包括17名男性和25名女性。 志願者每次喝完茶後,立即對人體進行紅外成像。 研究人員發現,在飲用不同的茶後,可能由12條假設的經絡連接的不同路徑和器官,温度都有明顯的上升。 一些其他的湯藥或飲品,也能產生類似的效果。 團隊分別測得手指、手掌的3個區域和12個器官的最高温度,並且將數據歸一化處理,得到了這樣的結果: 比如,圖中顯示的紅色部分,代表飲用紅茶後發熱的身體部位,包括無名指、手掌的一部分、甲狀腺和膽囊。 為了驗證某種茶是否會對所有經絡起作用,研究人員進行了相關性分析。結果顯示在喝茶之後,身體各部位的温度沒有同時升高。 並且,紅外線圖像顯示了被激活器官的清晰形狀,例如,心臟和腎臟被黃茶激活等等。 網友:熱水也能讓身體發熱 對此,網友紛紛提出了質疑。 一方面,是針對研究本身。 比如,經絡既然是看不見摸不着的,那又是如何證明是這些紅外影像顯示的就是經絡呢? 這些測得的熱量變化路徑,為什麼不是因為其他生化作用導致的? 有網友表示,只喝熱水也能讓身體不同部分發熱,要不也看看這是什麼脈? 這麼高級,那他們能測出來喝的什麼茶嗎? 還有網友關注到了研究細節上。 比如樣本不足。 只有幾十個樣本就搞出來的研究? 再比如,茶的温度沒有説明。 喝的是冷茶還是熱茶?每種茶都是同一個温度時喝的嗎? 還有人聯想到,當前中醫面臨的處境,令人嘆惋。 中藥可以入經脈,老祖宗5000年的經驗,愣是被説成偽科學!最後,還需要現代科技來驗證,是好事,也是悲哀。 發表在什麼期刊? 除了研究本身,網友關注的另一個點: 文章所在的期刊Quantitative Biology,定量生物學。 它創刊於2013年,由高等教育出版社、清華大學主辦的英文季刊。 期刊涉及領域包括系統與合成生物學、生物信息學與計算生物學。 由北京大學定量生物學中心、德克薩斯大學達拉斯分校的兩位教授擔任主編。 它的最新影響因子是1.161,此前也都在1上下徘徊。 啊這,確實是有點低了。 也難怪有網友質疑這一研究了。 還有網友認為,並不反對中醫,但這樣的報道和宣傳,加上覆旦的title,豈不是會把中醫黑的更慘。 而它日常的文章畫風,是這樣的。 人類基因組計劃:起點的開始。 合成基因電路進入臨牀。 這研究早就有書了 那麼最後,只剩下一個問題了。 這是一個什麼團隊? 論文一作是金雯俐,來自上海自然博物館。 除此之外,還有一位山西高研院的研究者以外,其餘全是來自復旦大學的學者。 通訊作者是復旦大學生命學院的教授、博導李輝。 主要研究領域為分子人類學,從DNA入手來探求人類起源和文明肇始,比如,通過y染色體來研究曹操的身世問題。 據南方週末報道,Science曾評價他的研究開創了新領域——歷史人類學。 今年,他有一篇科普新作《人類起源和遷徙之謎》,由他和他夫人金雯俐共同撰寫。 嗯?我好像知道了什麼不得了的事情。 而茶葉,只不過是他的興趣研究所在。 回溯這整個熱議過程,起因源自於李輝——這篇文章的通訊作者,投給《環球》雜誌的一篇文章。 進而引發了包括新華網、茶週刊在內多家媒體的報道。 他本人表示,更詳細的茶道和經絡的系統知識在《茶道經譯註》一書中。 這本書,由復旦大學出版社出版,今年1月剛發行,售價88元。 與之相匹配的,還有在優酷網站上對應的茶道經課。 可以看到,裏面就已經有「如何分辨茶對應的人體經絡」、「通過經絡分辨茶的好壞」相關視頻了。 不過學習這些,還是要付費滴~ 售價128元,有效期為10個月。 面對網友的質疑,李輝前幾天其實也藉着媒體回覆了。 這是一個非常初步的探索研究,絕對稱不上完美,但是完全可以第三方獨立重複驗證,已經有很多團隊聯繫跟進重複實驗和深入探索。 之所以強調茶葉,一方面是茶葉引發的紅外輻射屬於最強的一類,另一方面不同的茶葉類別激發不同的十二條經絡,有利於比較尋找歸經現象的分子基礎。一旦歸經的分子基礎找到,對於將來的靶向藥物設計的影響可能是革命性的。 那麼對於這件事情,你又是如何看的呢?

    程序員小灰 論文

  • level-ip之ping命令處理

    知識回顧 在前面的文章中,我們已經介紹了ip數據包的封裝接口,其中主要是以下幾個接口: route_init():路由表的初始化 ip_rcv():發送以太網幀數據 ip_output():發送以太網幀數據 這幾個接口是我們封裝IP數據接口的基礎,最好還是先搞明白原理。 ICMP 協議的格式 無論是在宿舍,還是在辦公室,或者運維一個數據中心,我們常常會遇到網絡不通的問題。那台機器明明就在那裏,你甚至都可以通過機器的終端連上去看。它看着好好的,可是就是連不上去,究竟是哪裏出了問題呢?一般情況下,你會想到 ping 一下。 那你知道 ping 是如何工作的嗎? ping 是基於 ICMP 協議工作的。ICMP全稱Internet Control Message Protocol,就是互聯網控制報文協議。 網絡包在異常複雜的網絡環境中傳輸時,常常會遇到各種各樣的問題。當遇到問題的時候,總不能“死個不明不白”,要傳出消息來,報告情況,這樣才可以調整傳輸策略。這就相當於我們經常看到的電視劇裏,古代行軍的時候,為將為帥者需要通過偵察兵、哨探或傳令兵等人肉的方式來掌握情況,控制整個戰局。ICMP 報文是封裝在 IP 包裏面的。因為傳輸指令的時候,肯定需要源地址和目標地址。它本身非常簡單。因為作為偵查兵,要輕裝上陣,不能攜帶大量的包袱。 從而我們可以確定,ICMP是在ip協議之上的傳輸層協議,如下圖: ICMP 報文有很多的類型,不同的類型有不同的代碼。最常用的類型是主動請求為 8,主動請求的應答為 0。 構造ICMP 報文 接下來,我們使用結構體來定義ICMP 報文,該結構體定義在level-ip的include/icmpv4.h文件中: 查詢報文類型 我們經常在電視劇裏聽到這樣的話:主帥説,來人哪!前方戰事如何,快去派人打探,一有情況,立即通報! 這種是主帥發起的,主動查看敵情,對應 ICMP 的查詢報文類型。例如,常用的ping 就是查詢報文,是一種主動請求,並且獲得主動應答的 ICMP 協議。所以,ping 發的包也是符合 ICMP 協議格式的,只不過它在後面增加了自己的格式。 對 ping 的主動請求,進行網絡抓包,稱為ICMP ECHO REQUEST。同理主動請求的回覆,稱為ICMP ECHO REPLY。比起原生的 ICMP,這裏面多了兩個字段,一個是標識符。這個很好理解,你派出去兩隊偵查兵,一隊是偵查戰況的,一隊是去查找水源的,要有個標識才能區分。另一個是序號,你派出去的偵查兵,都要編個號。如果派出去 10 個,回來 10 個,就説明前方戰況不錯;如果派出去 10 個,回來 2 個,説明情況可能不妙。 在選項數據中,ping 還會存放發送請求的時間值,來計算往返時間,説明路程的長短。 當前,ICMP還有差錯報文類型,適合在遇到各種網絡故障時使用,我們這裏暫不介紹。 ping:查詢報文類型的使用 接下來,我們重點來看 ping 的發送和接收過程 主機A執行ping 命令的時候,源主機首先會構建一個 ICMP 請求數據包,ICMP 數據包內包含多個字段。最重要的是兩個,第一個是類型字段,對於請求數據包而言該字段為 8;另外一個是順序號,主要用於區分連續 ping 的時候發出的多個數據包。每發出一個請求數據包,順序號會自動加 1。為了能夠計算往返時間 RTT,它會在報文的數據部分插入發送時間。 主機 B 收到這個數據幀後,先檢查它的目的 MAC 地址,並和本機的 MAC 地址對比,如符合,則接收,否則就丟棄。接收後檢查該數據幀,將 IP 數據包從幀中提取出來,交給本機的 IP 層。同樣,IP 層檢查後,將有用的信息提取後交給 ICMP 協議。主機 B 會構建一個 ICMP 應答包,應答數據包的類型字段為 0,順序號為接收到的請求數據包中的順序號,然後再發送出去給主機 A。 關於IP層數據收發和MAC層的數據收發已經在前面給大家分析過了,這裏不再講解,我們重點關注IP數據包遞交給ICMP層的過程。 ICMP數據接收接口 ICMP數據接收接口為icmpv4_incoming()函數。該函數在以太網數據幀讀取接口ip_rcv()函數中調用。我們來了解一下這個函數,如下圖: 第4行:從ip數據包中獲取icmp報文 第8行:判斷icmp的報文類型,如果是查詢報文,則調用icmpv4_reply()函數 ICMP數據發送接口 ICMP數據發送接口為icmpv4_reply()函數,當level-ip協議棧接受到icmp查詢類型報文後,會調用該接口進行ICMP數據回覆。如下圖: 第3行:獲取ip數據包首部 第8行:用ip數據包的總長度-ip首部長度,得到icmp包總長度 第10行:把sk_buff數據包指針移動到有效數據結束 第11行:把sk_buff數據包指針移動從有效數據的尾巴向前移動icmp包總長度 第13行:獲取icmp數據包 第15行:修改icmp協議類型為應答類型 第16行:把類型字段修改為0,表示ping應答 第17行:修改校驗數據 第20行:設置目標主機ip 第22行:調用ip數據發送接口,進行icmp數據包發送 總結 通過我們這邊文章,我們已經明白了ICMP協議的報文結構,並且對ping命令的處理過程有了一個源碼級別的瞭解。

    embed linux share tcp ping level-ip

  • level-ip之ip數據包接口剖析

    閲讀本文需要對level-ip的整體架構有所瞭解,如果讀者尚未接觸過level-ip,請先閲讀下面文章: 分享一款Linux平台下的tcp協議棧!超級透徹! level-ip之虛擬網卡接口封裝 level-ip之以太網數據接口封裝 請根據上述文章中的指引獲取leve-ip的全部源碼,並且嘗試在任意Linux發行版本上編譯運行。 知識回顧 在前面的文章中,我們已經介紹了以太網卡的封裝接口,其中主要是以下幾個接口: netdev_init():初始化網卡的ip地址、mac地址和mtu的值 netdev_receive():發送以太網幀數據 netdev_transmit():發送以太網幀數據 這幾個接口是我們封裝IP數據接口的基礎,最好還是先搞明白原理。 網際協議(IP)介紹 IP是整個TCP/IP協議的核心,網絡層協議,如UDP和TCP都需要IP提供的服務。而像ICMP和IGMP等網絡層協議也基於IP協議來傳輸協議數據。如下圖: 常見的廣域網路由器就工作在IP層,它們負責將IP數據報從源主機送到目的主機,主機間的區分是通過IP地址來實現的。主機上的IP協議需要完成工作有非常多,最基本的就是數據報的發送和遞交,在特殊情況下它還要完成數據報的分片和重裝功能,有時候話要完成數據報的轉發等工作。 IP報文組織結構 ip數據幀位於以太網數據幀的上一層,我們先來了解一下它的報文結構,如下圖: 我們來詳細學習一下,裏面每一個字段所代表的具體含義: 版本號:IP協議版本信息,例如對於IPV4,該值為4,對於IPV6,該值為6 首部長度:以字為單位,對於不含任何選項字段的IP首部,該值為5。 服務類型字段:主要用來描述當前IP數據報急需的服務類型,如最小延時、最大吞吐量、最高可靠性、最小費用等等。路由器在轉發數據報時,可以根據這個字段的值來為數據包選擇最合理的路由路徑。 總長度:描述了整個IP數據報的總字節數。理論上説,IP數據報的總長度最大可達65535字節。但是一般以太網底層鏈路允許的最長數據為1500字節,因此當IP數據包過大的時候,需要對IP進行分片,然後目的主機要對IP報文重裝。 標識字段:當IP數據包發生分片時,這個標識記錄每個IP分片的序號,目的主機需要根據這個字段對其進行重裝。 標誌和分片偏移量:標誌該ip數據報在轉發過程是否允許分片以及是否是最後一個分片。分片偏移量記錄該分片ip數據報在整個數據報中的相對位置。 生存時間(TTL):表示該IP數據報最多能被轉發的次數,每轉發一次,該值減1。 協議:表示該ip數據報中的數據來自哪個上層協議。 首部校驗和:針對ip首部做校驗。 源IP地址:本地主機ip 目的IP地址:待接受數據的主機ip 數據區:非必需,不同的上層協議會選擇性地使用該字段 構造ip首部 接下來,我們使用結構體來定義ip數據首部,該結構體定義在level-ip的include/ip.h文件中: 這兩個結構體的成員變量,與我們剛才介紹的ARP報文的每個字段是一一對應的,這裏不再重複解析。 IP數據報發送接口 IP數據的發送接口ip_output,會被上一層傳輸層協議接口調用,如UDP、TCP、ICMP等。在level-ip中,該接口函數保存在src\ip_output.c文件中。如下圖: 第6行:搜索路由表,找到合適的網卡來進行ip數據的發送,發送的ip數據報需要與網卡處於同一網段。 第14行:把路由表中記錄的網卡設備記錄在sk_buff結構體中,該結構體負責網絡數據發送的全部過程。 第15行:把路由也記錄在sk_buff結構體中。 第17行:把sk_buff結構體中用來裝載數據的區域,預留出ip數據包的首部 第19~39行:填充ip數據包的首部 第40行:填充ip首部的校驗信息 第42行:發送數據報出去,在dst_neigh_output()函數中,將進一步調用以太網卡接口來進行數據的發送。 我們進一步來分析一下dst_neigh_output()函數,在這裏會把ip數據報和arp數據的發送聯合使用。如下圖: 第15行:從arp緩存表中查詢目標ip對應的以太網地址 第17~23行:如果arp緩存表記錄了該ip地址對應的以太網地址,那麼直接調用以太網數據包發送接口來進行數據發送。反之則調用arp查詢接口,廣播發送arp幀。 ip數據報接收接口 ip數據接收接口為ip_rcv()函數。該函數在以太網數據幀讀取接口netdev_receive()函數中調用。該函數保存在src\ip_input.c文件中,我們來了解一下這個函數,如下圖: 第3行:從sk_buff中讀取ip首部信息 第6行:判斷ip協議版本是否為ipv4,此處只支持ipv4 第7行:判斷ip的首部的字節長度是否小於5字節 第16行,如果ip數據報的生存時間為0,説明已被廢棄,不再處理 第22行:檢查ip首部的檢驗。 第31行:把ip地址等字段進行小端轉換 第35~45行:判斷該ip協議的上層類型為ICMP還是TCP,轉交數據包給上層即可。 總結 通過我們這邊文章,我們已經明白了IP協議的報文結構、ip數據包的發送、IP數據包的接收處理等等。從接收函數的分析過程可知,level-ip並不支持ip數據包的分片和重裝,因此也就無法支持UDP協議進行大數據報的發送。

    embed linux share 接口 level-ip ip數據包

  • 怎樣學好網絡編程?

    前言 在嵌入式行業網絡編程使用相對較少,主流應用集中在NB-IOT、Lora、Mqtt這一塊,原理上一般是通過加入硬件模塊或者是使用第三方SDK來實現。因此,大部分嵌入式從業人員網絡編程能力較弱,網絡編程水平多數停留在大學階段。考慮到未來嵌入式的發展趨勢,必然是網絡化 智能化。因此深度掌握網絡編程和人工智能,會有更好的發展前途。當然,走研究linux內核方向也大有可為,只是週期較長,非十年苦工不能大成。 目前筆者研究了一段時間libevent和muduo庫早期版本的源碼,對如何開發高併發、高性能服務器有了較為深入的瞭解。在研究過程裏面,遇上了很多坑,也有了一些優化性能的心得。在我掌握了這部分知識內容之後,再回頭看,發現我們的學習網絡編程的路線,其實一直是有問題的。 幾個網絡編程問題 這裏分享幾個網絡相關問題,前兩個問題考察tcp協議理論基礎,後兩個考察對網絡庫的瞭解,大家可以自測一下: tcp四層模型和OSI七層模型的聯繫和區別是什麼? tcp三次握手和四次揮手是什麼?time_wait和close_wait狀態是意味着什麼? select、poll、epoll的原理和區別是什麼?epoll反應堆模式是什麼? 如何實現網絡事件驅動模型?對統一事件源,緩存管理有沒有了解? 理論與實戰 我相信應該大部分人都對前兩個問題較為熟悉,但是對後兩個問題就不太熟悉了。 這主要是因為目前我們在學校所學習的網絡編程基本上是偏理論的,筆者在大學學習tcp協議相關知識的時候,根本不能理解流量控制、滑動窗口、擁塞控制等等概念。但為了通過考試與面試,硬是把它死記硬背了下來。在實際開發過程中,大量使用網絡庫之後。逐漸意識到,理論與實戰是相符相成的。理論無聊乏味,與實際開發斷層嚴重,但卻是大廈之基。實戰生動有趣,成就感滿滿,卻被框架矇蔽雙眼,難以洞察本質。 既然理論龐大難學,框架易用難精,我們該如何把握學習網絡編程的路線? 答案如下: 學習tcp底層協議,從設計一款簡單的tcp協議棧開始着手研究,明白四層模型到底長什麼樣,每個狀態變化是在什麼情景下發生,tcp是如何來保障通信的可靠性 學習操作系統提供的網絡接口,瞭解socket編程接口的發展過程,知道操作系統該如何來捕獲各種網絡IO事件 學習一款優秀的網絡庫,明白高併發、高性能的網絡應用中,如何合理地使用線程。對多線程編程的各種大坑有一個基本的認知,深度思考如何做到"線程安全"

    embed linux share tcp 網絡編程 嵌入式

  • ARP協議是什麼鬼?這一篇源碼分析!

    前言 閲讀本文需要對level-ip的整體架構有所瞭解,如果讀者尚未接觸過level-ip,請先閲讀下面文章: 分享一款Linux平台下的tcp協議棧!超級透徹! level-ip之虛擬網卡接口封裝 level-ip之以太網數據接口封裝 請根據上述文章中的指引獲取leve-ip的全部源碼,並且嘗試在任意Linux發行版本上編譯運行。 知識回顧 在前面的文章中,我們已經介紹了以太網卡的封裝接口,其中主要是以下幾個接口: netdev_init():初始化網卡的ip地址、mac地址和mtu的值 netdev_receive():發送以太網幀數據 netdev_transmit():發送以太網幀數據 這幾個接口是我們封裝ARP數據接口的基礎,最好還是先搞明白原理。 ARP協議的由來 在上面,我們介紹netdev_receive()函數的時候,已經發現了以太網幀類型主要分兩大類型,一種是IP數據幀,另一種是ARP數據幀。也就是説ARP數據幀與IP數據幀同屬於網絡層的數據幀。如下圖: IP數據幀我們知道,是用來傳輸用户數據的。哪ARP數據幀有什麼用呢? 其實,ARP協議是用來將目標主機的IP地址轉換為對應的以太網(MAC)地址的。因為當我們的應用程序要向目標主機發送信息時,它只知道目標主機的IP地址,而IP地址是無法直接用於物理鏈路上傳輸數據的,所以需要ARP數據幀來把IP地址轉化為對應的MAC地址。 我們可以主動發起ARP查詢幀,在本地建立起IP地址和MAC地址的映射關係,也必須要及時回覆別人的ARP查詢幀! ARP報文組織結構 ARP數據幀位於以太網數據幀的上一層,我們先來了解一下它的報文結構,如下圖: 我們來詳解學習一下,裏面每個字段所代表的具體含義; 硬件協議:發送方想要知道的硬件接口類型,對於以太網接口來説,該值為1 協議類型:映射的協議地址類型,我們要把MAC地址映射為IP地址,該值為0x0800 硬件地址長度:對於MAC地址來説,該值為6 協議地址長度:對於IP地址來説,該值為4 OP:表示ARP數據包的具體類型,1為ARP請求,2為ARP應答 剩餘四個字段的具體含義非常簡單易懂,就不羅列出來講解了。 瞭解ARP報文組織結構之後,下一步,自然就是用c語言結構體來構造這個ARP報文組織,level-ip的ARP報文組織結構體保存在include\ethernet.h文件中,如下圖: 這兩個結構體的成員變量,與我們剛才介紹的ARP報文的每個字段是一一對應的,這裏不再重複解析。 ARP請求發送接口 ARP數據幀的發送接口為arp_request()函數。該函數保存在src/arp.c文件中。當我們在發送IP數據幀時,如果在ARP緩存表中找不到該IP所對應的MAC地址時,就會通過廣播的形式,來進行ARP請求數據包的發送。 如下圖: 第8行,動態申請一個sk_buff來繼續發送數據的存儲。 第12行,選擇使用哪個網卡來繼續數據幀的發送 第13行,在sk_buff中,向前移動arp_ipv4結構體大小的位置,把得到的指針賦值給payload指針 第14行,用網卡(netdev)中記錄的源主機mac地址,填充arp-ipv4結構體中的源主機mac地址(smac) 第15行,填充arp-ipv4結構體中的源主機ip地址(sip) 第16行,用廣播地址(broadcast_hw),填充arp-ipv4結構體中的目的主機mac地址(dmac) 第17行,填充arp-ipv4結構體中的目的主機ip地址(dip) 第18行,在sk_buff中,向前移動arp_hdr結構體大小的位置,把得到的指針賦值給arp指針 第19~29行,初始化ARP報文的硬件協議、協議類型、報文類型等等,htons()函數為進行數據的大小端切換。到這裏ARP報文就初始化好了 第31行,調用netdev_transmit()函數,進一步構建以太網數據幀發送 ARP數據讀取接口 ARP數據接收接口為arp_rcv()函數。該函數在以太網數據幀讀取接口netdev_receive()函數中調用。我們來了解一下這個函數,如下圖: 第8行,從讀取到的數據中獲取arp數據幀 第10~12行,獲取arp數據幀中的硬件類型、協議類型、報文類型 第25~28行,獲取源主機和目的主機的ip地址 第30行,繼續arp緩存表數據的更新 第32行,判斷該arp數據幀,是不是發送給本機的 第37行,如果arp數據幀中的IP地址還沒有緩存在本機的ARP緩存表中的話,那麼把這個IP地址插入到ARP緩存表中保存 第42行,判斷ARP數據幀的報文類型 第43、44行,如果報文類型為ARP請求幀,那麼調用arp_reply()函數進行ARP應答幀的發送 ARP應答幀發送接口 在上面我們介紹ARP數據讀取接口時,當我們如果接收到了ARP請求幀,那麼我們要調用arp_reply()函數進行ARP應答幀的發送,我們來學習一下這個函數。 如下圖: 第6行,獲取arp報文的數據 第8行,使用skb_reserve()函數來調整sk_buff中數據指針的位置,表示以太網首部和ARP報文的數據都還沒有填充 第9行,使用skb_push()函數,參數為ARP_HDR_LEN ARP_DATA_LEN,表示填充了ARP報文 第11~28行,將該ARP請求數據包的源主機信息和目的主機信息交換位置,並把操作字段op置為2 第30行,選擇發送網卡 第32行,調用netdev_transmit()函數,進一步構建以太網數據幀發送 總結 通過我們這邊文章,我們已經明白了ARP協議的報文結構、ARP數據包的發送、ARP數據包的接收處理等等。知道了ARP協議在TCP協議棧中的重要地位。不過文中對ARP緩存表沒有做深入介紹,這是因為該知識點比較基礎,主要是對鏈表的插入、刪除等操作。

    embed linux share 協議 源碼 ARP

  • level-ip之以太網數據接口封裝

    前言 閲讀本文需要對level-ip的整體架構有所瞭解,如果讀者尚未接觸過level-ip,請先閲讀下面文章: 分享一款Linux平台下的tcp協議棧!超級透徹! level-ip之虛擬網卡接口封裝 請根據上述文章中的指引獲取leve-ip的全部源碼,並且嘗試在任意Linux發行版本上編譯運行。 知識回顧 在前面的文章中,我們已經介紹了虛擬網卡的封裝接口,其中主要是以下幾個接口: tun_calloc():打開並設置虛擬網卡 tun_read():讀取虛擬網卡數據 tun_write():發送虛擬網卡數據 這幾個接口是我們封裝以太網數據接口的基礎,最好還是先搞明白原理。 以太網頭部結構 在tcp的多層協議中,以太網數據幀位於最底層的數據鏈路層,如下圖: 我們重點關注圖片中的MAC頭部(以太網頭部)結構,因為它是我們最終往發送數據中填充的信息內容。我們先來看一下MAC頭部的結構,如下圖; 瞭解了以太網頭部結構之後,下一步,自然就是用c語言結構體來構造這個以太網頭部結構,level-ip的以太網頭部結構保存在include\ethernet.h文件中,如下圖: 第3行:以太網目的地址,由6個8位16進制的數字構成。表示目標主機的物理地址 第4行:以太網源地址,由6個8位16進制的數字構成。表示源主機的物理地址 第5行:幀類型,用來區分ip數據包還是arp數據包(後面會詳細解析兩者區別) 第6行:變長數組,gcc編譯支持變長數組的用法 結構體用__attributr__關鍵字來設置結構體按照實際佔用字節數進行對齊,而變長數組又不佔據結構體內存,因此結構體實際大小為14個字節。 以太網卡管理結構體 在上面對以太網頭部結構的介紹中,我們可以看到它保存着以太網源地址,這個以太網源地址實際上就對應着我們的電腦上網卡,我們知道,電腦上面實際可能會有多個網卡,因此在level-ip中必定需要一個結構體來對這些網卡進行管理,這個結構體就是struct netdev,它定義在level-ip的include\netdev.h文件上,如下圖; 第2行,記錄本地網卡的靜態ip地址,該地址是以網絡傳輸的數值格式保存的 第3行,記錄地址長度 第4行,記錄本地網卡的以太網地址,由6個8位16進制的數字構成 第5行,記錄網卡的最大傳輸單元 以後,我們將把網卡相關信息記錄在這個struct netdev結構體中,然後在發送數據時,從中獲取以太網源地址。 網卡結構體初始化 上面介紹了struct netdev結構體,接下來我們看一下,level-ip是如何初始化它的。如下圖: netdev_init()函數負責初始化網卡的ip地址、mac地址和mtu的值,這個函數的調用為:main()->init_stack()->netdev_init()。在該函數裏面,使用到了netdev_alloc函數,函數負責具體的初始化工作,如下圖: 第3行,動態申請內存給netdev結構體使用 第5行,調用ip_parse函數,將十進制的ip地址轉化為用於網絡傳輸的數值格式 第7行,調用sscanf函數,填充以太網地址到netdev結構體 第14行,設置地址長度 第15行,設置最大單元發送長度 以太網發送接口 終於來到重頭戲!level-ip的以太網發送接口為netdev_transmit()函數,該函數保存在src\netdev.c文件中,如下圖: 第3行,定義了一個netdev結構體指針,前面介紹過了,這個結構體保存了本地網卡的ip、以太網地址等信息。 第4行,定義了一個eth_hdr結構體指針,我們將通過這個結構體來給數據幀填充以太網頭部 第7行,從sk_buff結構體中獲取netdev結構體。在用户連接網絡,準備發送數據的時候,會用sk_buff來記錄了待發送的數據幀和選中的網卡型號,因此能通過該結構體來獲取netdev結構體,這個地方以後會進一步深入講解 第9行,skb_push函數會把sk_buff中的數據幀指針,往前移動eth_hdr結構體大小,這樣我們就能通過eth_hdr結構體來給數據幀添加以太網頭部了。 第13~17行,從netdev結構體提取網卡信息,填充到數據幀頭部,得到待發送的完整數據幀。 第19行,調用虛擬網卡發送函數,來進行以太網幀的數據發送。 以太網接收接口 level-ip的以太網發送接口為netdev_receive()函數,該函數保存在src\netdev.c文件中,如下圖: 以太網的數據接收過程如下: main函數中調用run_threads函數,來創建netdev_rx_loop線程,netdev_rx_loop線程負責調用虛擬網卡讀取接口tun_read來讀取數據,把讀取到的數據緩存到sk_buff結構體後,調用netdev_receive函數來進行以太網頭部解析。下面我們看netdev_receive函數分析過程: 第3行,調用eth_hdr函數,從sk_buff結構體中,獲取以太網頭部數據 第7行,判斷該幀數據是屬於ARP數據幀、IP數據幀、還是IPV6數據幀。這些概念我們下一篇文章再分析,這裏只要瞭解以太網頭部數據是怎麼獲取的就可以了。 第8~19行,對不同的類型的幀作進一步處理。 總結 通過我們這邊文章,我們已經明白了以太網幀頭部的三要素: 以太網目的地址 以太網源地址 以太網幀類型

    embed linux share 以太網 Linux level-ip

  • level-ip之虛擬網卡接口封裝

    閲讀本文需要對level-ip的整體架構有所瞭解,如果讀者尚未接觸過level-ip,請先閲讀下文: 分享一款Linux平台下的tcp協議棧!超級透徹! 請根據上述文章中的指引獲取leve-ip的全部源碼,並且嘗試在任意Linux發行版本上編譯運行。 前言 在整個tcp/ip協議棧中,對協議是進行上下分層的。用户需要收發的數據在不同協議層中傳輸時,會添加和刪除各種協議包頭。如下圖: 我們暫且先把tcp協議棧粗淺地理解為:為用户數據添加各種包頭,然後發送出去。等接收到數據之後,又按相反的順序,解析出數據包中的有效數據,然後傳輸給用户程序。 既然數據是層層遞交的,那麼我們就應該先設計好每一層協議所需要使用到的接口。這一章,我們就來分析最底層的虛擬網卡接口。這個虛擬網卡接口,以後就負責幫助我們跟互聯網進行數據收發。 虛擬網卡接口封裝 在前面的文章中,我們已經講解了虛擬網卡的基本原理,明白虛擬網卡在Linux系統下的設備文件為"/dev/net/tun"。 下面我們來分析源碼,看如何對虛擬網卡的設備文件接口進行封裝,以方便我們日後收發數據。 打開虛擬網卡設備文件 學習過Linux系統編程的小夥伴應該知道,Linux下有"一切皆文件"的哲學思想,我們想要使用虛擬網卡,就應該先用open函數打開虛擬網卡文件,獲取它的文件描述符之後,然後才能對它進行讀寫等操作。 在level-ip\src\tuntap_if.c文件中,有一個tun_init()函數。該函數在main()函數中被調用,負責初始化虛擬網卡設備。如下圖: 我們在這裏主要是關注其中的tun_calloc()函數,在這個函數裏面,就打開了"/dev/net/tun"設備文件,如下圖: 設置虛擬網卡屬性 我們的虛擬網卡,有很多不同屬性,在這裏我們要去配置虛擬網卡為tap網卡,該類型網卡可以抓取以太網層數據包。配置過程也是tun_calloc()函數中完成的。如下圖: 其中,配置屬性如下: IFF_TUN: 創建一個點對點設備 IFF_TAP: 創建一個以太網設備 IFF_NO_PI: 不包含包信息,默認的每個數據包當傳到用户空間時,都將包含一個附加的包頭來保存包信息 然後使用ioctl()函數進行屬性的配置,關於虛擬網卡在驅動程序的詳細配置過程,可以參考這篇文章的分析:分享一款Linux平台下的tcp協議棧!超級透徹! 封裝虛擬網卡讀取接口--tun_read tun_read()其實就是對read()函數的簡單封裝,此處封裝的意義,是用來唯一標識出虛擬網卡數據讀取接口。如下圖: 封裝虛擬網卡發送接口--tun_write tun_write其實就是對write()函數的簡單封裝,此處封裝的意義,是用來唯一標識出虛擬網卡數據發送接口。如下圖: 總結 到這裏,我們就完成了虛擬網卡讀寫接口的封裝,這是我們萬里長征的第一步,也是最重要的一步。從上面tcp協議棧的層次圖可以看出來,它是實現以太網協議的關鍵依賴接口,以後我們將會基於這些接口,封裝出以太網協議上的數據收發接口。

    embed linux share 網卡 虛擬網卡 level-ip

  • 代碼的設計圖紙——UML(下)

    UML建模在工程開發中,不止程序員可以用於做軟件程序設計,而且產品經理也常常使用它來做軟件需求分析。它本身的語法複雜度不高,新手經過一段時間的系統練習,很快可以熟悉使用。當軟件需求者和軟件開發者共同遵循這一套標準時,無疑可以大幅降低溝通成本,提高生產效率。 在UML建模的語法體系中,主要分為兩大類的設計圖,分別是結構性、行為性。在這兩大類圖之下又有進一步的細分,但是本文側重於介紹在軟件設計中最關鍵的類圖。 類圖 UML類圖面向對象編程的核心,它主要把真實系統中的對象抽象成類後,進一步描述對象(類)之間的關係。常見的有以下幾種關係:繼承(Generalization)、實現(Realization)、關聯(Association)、聚合(Aggregation)、組合(Composition)、依賴(Dependency)。其中有一些資料又把"繼承"叫做"泛合"、"聚合"叫做"合成","實現"叫做"接口",大家清楚是指同一種關係即可。 這六種關係的圖示方法比較接近,初學者容易混淆,這裏以近幾年比較火熱的RTOS為例,來介紹一下這些基本圖示法。 UML類圖圖示樣例 我們看"RTOS"那個矩形框,它就是代表了一個對象(類),類圖有三個部分,第一部分是類的名稱,第二部分指類的特性,一般就是字段和屬性。第三部分是類的操作,通常是行為或者方法。在類圖中,不同的符號有不同的含義,' '表示public屬性,'-'表示private屬性,'#'表示protrcted屬性。在C語言中,習慣用結構體來替代面向對象中的類,而結構體中沒有區別這些屬性,也可以認為結構體裏面全部都是public屬性。 分析一下"RTOS"矩形框裏面的內容,我們知道RTOS最重要的屬性就是實時性和可靠性,它最關鍵的行為就是在合理的場景下進行任務調度。那麼這幾個要素就構成了"RTOS"這麼一個對象。 依賴關係 注意左上角的"編程語言"和"硬件平台"矩形框,要知道,任何RTOS都不是憑空而來的,它需要編程語言來翻譯完成相關邏輯運算。同時藉助硬件平台作為運行載體,才能在實際生產環境中完成它的功能。因此它們之間是一個依賴關係,用虛線箭頭來表示。 繼承關係 這裏列舉幾種目前比較主流的RTOS,分別是:µC/OS-ii/iii、FreeRTOS、VxWorks、RTThread。它們都滿足實時性強、安全可靠的要求。雖然它們本質上都是RTOS,但是在垂直領域又各有各的優勢和特點。因此它們與RTOS之間是一種繼承關係。繼承關係用空心三角形 實線來表示。 組合關係 再看左下角的µC/FS,它是Micriµm公司的軟件,通常搭載在µC/OS平台上作為文件系統使用,因此它與µC/OS平台是一種"整體"與"部分"的關係。我們把這種強的"擁有"關係稱為組合關係,一般用實心菱形 實線箭頭表示。 聚合關係 與組合關係相近、容易混淆的是聚合關係。看右下角的LWIP,它是一個高度可移植、可固化的網絡協議棧,可與所有主流的操作系統兼容。那麼RTThread與LWIP就是一種弱的擁有關係,這體現在操作系統平台可以包含LWIP,但LWIP不是RTThread的一部分,它也可以單獨運行在裸機程序中。聚合關係用空心的菱形 實線箭頭表示。 實現關係 程序員對"接口"這個應該都不陌生,接口的存在是因為不同對象(類)之間存在一些共同的行為。那麼把這些行為抽象出來,單獨作為一個接口就可以提高代碼複用率。以POSIX接口為例,它的全稱為可移植性操作系統接口,是一套關於信息技術的IEEE標準。RTThread為了豐富用户生態、促進自身發展,就儘可能地去實現這些接口,使得其他系統的應用程序無痛遷移到RTThread。實現關係的表示方法俗稱棒棒糖表示法。 聯繫關係 如果你去閲讀RTThread的源碼,會發現RTThread不止代碼風格非常接近Linux內核,而且在很多設計上借鑑了Linux內核的實現。所以你可以在RTThread裏面你看到一些Linux的影子。Linux內核發展早、實力強,而且開源、免費,這些年已經風靡全球。所以RTThread關注Linux內核的發展,借鑑它的一些實現思路也無可厚非了。在這一層意義上,可以説RTThread與Linux是有聯繫關係的。聯繫關係用實線箭頭表示。

    embed linux share 代碼 UML

  • 代碼的設計圖紙——UML(上)

    優秀項目代碼是怎麼構建出來? 寫一步算一步? 憑空想象? 回答這個問題之前,先讓我們看這麼一個場景:搭豬窩or建高樓大廈。 搭豬窩   搭一個豬窩,可以這樣來操作: 根據豬體型大小,粗略計算出豬窩大概要佔幾平米。去生活市場購買相應數量的木塊、鐵錘、尺子、釘子等一些基本物料和工具。物料準備完畢後,使用鐵錘和釘子,很快可以利用木塊把豬窩的外形搭建好,然後再往裏面鋪一些稻草。 前後不需幾個小時,一個人就完成了從構思到施工的全部過程。這個豬窩只要能夠遮風擋雨以及保暖,就滿足豬的需求了。哪怕搭建得不夠牢固,也完全可以等空閒的時候,再返修一下。 建大廈   假如你要建造一座高層辦公大廈,若還是先備好木料、釘子和一些基本工具就開始工作,那將是非常愚蠢的。因為你所使用的資金可能是別人的,他們會對建築物的規模、外形和風格做出要求。同時,他們經常會改變想法,甚至是在工程已經開工之後。由於失敗的代價太高了,因此必須要做詳盡的計劃。 負責建築物設計和施工的是一個龐大的組織機構,你只是其中的一部分。這個組織將需要各種各樣的設計圖和模型,以供各方相互溝通。只有得到了合適的人員和工具,並對把建築概念轉換為實際建築的過程進行積極的治理,才能最終建成這座滿足使用要求的大廈。 UML建模語言 注意到沒有,搭豬窩和建高樓大廈之間,設計的技能和工作量差的可不是一點半點。如果沒有良好的設計、沒有對項目進行整體把控,那麼你的任何項目很有可能會淪落為倉促地搭豬窩。 如果你的目標是建造一個大廈式的軟件,那麼學會設計模式是非常重要的。UML建模就是進行良好軟件設計的工具,在代碼的世界裏,UML的各種圖就是工地上大廈的施工圖紙。UML之於程序員,就猶如CAD之於機械工程師,猶如PS之於美工,毫不誇張地説,它是我們設計一個優秀項目的左臂右磅。 UML與嵌入式   隨着後PC時代的到來,嵌入式抓住了物聯網、5G等高科技不斷髮展的風口,琳琅滿目的應用層出不窮。嵌入式編程與PC編程的邊界已經逐漸模糊,C語言對應的傳統結構化設計方法已經不能滿足嵌入式軟件設計和開發的需求。現在更為流行與使用的方法是用c語言來實現面向對象的語法(C /JAVA),比如用結構體來模擬類,用函數指針來表示成員函數。因此以往在C /JAVA領域上大量使用的代碼設計工具--UML,也勢必將在嵌入式領域攪起一番風雲!

    embed linux share 代碼 UML

  • 還在為垃圾代碼發愁嗎?學習設計模式吧

    嵌入式工程師現狀 在我頭兩三年的工作時間裏,接觸過不少嵌入式工程師,對這個行業的現狀有一定的深入瞭解。從傳統嵌入式工程師的學習路徑來看,大部分是從熟悉硬件手冊、配置寄存器開始學習,接着在邊做項目邊加深學習C語言,根據項目不同,會接觸一些基礎算法,比如控制上的pid、多軸插補,圖像處理上的濾波、分割等等。在這個階段中,會逐漸熟悉一些硬件之間的特性差異、掌握c語言編程來實現不同功能,學有餘力的可能還接觸了一些RTOS,更深入地掌握了多線程編程技巧、GUI開發界面、網絡編程等。 但是在走完這一階段之後,其實大部分工程師,代碼能力還是很弱。這是由於在實際工作過程,過於依賴面向過程開發,只管功能實現,忽視了代碼質量和提高。這樣的代碼維護起來非常費勁,添加或者修改一個功能,常常會牽一髮而動全身,讓你無從下手,恨不得將全部的代碼刪掉重寫!回首看一下過去幾年間自己的代碼,時常有抽自己幾巴掌的衝動。 意識覺醒 我們有幸在最好的開源時代,在嵌入式領域目前已經有大量優秀的開源代碼可以供所有程序員參考對比,比如熊大的RT-thread、周立功的AWTK;我們不幸在最壞的開源時代,當知識的門檻越來越低,每個人面臨的競爭壓力是前所未有的。這種壓力應該促使程序員去思考,如何去提高自己的代碼水平,如何去設計出如熊大、周立功他們一樣優秀的項目。 在開源商業軟件高速發展的今天,希望我的一點經驗和分享可以慢慢地讓一部分工程師意識覺醒,認識到斑駁繁雜的業務開發,需要大量的基礎知識的支撐。只有花狠功夫去夯實基礎,才能看得更長遠,走得更持久。 但是,必須要明白的一點是,基礎學科和理論大都不是與技術能力直接掛鈎的,無法通過短期的突破來取得立竿見影的效果。它更多的是慢慢在潛意思中生根發芽,默默地在解決問題的過程中影響你思考的角度和深度。這是緩慢受錘的過程,這一錘一錘的折磨,大部分人挺不過來。 如何破局 那麼怎麼吸收理解乏味沉悶的基礎知識?如何把它內化成自己的工程開發能力依然是很多程序的困惑。在被操作系統、編譯原理、微機原理等大部頭幾乎殺死了對編程的熱衷的同時,更是遲遲看不到投入產出比。甚至幾年工作下來,悲哀地發現,代碼水平跟還在大學裏的毛頭小子差不多。因此,選一個合適的切入點,在職業進階發展時建立起強大的學習自信心就尤為關鍵。設計模式就是這樣一個最佳的切入點,它既是非常重要的基礎知識,又直接反映技術能力水平。設計模式、數據結構、算法是程序員的三座大山,但是設計模式與數據結構和算法不一樣的地方在於,數據結構和算法是業務場景的總結,它的關注點在於如何寫出高效代碼來改善產品的性能,這在不同產品、不同業務上是差異很大的。而設計模式,更多的是一種工程經驗,它客觀地歸納了高質量代碼開發的技巧,指導着工程師把代碼寫得可維護性強、可擴展性強、健壯性強。所以,不必擔心設計模式是"屠龍刀",只要你有心想用好它,那麼在你的業務代碼中,一定有用得上的地方。除非你是想開發一個只管功能實現,不管後人維護擴展的爛項目。我在這些年的工作經歷中,實在是見過不少的爛代碼,比如命名不規範、類設計不合理、分層不清晰、沒有模塊化概念、代碼結構混亂、高度耦合等等。 應對面試中的設計模式 學習設計模式,最功利、最直接的目的,和算法一樣,可能就是應對面試了。不管你是前端工程師、後端工程師,還是嵌入式工程師,在高階崗位的求職面試中,設計模式問題是被問得頻率比較高的一類問題。特別是一些像 華為、大疆 這樣的大公司,比較重視候選人的基本功,經常會拿算法、設計模式之類的問題來考察候選人。所以,求職面試的時候,應該提前準備、温習一遍設計模式。儘管不一定每次面試都會被問到,但一旦被問到,如果回答得不好,就是一個敗筆,這場面試基本上也就涼涼了。所以,為了保證萬無一失,擺脱一旦被問到答不出來的窘境,對於設計模式這種大概率被問到的問題,需要未雨綢繆,認真提前準備一下。 關於嵌入式的設計模式 目前市面上關於設計模式的書籍和視頻,主要都是用面向對象的語言來進行講解,比如java\c 。而嵌入式程序員主要用c語言開發,那麼學習設計模式時不可避免會有一些語言上面的障礙,因此本系列文章,希望是以c語言來闡述清楚每種設計模式的用途。讀者在有了對它有一定的概念和理解後,再去看相關書籍時,會減少很多障礙。  

    embed linux share 單片機 設計模式 代碼 嵌入式

  • 字節終面:CPU 是如何讀寫內存的?

    如果你覺得這是一個非常簡單的問題,那麼你真應該好好讀讀本文,我敢保證這個問題絕沒有你想象的那麼簡單。注意,一定要完本文,否則可能會得出錯誤的結論。閒話少説,讓我們來看看CPU在讀寫內存時底層究竟發生了什麼。 誰來告訴CPU讀寫內存 我們第一個要搞清楚的問題是:誰來告訴CPU去讀寫內存?答案很明顯,是程序員,更具體的是編譯器。CPU只是按照指令按部就班的執行,機器指令從哪裏來的呢?是編譯器生成的,程序員通過高級語言編寫程序,編譯器將其翻譯為機器指令,機器指令來告訴CPU去讀寫內存。在精簡指令集架構下會有特定的機器指令,Load/Store指令來讀寫內存,以x86為代表的複雜指令集架構下沒有特定的訪存指令。精簡指令集下,一條機器指令操作的數據必須來存放在寄存器中,不能直接操作內存數據,因此RISC下,數據必須先從內存搬運到寄存器,這就是為什麼RISC下會有特定的Load/Store訪存指令,明白了吧。而x86下無此限制,一條機器指令操作的數據可以來自於寄存器也可以來自內存,因此這樣一條機器指令在執行過程中會首先從內存中讀取數據。關於複雜指令集以及精簡指令集你可以參考這兩篇文章《CPU進化論:複雜指令集》與《不懂精簡指令集還敢説自己是程序員?》 程序執行過程中需要讀寫來自內存中的數據 CPU需要訪問內存讀取下一條要執行的機器指令 然後CPU根據機器指令中包含的內存地址或者PC寄存器中下一條機器指令的地址訪問內存。這不就完了嗎?有了內存地址,CPU利用硬件通路直接讀內存就好了,你可能也是這樣的想的。真的是這樣嗎?彆着急,我們接着往下看,這兩節只是開胃菜,正餐才剛剛開始。 無處不在的28定律 28定律我想就不用多介紹了吧,在《不懂精簡指令集還敢説自己是程序員》這篇文章中也介紹過,CPU執行指令符合28定律,大部分時間都在執行那一少部分指令,這一現象的發現奠定了精簡指令集設計的基礎。而程序操作的數據也符合類似的定律,只不過不叫28定律,而是叫principle of locality,程序局部性原理。如果我們訪問內存中的一個數據A,那麼很有可能接下來再次訪問到,同時還很有可能訪問與數據A相鄰的數據B,這分別叫做時間局部性和空間局部性。如圖所示,該程序佔據的內存空間只有一少部分在程序執行過程經常用到。有了這個發現重點就來了,既然只用到很少一部分,那麼我們能不能把它們集中起來呢?就像這樣:集中起來然後呢?放到哪裏呢?當然是放到一種比內存速度更快的存儲介質上,這種介質就是我們熟悉的SRAM,普通內存一般是DRAM,這種讀寫速度更快的介質充當CPU和內存之間的Cache,這就是所謂的緩存。 天下沒有免費的午餐 雖然小小的cache能帶來性能的極大提升,但,這也是有代價的。這個代價出現在寫內存時。當CPU需要寫內存時該怎麼辦呢?現在有了cache,CPU不再直接與內存打交道,因此CPU直接寫cache,但此時就會有一個問題,那就是cache中的值更新了,但內存中的值還是舊的,這就是所謂的不一致問題,inconsistent.就像下圖這樣,cache中變量的值是4,但內存中的值是2。 異步更新緩存 這種方法性能差不是因為寫內存慢,寫內存確實是慢,更重要的原因是CPU在同步等待,因此很自然的,這類問題的統一解法就是把同步改為異步。關於同步和異步的話題,你可以參考這篇文章《從小白到高手,你需要理解同步和異步》。異步的這種方法是這樣的,當CPU寫內存時,直接更新cache,然後,注意,更新完cache後CPU就可以認為寫內存的操作已經完成了,儘管此時內存中保存的還是舊數據。當包含該數據的cache塊被剔除時再更新到內存中,這樣CPU更新cache與更新內存就解耦了,也就是説,CPU更新cache後不再等待內存更新,這就是異步,這種方案也被稱之為write-back,這種方案相比write-through來説更復雜,但很顯然,性能會更好。現在你應該能看到,添加cache後會帶來一系列問題,更不用説cache的替換算法,畢竟cache的容量有限,當cache已滿時,增加一項新的數據就要剔除一項舊的數據,那麼該剔除誰就是一個非常關鍵的問題,限於篇幅就不在這裏詳細講述了,你可以參考《深入理解操作系統》第7章有關於該策略的講解。 多核,多問題 當摩爾定律漸漸失效後雞賊的人類換了另一種提高CPU性能的方法,既然單個CPU性能不好提升了,我們還可以堆數量啊,這樣,CPU進入多核時代,程序員開始進入苦逼時代。擁有一堆核心的CPU其實是沒什麼用的,關鍵需要有配套的多線程程序才能真正發揮多核的威力,但寫過多線程程序的程序員都知道,能寫出來不容易,能寫出來並且能正確運行更不容易,關於多線程與多線程編程的詳細闡述請參見《深入理解操作系統》第5、6兩章(關注公眾號“碼農的荒島求生”並回復“操作系統”)。CPU開始擁有多個核心後不但苦逼了軟件工程師,硬件工程師也不能倖免。前文提到過,為提高CPU 訪存性能,CPU和內存之間會有一個層cache,但當CPU有多個核心後新的問題來了:現在假設內存中有一變量X,初始值為2。系統中有兩個CPU核心C1和C2,現在C1和C2要分別讀取內存中X的值,根據cache的工作原理,首次讀取X不能命中cache,因此從內存中讀取到X後更新相應的cache,現在C1 cache和C2 cache中都有變量X了,其值都是2。接下來C1需要對X執行 2操作,同樣根據cache的工作原理,C1從cache中拿到X的值 2後更新cache,在然後更新內存,此時C1 cache和內存中的X值都變為了4。然後C2也許需要對X執行加法操作,假設需要 4,同樣根據cache的工作原理,C2從cache中拿到X的值 4後更新cache,此時cache中的值變為了6(2 4),再更新內存,此時C2 cache和內存中的X值都變為了6。 看出問題在哪裏了嗎?一個初始值為2的變量,在分別 2和 4後正確的結果應該是2 2 4 = 8,但從上圖可以看出內存中X的值卻為6,問題出在哪了呢? 夠複雜了吧! 怎麼樣?到目前為止,是不是CPU讀寫內存沒有看上去那麼簡單?現代計算機中CPU和內存之間有多級cache,CPU讀寫內存時不但要維護cache和內存的一致性,同樣需要維護多核間cache的一致性。你以為這就完了,NONO,最大的謎團其實是接下來要講的。 天真的CPU CPU真的是很傻很天真的存在。上一節講的操作系統施加的障眼法把CPU也矇在鼓裏。CPU執行機器指令時,指令指示CPU從內存地址A中取出數據,然後CPU執行機器指令時下發命令:“給我從地址A中取出數據”,儘管真的能從地址A中取出數據,但這個地址A不是真的,不是真的,不是真的。因為這個地址A屬於虛擬內存,也就是那個“假的地址空間”,現代CPU內部有一個叫做MMU的模塊將這假的地址A轉換為真的地址B,將地址A轉換為真實的地址B之後才是本文之前講述的關於cache的那一部分。你以為這終於應該講完了吧!NONO!CPU給出內存地址,此後該地址被轉為真正的物理內存地址,接下來查L1 cache,L1 cache不命中查L2 cache,L2 cache不命中查L3 cache,L3 cache不能命中查內存。各單位注意,各單位注意,到查內存時還不算完,現在有了虛擬內存,內存其實也是一層cache,是磁盤的cache,也就是説查內存也有可能不會命中,因為內存中的數據可能被虛擬內存系統放到磁盤中了,如果內存也不能命中就要查磁盤。So crazy,限於篇幅這個過程不再展開,《深入理解操作系統》第七章有完整的講述。至此,CPU讀寫內存時完整的過程闡述完畢。

    架構師社區 CPU 內存 字節

  • Linux 內核 vs Windows 內核

    Windows 和 Linux 可以説是我們比較常見的兩款操作系統的。 Windows 基本佔領了電腦時代的市場,商業上取得了很大成功,但是它並不開源,所以要想接觸源碼得加入 Windows 的開發團隊中。 對於服務器使用的操作系統基本上都是 Linux,而且內核源碼也是開源的,任何人都可以下載,並增加自己的改動或功能,Linux 最大的魅力在於,全世界有非常多的技術大佬為它貢獻代碼。 這兩個操作系統各有千秋,不分伯仲。 操作系統核心的東西就是內核,這次我們就來看看,Linux 內核和 Windows 內核有什麼區別? 內核 什麼是內核呢? 計算機是由各種外部硬件設備組成的,比如內存、cpu、硬盤等,如果每個應用都要和這些硬件設備對接通信協議,那這樣太累了。 所以,這個中間人就由內核來負責,讓內核作為應用連接硬件設備的橋樑,應用程序只需關心與內核交互,不用關心硬件的細節。 內核內核有哪些能力呢? 現代操作系統,內核一般會提供 4 個基本能力: 管理進程、線程,決定哪個進程、線程使用 CPU,也就是進程調度的能力; 管理內存,決定內存的分配和回收,也就是內存管理的能力; 管理硬件設備,為進程與硬件設備之間提供通信能力,也就是硬件通信能力; 提供系統調用,如果應用程序要運行更高權限運行的服務,那麼就需要有系統調用,它是用户程序與操作系統之間的接口。 內核是怎麼工作的? 內核具有很高的權限,可以控制 cpu、內存、硬盤等硬件,而應用程序具有的權限很小,因此大多數操作系統,把內存分成了兩個區域: 內核空間,這個內存空間只有內核程序可以訪問; 用户空間,這個內存空間專門給應用程序使用; 用户空間的代碼只能訪問一個局部的內存空間,而內核空間的代碼可以訪問所有內存空間。 因此,當程序使用用户空間時,我們常説該程序在用户態執行,而當程序使內核空間時,程序則在內核態執行。 應用程序如果需要進入內核空間,就需要通過「系統調用」,下面來看看系統調用的過程: 內核程序執行在內核態,用户程序執行在用户態。當應用程序使用系統調用時,會產生一箇中斷。發生中斷後, CPU 會中斷當前在執行的用户程序,轉而跳轉到中斷處理程序,也就是開始執行內核程序。內核處理完後,主動觸發中斷,把 CPU 執行權限交回給用户程序,回到用户態繼續工作。 Linux 的設計 Linux 的開山始祖是來自一位名叫 Linus Torvalds 的芬蘭小夥子,他在 1991 年用 C 語言寫出了第一版的 Linux 操作系統,那年他 22 歲。 完成第一版 Linux 後,Linux Torvalds 就在網絡上發佈了 Linux 內核的源代碼,每個人都可以免費下載和使用。 Linux 內核設計的理念主要有這幾個點: MutiTask,多任務 SMP,對稱多處理 ELF,可執行文件鏈接格式 Monolithic Kernel,宏內核 MutiTask MutiTask 的意思是多任務,代表着 Linux 是一個多任務的操作系統。 多任務意味着可以有多個任務同時執行,這裏的「同時」可以是併發或並行: 對於單核 CPU 時,可以讓每個任務執行一小段時間,時間到就切換另外一個任務,從宏觀角度看,一段時間內執行了多個任務,這被稱為併發。 對於多核 CPU 時,多個任務可以同時被不同核心的 CPU 同時執行,這被稱為並行。 SMP SMP 的意思是對稱多處理,代表着每個 CPU 的地位是相等的,對資源的使用權限也是相同的,多個 CPU 共享同一個內存,每個 CPU 都可以訪問完整的內存和硬件資源。 這個特點決定了 Linux 操作系統不會有某個 CPU 單獨服務應用程序或內核程序,而是每個程序都可以被分配到任意一個 CPU 上被執行。 ELF ELF 的意思是可執行文件鏈接格式,它是 Linux 操作系統中可執行文件的存儲格式,你可以從下圖看到它的結構: ELF 文件格式ELF 把文件分成了一個個分段,每一個段都有自己的作用,具體每個段的作用這裏我就不詳細説明了,感興趣的同學可以去看《程序員的自我修養——鏈接、裝載和庫》這本書。 另外,ELF 文件有兩種索引,Program header table 中記錄了「運行時」所需的段,而 Section header table 記錄了二進制文件中各個「段的首地址」。 那 ELF 文件怎麼生成的呢? 我們編寫的代碼,首先通過「編譯器」編譯成彙編代碼,接着通過「彙編器」變成目標代碼,也就是目標文件,最後通過「鏈接器」把多個目標文件以及調用的各種函數庫鏈接起來,形成一個可執行文件,也就是 ELF 文件。 那 ELF 文件是怎麼被執行的呢? 執行 ELF 文件的時候,會通過「裝載器」把 ELF 文件裝載到內存裏,CPU 讀取內存中的指令和數據,於是程序就被執行起來了。 Monolithic Kernel Monolithic Kernel 的意思是宏內核,Linux 內核架構就是宏內核,意味着 Linux 的內核是一個完整的可執行程序,且擁有最高的權限。 宏內核的特徵是系統內核的所有模塊,比如進程調度、內存管理、文件系統、設備驅動等,都運行在內核態。 不過,Linux 也實現了動態加載內核模塊的功能,例如大部分設備驅動是以可加載模塊的形式存在的,與內核其他模塊解藕,讓驅動開發和驅動加載更為方便、靈活。 分別為宏內核、微內核、混合內核的操作系統結構與宏內核相反的是微內核,微內核架構的內核只保留最基本的能力,比如進程調度、虛擬機內存、中斷等,把一些應用放到了用户空間,比如驅動程序、文件系統等。這樣服務與服務之間是隔離的,單個服務出現故障或者完全攻擊,也不會導致整個操作系統掛掉,提高了操作系統的穩定性和可靠性。 微內核內核功能少,可移植性高,相比宏內核有一點不好的地方在於,由於驅動程序不在內核中,而且驅動程序一般會頻繁調用底層能力的,於是驅動和硬件設備交互就需要頻繁切換到內核態,這樣會帶來性能損耗。華為的鴻蒙操作系統的內核架構就是微內核。 還有一種內核叫混合類型內核,它的架構有點像微內核,內核裏面會有一個最小版本的內核,然後其他模塊會在這個基礎上搭建,然後實現的時候會跟宏內核類似,也就是把整個內核做成一個完整的程序,大部分服務都在內核中,這就像是宏內核的方式包裹着一個微內核。 Windows 設計 當今 Windows 7、Windows 10 使用的內核叫 Windows NT,NT 全稱叫 New Technology。 下圖是 Windows NT 的結構圖片: Windows NT 的結構Windows 和 Linux 一樣,同樣支持 MutiTask 和 SMP,但不同的是,Windows 的內核設計是混合型內核,在上圖你可以看到內核中有一個 MicroKernel 模塊,這個就是最小版本的內核,而整個內核實現是一個完整的程序,含有非常多模塊。 Windows 的可執行文件的格式與 Linux 也不同,所以這兩個系統的可執行文件是不可以在對方上運行的。 Windows 的可執行文件格式叫 PE,稱為可移植執行文件,擴展名通常是.exe、.dll、.sys等。 PE 的結構你可以從下圖中看到,它與 ELF 結構有一點相似。 PE 文件結構 總結 對於內核的架構一般有這三種類型: 宏內核,包含多個模塊,整個內核像一個完整的程序; 微內核,有一個最小版本的內核,一些模塊和服務則由用户態管理; 混合內核,是宏內核和微內核的結合體,內核中抽象出了微內核的概念,也就是內核中會有一個小型的內核,其他模塊就在這個基礎上搭建,整個內核是個完整的程序; Linux 的內核設計是採用了宏內核,Windows 的內核設計則是採用了混合內核。 這兩個操作系統的可執行文件格式也不一樣, Linux 可執行文件格式叫作 ELF,Windows 可執行文件格式叫作 PE。

    程序員小灰 Windows 內核 Linux

首頁  上一頁  1 2 3 4 5 6 7 8 9 10 下一頁 尾頁
發佈文章