自開班以來已經(jīng)近兩個月了,不能說所教知識都掌握得非常好,但至少還是能跟著老師步伐、理解關鍵知識點、正確完成作業(yè)的。現(xiàn)在還沒真正開始進入硬件方面的學習,但我相信對基本理論知識及編程開發(fā)的牢固掌握是后續(xù)深入學習的前提。因此,趁著現(xiàn)在正處與學習進度的轉折點,有必要對先前的知識作一下概括性總結。
★Linux的使用
開班的第一天,老師就給我們講了為什么要先學c、學linux:因為嵌入式的根本就是軟件驅動硬件,而C語言是最接近硬件的語言、有指針的概念、可以直接操作硬件,另外,功能復雜的硬件是含有操作系統(tǒng)的,這就需要我們選擇使用廣泛而開源的linux來學習。
◇Shell 命令
shell是一個命令行解釋器,命令行格式為:命令名稱、選項、參數(shù),常用的命令有:cd進入目錄;ls顯示目錄下的文件;touch、mkdir創(chuàng)建文件、文件夾;mv、cp、rm移動、復制、刪除文件和文件夾;zip、gzip、tar壓縮和解壓文件;ln創(chuàng)建軟硬鏈接文件。
◇文件管理、用戶管理
Linux是一個多用戶系統(tǒng),它可以用adduser來創(chuàng)建多個用戶,并用su來進行不同用戶及用戶與管理員之間的切換;另外,與目錄結構屬于分區(qū)的windows不同,在Linux的文件系統(tǒng)中,分區(qū)屬于目錄結構。
◇軟件管理
ubuntu上的軟件包管理工具apt可以通過網(wǎng)絡很方便地完成軟件包的獲取、安裝、卸載、查詢等操作。比如,當我們在使用putty前需要安裝SSH的時候,就可以通過命令:sudo apt-get update、sudo apt-get install SSH 來安裝。
◇VI 編輯器,GCC 編譯器
Vi是Linux系統(tǒng)中常用的一個文本編輯器,通過vi加上文件名可以對文件創(chuàng)建或編輯。三種模式中:命令模式是我們進入編輯器的第一模式,可以對文本進行剪切復制替換刪除操作;輸入模式可以是對文件進行常規(guī)編輯;底行模式可以保存文本并退出。vi編輯好的c文件需要被編譯二進制文件才可以被機器識別運行,而GCC 編譯器就承載著這樣的功能,它通過預處理、編譯、匯編、鏈接四個步驟完成該操作。
★C語言編程
C語言學習的開始就是掌握基本的語法規(guī)則,主要包括各種基本類型常量變量、運算符、控制流、函數(shù)的使用。這部分總的來說沒有什么難度,但有一些容易忽略的細節(jié)需要留意,如同為單目運算的*解引用和++操作同時使用時哪個優(yōu)先運算;數(shù)組被定義后其空間大小和首地址不允許改變;要用strcmp()判斷字符串相等而不能直接用==;指針定義后但沒初始化會變成野指針,后續(xù)使用可能發(fā)生段錯誤;使用較大的數(shù)據(jù)時應在堆中開辟空間存放以防棧溢出。
☆數(shù)據(jù)結構
數(shù)據(jù)結構就是對數(shù)據(jù)進行人為的格式化規(guī)范化儲存,使得數(shù)據(jù)能夠快捷地增刪查改,不同的數(shù)據(jù)結構有不同的優(yōu)缺點及主要用途,如順序表查找快增刪慢而鏈表查找滿增刪快。鑒于內核鏈表的重要性及以后使用的廣泛性,在此只做內核鏈表的總結。內核鏈表和雙向循環(huán)鏈表類似,與之不同的是,內核鏈表將數(shù)據(jù)和鏈表剝離開,并提供了很多的宏和封裝函數(shù)。其中非常重要的是list_for_each()、list_entry(),它們分別實現(xiàn)了對小結構體(循環(huán)鏈表)的遍歷操作、通過小結構體的地址反推找到大結構體的地址,從而通過大結構體得到其下的數(shù)據(jù)域。另外還有l(wèi)ist_add()、list_del_init()封裝實現(xiàn)了對大結構體節(jié)點的插入、孤立刪除,不然自己搭建雙向循環(huán)鏈表的話,就需在頭插node節(jié)點時要寫上經(jīng)典的四句指針域重連接指令:①node->next = head->next;②head->next->pre = node;③node->pre = head;④head->next = node;
☆文件IO
這里的iO指的是內存和磁盤間的文件交互,由于linux“一切皆文件的”的特性,其7種類型的文件都是可以被輸入輸出的。其間我們重點學習了文件IO和標準IO,他們的主要區(qū)別就是前者直接調用系統(tǒng)函數(shù)沒有緩沖區(qū),而后者調用的是封裝好的庫函數(shù)有緩沖區(qū)。一般來說,文件IO是專門給文件使用的,而標準IO是專門給設備使用的。文件、標準io的打開和關閉所用的函數(shù)是相似的,只是一個有f一個沒f;但他們的讀寫函數(shù)差別就比較大了,文件io用的是read()、write(),而標準IO根據(jù)輸入輸出是否格式化分為printf、put、scanf、get,具體還會根據(jù)一字一行一塊地讀寫及讀寫目標地的不同而有所不同。另外還了解了下利用time()、ctime()來獲取標準時間并轉化為可閱讀化時間的時間編程,以及通過lstat()、opendir()、readdir()來查看目錄下的文件屬性。最后介紹了動靜態(tài)庫的概念及制作,前者在文件運行時才加載到可執(zhí)行文件中,而后者在編譯時即完成,至于二者制作步驟的話比較復雜,需要用到的時候再對照著筆記制作好了。
☆進程線程
這部分我想是重中之重,畢竟進程線程出現(xiàn)的一大動機就跟人類的無限欲望相關聯(lián):想要在越短的時間做越多的事情。進程就是執(zhí)行中的程序,但與只包含指令和數(shù)據(jù)的程序不同,進程有屬于自己的地址空間,里面不僅含有指令段數(shù)據(jù)段,還有動態(tài)的堆棧段,因此多個進程可以實現(xiàn)了同一時間做多個任務。進程通過fork()函數(shù)創(chuàng)建,對應的PCB由內核創(chuàng)建并保存在內核空間。盡管多核芯片的出現(xiàn)可以讓多個進程真的在同時執(zhí)行任務,但不是所有進程都同時處于運行狀態(tài)的,更多的進程是在極小的時間片段下輪流替換著來工作,至于替換的順序是由cpu調度機制決定的,我們無法確定,這也是為什么進程擁有異步特性的原因。未在運行狀態(tài)的進程往往處于隊列就緒等待狀態(tài)或休眠狀態(tài),其它具體的狀態(tài)可以通過命令ps -aux查看。此外,運行中的進程還分為前臺運行和后臺運行,如果進程是后臺運行的話,就不能對它進行前臺操作,如不能對它ctrl + c暫停,這時候可以通過fg指令把它轉變?yōu)榍芭_運行或直接用kill指令終止進程。被創(chuàng)建后的進程是有生命周期的,它不但可以exit()自行終止進程,還可以用exec函數(shù)族中途改為執(zhí)行新的進程,殊途同歸,進程最后都是要終止的,終止后的進程的PCB需要被其父進程wait()回收,中間有差錯的話就可能導致孤兒進程或僵尸進程的出現(xiàn)。
當需要進程不受干擾地一直在后臺運行、周期性地等待或者執(zhí)行某一個任務的時候,可以將它設置為守護進程。守護進程不與任何終端關聯(lián),即使終端關閉了也還是會照常運行,老師教的7步創(chuàng)建法得記熟。值得注意的是,因為守護進程無法往標準輸出打印,中間出錯了也沒人知道,所以得找個地方儲存它的運行情況,因此系統(tǒng)日志應運而生。系統(tǒng)日志可以通過命令cat /var/log/syslog查看。
進程可以通過無名管道、有名管道、信號、共享內存、消息隊列、信號量來進行進程間通信,這些通信方式都是在內核中得以實現(xiàn)的。具體的函數(shù)及使用筆記上都有詳細記錄,就不再贅述了。值得留意的是無名有名管道、消息隊列中的同一數(shù)據(jù)是讀了一次就沒有了的,而共享內存中的同一數(shù)據(jù)可以被多次讀取。
進程和線程都是為了實現(xiàn)計算機的并發(fā)功能,但是進程的創(chuàng)建消亡,及進程間的切換都很耗費資源,每次切換進程都要進程上下文切換。線程的優(yōu)勢是多個線程共享指令和全局變量,這就減少了資源管理的消耗,從而更專注于任務的執(zhí)行。但有優(yōu)點就有缺點,資源的共享可能會致使多個線程同時對臨界資源進行操作,從而導致運算結果的不準確。為了解決這一問題,出現(xiàn)了線程的互斥與同步。線程互斥利用mutex鎖在臨界區(qū)的前后分別pthread_mutex_lock()上鎖和pthread_mutex_destroy()解鎖,實現(xiàn)同一時間只允許一個線程操作臨界資源。線程的同步用的是信號量,相當于加上了數(shù)量的線程的互斥,通過合理地使用p+1、v-1操作使得多個線程按一定次序運行。
以上即為近兩個月來重要知識的概括性總結。