嵌入式知識點蒐集

//幾種接口
UART	串  異步	慢 波特率設置全雙工	 2線 Rx、Tx   星樹 遠
I2C     串  同步 慢 半雙工             2線SDA、SCL  總線仲裁機  近
SPI     串  同步 快 全雙工             3線或4線SCLK、SIMO、SOMI、SS(片選)  環  遠
usb     串  同步 快 半雙工          4線Vbus(5V)、GND、D+、D-(3.3V)	星形   近


在這裏插入圖片描述
linux用戶態切換到內核態的方法:
1.系統調用system_call()
軟件中斷-》產生一個0x80的異常-》中斷描述符表IDT-》system_call(系統調用處理函數)-》系統調用表

2.procfs方式
進程文件系統 僞文件系統,爲什麼說是 僞 文件系統呢?因爲它不佔用外部存儲空間,只是佔用少量的內存,通常是掛載在 /proc 目錄下

3.sysctl
sysctl 是一個 Linux 命令,它可以在內核運行過程中,動態修改內核參數。

4.sysfs
和 procfs 不同的是,sysfs 是將一些原本在 procfs 中的,關於設備和驅動的部分,獨立出來,以 “設備樹” 的形式呈現給用戶。

5.netlink
使得它可以用於內核與多種用戶進程之間的消息傳遞系統,比如路由子系統,防火牆(Netfilter),ipsec 安全策略等等。

6.ioctl
ioctl:函數是文件結構中的一個屬性分量,就是說如果你的驅動程序提供了對ioctl的支持,用戶就可以在用戶程序中使用ioctl函數控制設備的I/O通道。

用戶態和內核態的轉換方式:
*系統調用:這是用戶進程主動要求切換到內核態的一種方式,用戶進程通過系統調用申請操作系統提供的服務程序完成工作。而系統調用的機制其核心還是使用了操作系統爲用戶特別開放的一箇中斷來實現,例如Linux的ine 80h中斷;
*異常:當CPU在執行運行在用戶態的程序時,發現了某些事件不可知的異常,這是會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就到了內核態,比如缺頁異常;
*外圍設備的中斷:當外圍設備完成用戶請求的操作之後,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條將要執行的指令轉而去執行中斷信號的處理程序,如果先執行的指令是用戶態下的程序,那麼這個轉換的過程自然也就發生了有用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行後續操作等。

/tmp:這是讓一般使用者或者是正在執行的程序暫時放置檔案的地方。
/usr:不是user的縮寫,其實usr是Unix Software Resource的縮寫, 也就是Unix操作系統軟件資源所放置的目錄,而不是用戶的數據啦
/etc:系統主要的設定檔幾乎都放置在這個目錄內

const int a;
int const a;
const int *a;
int * const a;
const int * const a;
int const * const a;
前兩個的作用是一樣,a是一個常整型數;
第三個意味着a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以);
第四個意思a是一個指向整型 數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的);
最後兩個意味着a是一個指向常整型數的常指針(也就是說,指針指向的整型數 是不可修改的,同時指針也是不可修改的)。

如果修改,編譯直接報錯。

在32位系統中,有如下結構體,那麼sizeof(fun)的數值是()

#pragma pack(1)
 
struct fun{
	int i;
	double d;
	char c;
};

解答:13。

可能是一般的內存對齊做習慣了,如果本題採用內存對齊的話,結果就是24(int 4 double char 7)。但是#pragma pack(1)讓編譯器將結構體數據強制按1來對齊。

每個特定平臺上的編譯器都有自己的默認“對齊係數”(32位機一般爲4,64位機一般爲8)。我們可以通過預編譯命令#pragma pack(k),k=1,2,4,8,16來改變這個係數,其中k就是需要指定的“對齊係數”。

只需牢記:
第一個數據成員放在offset爲0的地方,對齊按照對齊係數和自身佔用字節數中,二者比較小的那個進行對齊;
在數據成員完成各自對齊以後,struct或者union本身也要進行對齊,對齊將按照對齊係數和struct或者union中最大數據成員長度中比較小的那個進行;
原文:https://blog.csdn.net/qq_38410730/article/details/80951443

那麼爲什麼要內存對其呢?
如果操作1字節的數據,可以是任意地址,如果是操作2字節的數據,如果開始地址在偶數地址,一次就可以取2字節,如果開始地址在奇數,就要2次內存操作才能完成;如果操作4字節的數據,最好開始地址在能被4整除的數值上,這樣可以用一條32位的內存操作指令完成。同樣,8字節的開始位置最好的能被8整除的數值上,這樣可以用一條64位的內存操作指令完成。
比如32位機,32根地址線,32根數據線,取數時,CPU的32根據地址線與內存的0-3號地址對齊,CPU的32位的數據線也同樣,一個讀取週期只能取這0-3地址的3個字節。如果你是取3-4地址的數據,CPU會自動把它分解成2次取數據操作,一次取8位的3單元和一次取84單元數據。
只有開始地址是048...32位的數據操作才能一次操作完成,內存不支持從1號單元開始的4字節讀,CPU和內存的數據線必須相應數據線對齊纔行。
歸納:
1.平臺移植的問題。某些平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。 
2.性能的問題。對其的一次就可以讀完

 /*int 字節*/
    printf("%d\n",sizeof(int)); //4 
    printf("%d\n",sizeof(__int8)); //1
    printf("%d\n",sizeof(__int16)); //2
    printf("%d\n",sizeof(__int32)); //4
    printf("%d\n",sizeof(__int64)); //8
    /*char 字節*/
    printf("%d\n",sizeof(char)); //1
    /*double 字節*/
    printf("%d\n",sizeof(double)); //8
    /*long 字節*/
    printf("%d\n",sizeof(long)); //4
    printf("%d\n",sizeof(long int)); //4
    printf("%d\n",sizeof(long long)); //8
    /*short 字節*/
    printf("%d\n",sizeof(short)); //2 
    printf("%d\n",sizeof(short int)); //2
    /*unsigned 字節*/
    printf("%d\n",sizeof(unsigned)); //4
    printf("%d\n",sizeof(unsigned int)); //4
    /*signed 字節*/
    printf("%d\n",sizeof(signed)); //4
    printf("%d\n",sizeof(signed int)); //4
    /*const 字節*/
    printf("%d\n",sizeof(const)); //4

Big-endian大端模式 低對高 高對低
Little-endian小端模式 低對低 高對高

棧:由編譯器自動分配釋放,存放函數的參數值,局部變量等值;
靜態存儲區:一定會存在且不會消失,這樣的數據包括常量、常變量(const 變量)、靜態變量、全局變量等;
常量存儲區:常量佔用內存,只讀狀態,決不可修改,常量字符串就是放在這裏的。
BSS段:保存定義但是未賦值的變量。

volatile應該是在編譯階段,extern在鏈接階段起到作用

進程間,文件描述符是獨立的

實時操作系統是保證在一定時間限制內完成特定功能的操作系統。實時操作系統有硬實時和軟實時之分,硬實時要求在規定的時間內必須完成操作,這是在操作系統設計時保證的;軟實時則只要按照任務的優先級,儘可能快地完成操作即可。

實時性最主要的含義是:任務的最遲完成時間是可確認預知的。
比較項目				非實時系統			實時系統
交互能力				較強     			較弱
響應時間				秒集	        		毫秒、微秒級
可靠性				一般	        		較高
進程完成的截止期限		沒有	 	    		有
進程切換的要求		一般					快
內核					非可剝奪(體現公平)	可剝奪(體現優先級別)

	內存屏障,也稱內存柵欄,內存柵障,屏障指令等, 是一類同步屏障指令,是CPU或編譯器在對內存隨機訪問的操作中的一個同步點,使得此點之前的所有讀寫操作都執行後纔可以開始執行此點之後的操作。
  大多數現代計算機爲了提高性能而採取亂序執行,這使得內存屏障成爲必須。
  語義上,內存屏障之前的所有寫操作都要寫入內存;內存屏障之後的讀操作都可以獲得同步屏障之前的寫操作的結果。因此,對於敏感的程序塊,寫操作之後、讀操作之前可以插入內存屏障。
  屏障的分類
  1.編譯器引起的內存屏障  volatile
  2.緩存引起的內存屏障
  3.亂序執行引起的內存屏障

//內存順序
	內存順序描述了計算機 CPU 獲取內存的順序,內存的排序既可能發生在編譯器編譯期間,也可能發生在 CPU 指令執行期間。
	爲了儘可能地提高計算機資源利用率和性能,編譯器會對代碼進行重新排序, CPU 會對指令進行重新排序、延緩執行、各種緩存等等,以達到更好的執行效果。當然,任何排序都不能違背代碼本身所表達的意義,並且在單線程情況下,通常不會有任何問題。
	但是在多線程環境下,比如無鎖(lock-free)數據結構的設計中,指令的亂序執行會造成無法預測的行爲。所以我們通常引入內存柵欄(Memory Barrier)這一概念來解決可能存在的併發問題。

//指令序列
你有一臺很簡單的電腦,它只有兩個寄存器X和Y。每個寄存器可以存放一個整數。電腦只有兩條指令: 
指令[X]: X=X+Y 
指令[Y]: Y=X+Y 
剛開始的時候兩個寄存器裏都是1,經過經過一系列的指令,兩個寄存器裏的內容會發生變化.比如指令序列XXY的執行過程是: 
1 1 -> 2 1 -> 3 1 -> 3 4 
現在給你一個數R,讓你輸出最短的指令序列使得X寄存器裏數是R.如果存在多個最短指令序列,那麼輸出字典序最小的一個.
Sample Input:
3
10
Sample Output:
XX
XXYYX

//ABA問題
什麼是ABA問題?(掉包,替換)
ABA問題的根本在於cas在修改變量的時候,無法記錄變量的狀態,比如修改的次數,否修改過這個變量。這樣就很容易在一個線程將A修改成B時,另一個線程又會把B修改成A,造成casd多次執行的問題。
T1 線程從共享的內存地址讀取值 A;
T1 線程被搶佔,線程 T2 開始運行;
T2 線程將共享的內存地址中的值由 A 改成 B,然後又改成 A;
T1 線程繼續執行,讀取共享的內存地址中的值 A,認爲沒有改變,然後繼續執行

//無鎖編程Lock-Free 非阻塞同步
有鎖編程
	在多線程編程中只要需要共享某些數據,就應當將對它的訪問串行化。比如像++count(count是整型變量)這樣的簡單操作也得加鎖,因爲即便是增量操作這樣的操作,,實際上也是分三步進行的:讀、改、寫(回)。
	執行的過程中,這兩條指令之間也是可以被打斷的,而不是一條原子操作。(也就是所謂的寫撕裂)
	所以修改共享數據的操作必須以原子操作的形式出現,這樣才能保證沒有其它線程能在中途插一腳來破壞相應數據。而在使用鎖機制的過程中,即便在鎖的粒度(granularity),負載(overhead),競爭(contention),死鎖(deadlock)等需要重點控制的方面解決的很好,也無法徹底避免這種機制的如下一些缺點:
其次是避免鎖的使用引起的錯誤和問題。
1.死鎖(dead lock):兩個以上線程互相等待
2.鎖護送(lock convoy):多個同優先級的線程反覆競爭同一個鎖,搶佔鎖失敗後強制上下文切換,引起性能下降
3.優先級反轉(priority inversion):低優先級線程擁有鎖時被中優先級的線程搶佔,而高優先級的線程因爲申請不到鎖被阻塞

無鎖編程(Lock-Free)就是在某些應用場景和領域下解決以上基於鎖機制的併發編程的一種方案。
1.原子操作
2.內存柵欄(memory barriers), 
3.內存順序衝突(memory order),
4.指令序列一致性(sequential consistency)和順ABA現象等等。

在現代的 CPU 處理器上,很多操作已經被設計爲原子的,比如對齊讀(Aligned Read)和對齊寫(Aligned Write)等。Read-Modify-Write(RMW)操作的設計讓執行更復雜的事務操作變成了原子操作,當有多個寫入者想對相同的內存進行修改時,保證一次只執行一個操作。
RMW 操作在不同的 CPU 家族中是通過不同的方式來支持的:
1.x86/64 和 Itanium 架構通過 Compare-And-Swap (CAS) 方式來實現
2.PowerPC、MIPS 和 ARM 架構通過 Load-Link/Store-Conditional (LL/SC) 方式來實現

無鎖編程具體使用和考慮到的技術方法包括:原子操作(atomic operations), 內存柵欄(memory barriers), 內存順序衝突(memory order), 指令序列一致性(sequential consistency)和順ABA現象等等。
在這其中最基礎最重要的是操作的原子性或說原子操作。原子操作可以理解爲在執行完畢之前不會被任何其它任務或事件中斷的一系列操作。原子操作是非阻塞編程最核心基本的部分,沒有原子操作的話,操作會因爲中斷異常等各種原因引起數據狀態的不一致從而影響到程序的正確。
對於原子操作的實現機制,在硬件層面上CPU處理器會默認保證基本的內存操作的原子性,CPU保證從系統內存當中讀取或者寫入一個字節的行爲肯定是原子的,當一個處理器讀取一個字節時,其他CPU處理器不能訪問這個字節的內存地址。但是對於複雜的內存操作CPU處理器不能自動保證其原子性,比如跨總線寬度或者跨多個緩存行(Cache Line),跨頁表的訪問等。這個時候就需要用到CPU指令集中設計的原子操作指令,現在大部分CPU指令集都會支持一系列的原子操作。
而在無鎖編程中經常用到的原子操作是Read-Modify-Write  (RMW)這種類型的,這其中最常用的原子操作又是 COMPARE AND SWAP(CAS),幾乎所有的CPU指令集都支持CAS的原子操作,比如X86平臺下中的是 CMPXCHG(Compare Are Exchange)。
繼續說一下CAS,CAS操作行爲是比較某個內存地址處的內容是否和期望值一致,如果一致則將該地址處的數值替換爲一個新值。CAS操作具體的實現原理主要是兩種方式:總線鎖定和緩存鎖定。所謂總線鎖定,就是CPU執行某條指令的時候先鎖住數據總線的, 使用同一條數據總線的CPU就無法訪問內存了,在指令執行完成後再釋放鎖住的數據總線。鎖住數據總線的方式系統開銷很大,限制了訪問內存的效率,所以又有了基於CPU緩存一致性來保持操作原子性作的方法作爲補充,簡單來說就是用CPU的緩存一致性的機制來防止內存區域的數據被兩個以上的處理器修改。
最後這裏隨便說一下CAS操作的ABA的問題,所謂的ABA的問題簡要的說就是,線程a先讀取了要對比的值v後,被線程b搶佔了,線程b對v進行了修改後又改會v原來的值,線程1繼續運行執行CAS操作的時候,無法判斷出v的值被改過又改回來。
解決ABA的問題的一種方法是,一次用CAS檢查雙倍長度的值,前半部是指針,後半部分是一個計數器;或者對CAS的數值加上版本號。 

中斷向量:中斷服務程序的入口地址;
中斷向量表:把系統中所有的中斷類型碼及其對應的中斷向量按一定的規律存放在一個區域內,這個存儲區域就叫做中斷向量表;
中斷源:軟中斷/內中斷、外中斷/硬件中斷、異常等。
請求中斷→中斷響應→保護現場→中斷服務→恢復現場→中斷返回。

虛擬內存管理(Virtual Memory Management) 機制,這需要MMU
MMU就是負責虛擬地址(virtual address)轉化成物理地址(physical address)
CPU看到的用到的只是VA,CPU不管VA最終是怎樣到PA的;
而cache、MMU也是看不到VA的,它們使用的是MVA(VA到MVA的轉換是由硬件自動完成的);
實際設備看不到VA、MVA(轉變後的虛擬地址),讀寫設備使用的是PA物理地址。

I2c通信協議

在這裏插入圖片描述
;在總線空閒狀態時,這兩根線一般被上面所接的上拉電阻拉高,保持着高電平。
I2C通信方式爲半雙工,只有一根SDA線,同一時間只可以單向通信,485也爲半雙工,SPI和uart爲雙工。

  1. I2C總線特徵
    I2C總線上的每一個設備都可以作爲主設備或者從設備,而且每一個設備都會對應一個唯一的地址(地址通過物理接地或者拉高,可以從I2C器件的數據手冊得知,如TVP5158芯片,7位地址依次bit6~bit0:x101 1xxx, 最低三位可配,如果全部物理接地,則該設備地址爲0x58, 而之所以7bit因爲1個bit要代表方向,主向從和從向主),主從設備之間就通過這個地址來確定與哪個器件進行通信,在通常的應用中,我們把CPU帶I2C總線接口的模塊作爲主設備,把掛接在總線上的其他設備都作爲從設備。
    I2C總線上可掛接的設備數量受總線的最大電容400pF 限制,如果所掛接的是相同型號的器件,則還受器件地址位的限制。
    I2C總線數據傳輸速率在標準模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s。一般通過I2C總線接口可編程時鐘來實現傳輸速率的調整,同時也跟所接的上拉電阻的阻值有關。

2 I2C總線協議
I2C協議規定,總線上數據的傳輸必須以一個起始信號作爲開始條件,以一個結束信號作爲傳輸的停止條件。起始和結束信號總是由主設備產生(意味着從設備不可以主動通信?所有的通信都是主設備發起的,主可以發出詢問的command,然後等待從設備的通信)。
起始和結束信號產生條件:總線在空閒狀態時,SCL和SDA都保持着高電平,當SCL爲高電平而SDA由高到低的跳變,表示產生一個起始條件;當SCL爲高而SDA由低到高的跳變,表示產生一個停止條件。
在起始條件產生後,總線處於忙狀態,由本次數據傳輸的主從設備獨佔,其他I2C器件無法訪問總線;而在停止條件產生後,本次數據傳輸的主從設備將釋放總線,總線再次處於空閒狀態。起始和結束如圖所示:
在這裏插入圖片描述
在瞭解起始條件和停止條件後,我們再來看看在這個過程中數據的傳輸是如何進行的。前面我們已經提到過,數據傳輸以字節爲單位。主設備在SCL線上產生每個時鐘脈衝的過程中將在SDA線上傳輸一個數據位,當一個字節按數據位從高位到低位的順序傳輸完後,緊接着從設備將拉低SDA線,回傳給主設備一個應答位, 此時才認爲一個字節真正的被傳輸完成。當然,並不是所有的字節傳輸都必須有一個應答位,比如:當從設備不能再接收主設備發送的數據時,從設備將回傳一個否 定應答位。數據傳輸的過程如圖所示:
在這裏插入圖片描述
在前面我們還提到過,I2C總線上的每一個設備都對應一個唯一的地址,主從設備之間的數據傳輸是建立在地址的基礎上,也就是說,主設備在傳輸有效數據之前要先指定從設備的地址,地址指定的過程和上面數據傳輸的過程一樣,只不過大多數從設備的地址是7位的,然後協議規定再給地址添加一個最低位用來表示接下來數據傳輸的方向,0表示主設備向從設備寫數據,1表示主設備向從設備讀數據。向指定設備發送數據的格式如圖所示:(每一最小包數據由9bit組成,8bit內容+1bit ACK, 如果是地址數據,則8bit包含1bit方向)
在這裏插入圖片描述
在這裏插入圖片描述
4 I2C總線操作
對I2C總線的操作實際就是主從設備之間的讀寫操作。大致可分爲以下三種操作情況:
主設備往從設備中寫數據。數據傳輸格式如下:
在這裏插入圖片描述
主設備從從設備中讀數據。數據傳輸格式如下:
在這裏插入圖片描述
主設備往從設備中寫數據,然後重啓起始條件,緊接着從從設備中讀取數據;或者是主設備從從設備中讀數據,然後重啓起始條件,緊接着主設備往從設備中寫數據。數據傳輸格式如下:
在這裏插入圖片描述
第三種操作在單個主設備系統中,重複的開啓起始條件機制要比用STOP終止傳輸後又再次開啓總線更有效率。

時鐘同步和仲裁
如果兩個master都想在同一條空閒總線上傳輸,此時必須能夠使用某種機制來選擇將總線控制權交給哪個master,這是通過時鐘同步和仲裁來完成的,而被迫讓出控制權的master則需要等待總線空閒後再繼續傳輸。在單一master的系統上無需實現時鐘同步和仲裁。

總線仲裁
總線仲裁和時鐘同步類似,當所有master在SDA上都寫1時,SDA的數據纔是1,只要有一個master寫0,那此時SDA上的數據就是0。一個master每發送一個bit數據,在SCL處於高電平時,就檢查看SDA的電平是否和發送的數據一致,如果不一致,這個master便知道自己輸掉仲裁,然後停止向SDA寫數據。也就是說,如果master一直檢查到總線上數據和自己發送的數據一致,則繼續傳輸,這樣在仲裁過程中就保證了贏得仲裁的master不會丟失數據。
輸掉仲裁的master在檢測到自己輸了之後也不再產生時鐘脈衝,並且要在總線空閒時才能重新傳輸。

SPI通信

是一種高速的,全雙工,同步的通信總線。
缺點
沒有指定的流控制,沒有應答機制確認是否接收到數據,所以跟IIC總線協議比較在數據 可靠性上有一定的缺陷。
(1)SDO/MOSI – 主設備數據輸出,從設備數據輸入;
(2)SDI/MISO – 主設備數據輸入,從設備數據輸出;
(3)SCLK – 時鐘信號,由主設備產生;
(4)CS/SS – 從設備使能信號,由主設備控制。當有多個從設備的時候,因爲每個從設
備上都有一個片選引腳接入到主設備機中,當我們的主設備和某個從設備通信時將需
要將從設備對應的片選引腳電平拉低或者是拉高。
在這裏插入圖片描述
這樣傳輸的特點:這樣的傳輸方式有一個優點,與普通的串行通訊不同,普通的串行通訊一次連續傳送至少8位數據,而SPI允許數據一位一位的傳送,甚至允許暫停,因爲SCK時鐘線由主控設備控制,當沒有時鐘跳變時,從設備不採集或傳送數據,也就是說,主設備通過對SCK時鐘線的控制可以完成對通訊的控制。SPI還是一個數據交換協議:因爲SPI的數據輸入和輸出線獨立,所以允許同時完成數據的輸入和輸出。不同的SPI設備的實現方式不盡相同,主要是數據改變和採集的時間不同,在時鐘信號上沿或下沿採集有不同定義,具體請參考相關器件的文檔。
在點對點的通信中,SPI接口不需要進行尋址操作,且爲全雙工通信,顯得簡單高效。在多個從設備的系統中,每個從設備需要獨立的使能信號,硬件上比I2C系統要稍微複雜一些。
最後,SPI接口的一個缺點:沒有指定的流控制,沒有應答機制確認是否接收到數據。
在這裏插入圖片描述
在主設備這邊配置SPI接口時鐘的時候一定要弄清楚從設備的時鐘要求,因爲主設備這邊的時鐘極性和相位都是以從設備爲基準的。因此在時鐘極性的配置上一定要搞清楚從設備是在時鐘的上升沿還是下降沿接收數據,是在時鐘的下降沿還是上升沿輸出數據。但要注意的是,由於主設備的SDO連接從設備的SDI,從設備的SDO連接主設備的SDI,從設備SDI接收的數據是主設備的SDO發送過來的,主設備SDI接收的數據是從設備SDO發送過來的,所以主設備這邊SPI時鐘極性的配置(即SDO的配置)跟從設備的SDI接收數據的極性是相反的,跟從設備SDO發送數據的極性是相同的。下面這段話是Sychip Wlan8100 Module Spec上說的,充分說明了時鐘極性是如何配置的:

The 81xx module will always input data bits at the rising edge of the clock, and the host will always output data bits on the falling edge of the clock.

意思是:主設備在時鐘的下降沿發送數據,從設備在時鐘的上升沿接收數據。因此主設備這邊SPI時鐘極性應該配置爲下降沿有效。

又如,下面這段話是摘自LCD Driver IC SSD1289:

SDI is shifted into 8-bit shift register on every rising edge of SCK in the order of data bit 7, data bit 6 …… data bit 0.

意思是:從設備SSD1289在時鐘的上升沿接收數據,而且是按照從高位到地位的順序接收數據的。因此主設備的SPI時鐘極性同樣應該配置爲下降沿有效。
clk低電平代表空閒狀態

SPI有四種工作模式,取決於兩個參數:
(這兩個參數其實就是控制了CLK這一根線,SPI通信不像UART或IIC那樣有專門的通信週期,有專門的通信起始信號和結束信號。所以SPI協議能夠通過控制時鐘信號線在沒有數據交流的時候保持的狀態,要麼是高電平,要麼是低電平)
1、 CPOL,clock polarity,譯作時鐘極性。
2、 CPHA,clock phase,譯作時鐘相位。
CPOL具體說明:
CPOL用於定義時鐘信號在空閒狀態下處於高電平還是低電平,爲1代表高電平,0爲低電平。
CPHA具體說明:
首先,在同步接口中,肯定存在一個接口時鐘,用來同步採樣接口上數據的。
CPHA就是用來定義數據採樣在第幾個邊沿的,數據的採樣時刻。爲1代表第二個邊沿採樣,爲0代表第一個邊沿採樣。
以上兩個參數,總共有四種組合:
CPOL=0,CPHA=0:此時空閒態時,SCLK處於低電平,數據採樣是在第1個邊沿,也就是
SCLK由低電平到高電平的跳變,所以數據採樣是在上升沿,數據發送是在下降沿。
CPOL=0,CPHA=1:此時空閒態時,SCLK處於低電平,數據發送是在第1個邊沿,也就是
SCLK由低電平到高電平的跳變,所以數據採樣是在下降沿,數據發送是在上升沿。
CPOL=1,CPHA=0:此時空閒態時,SCLK處於高電平,數據採集是在第1個邊沿,也就是
SCLK由高電平到低電平的跳變,所以數據採集是在下降沿,數據發送是在上升沿。
CPOL=1,CPHA=1:此時空閒態時,SCLK處於高電平,數據發送是在第1個邊沿,也就是
SCLK由高電平到低電平的跳變,所以數據採集是在上升沿,數據發送是在下降沿。

MSB最高有效位
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述
進程間通信方式:
有名管道:看見這個名字就能知道個大概了,它於管道的不同的是它有名字了。這就不同與管道只能在具有親緣關係的進程間通信了。它提供了一個路徑名與之關聯,有了自己的傳輸格式。有名管道和管道的不同之處還有一點是,有名管道是個設備文件,存儲在文件系統中,沒有親緣關係的進程也可以訪問,但是它要按照先進先出的原則讀取數據。同樣也是單雙工的。
管道
特點:
它是半雙工的(即數據只能在一個方向上流動),具有固定的讀端和寫端。
它只能用於具有親緣關係的進程之間的通信(也是父子進程或者兄弟進程之間)。
它可以看成是一種特殊的文件,對於它的讀寫也可以使用普通的read、write 等函數。但是它不是普通的文件,並不屬於其他任何文件系統,並且只存在於內存中。
1 #include <unistd.h>
2 int pipe(int fd[2]); // 返回值:若成功返回0,失敗返回-1
當一個管道建立時,它會創建兩個文件描述符:fd[0]爲讀而打開,fd[1]爲寫而打開。如下圖:
在這裏插入圖片描述
要關閉管道只需將這兩個文件描述符關閉即可。
單個進程中的管道幾乎沒有任何用處。所以,通常調用 pipe 的進程接着調用 fork,這樣就創建了父進程與子進程之間的 IPC 通道。如下圖所示:

FIFO
FIFO,也稱爲命名管道,它是一種文件類型。
1、特點
FIFO可以在無關的進程之間交換數據,與無名管道不同。
FIFO有路徑名與之相關聯,它以一種特殊設備文件形式存在於文件系統中。
1 #include <sys/stat.h>
3 int mkfifo(const char *pathname, mode_t mode);
其中的 mode 參數與open函數中的 mode 相同。一旦創建了一個 FIFO,就可以用一般的文件I/O函數操作它。
當 open 一個FIFO時,是否設置非阻塞標誌(O_NONBLOCK)的區別:
若沒有指定O_NONBLOCK(默認),只讀 open 要阻塞到某個其他進程爲寫而打開此 FIFO。類似的,只寫 open 要阻塞到某個其他進程爲讀而打開它。
若指定了O_NONBLOCK,則只讀 open 立即返回。而只寫 open 將出錯返回 -1 如果沒有進程已經爲讀而打開該 FIFO,其errno置ENXIO。
FIFO的通信方式類似於在進程中使用文件來傳輸數據,只不過FIFO類型文件同時具有管道的特性。在數據讀出時,FIFO管道中同時清除數據,並且“先進先出”。下面的例子演示了使用 FIFO 進行 IPC 的過程:

消息隊列
消息隊列,是消息的鏈接表,存放在內核中。一個消息隊列由一個標識符(即隊列ID)來標識。
特點
消息隊列是面向記錄的,其中的消息具有特定的格式以及特定的優先級。
消息隊列獨立於發送與接收進程。進程終止時,消息隊列及其內容並不會被刪除。
消息隊列可以實現消息的隨機查詢,消息不一定要以先進先出的次序讀取,也可以按消息的類型讀取。

信號量
信號量(semaphore)與已經介紹過的 IPC 結構不同,它是一個計數器。信號量用於實現進程間的互斥與同步,而不是用於存儲進程間通信數據。
特點
信號量用於進程間同步,若要在進程間傳遞數據需要結合共享內存。
信號量基於操作系統的 PV 操作,程序對信號量的操作都是原子操作。
每次對信號量的 PV 操作不僅限於對信號量值加 1 或減 1,而且可以加減任意正整數。
支持信號量組。
最簡單的信號量是隻能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二值信號量(Binary Semaphore)。而可以取多個正整數的信號量被稱爲通用信號量。
Linux 下的信號量函數都是在通用的信號量數組上進行操作,而不是在一個單一的二值信號量上進行操作。

共享內存
共享內存(Shared Memory),指兩個或多個進程共享一個給定的存儲區。
1、特點
共享內存是最快的一種 IPC,因爲進程是直接對內存進行存取。
因爲多個進程可以同時操作,所以需要進行同步。
信號量+共享內存通常結合在一起使用,信號量用來同步對共享內存的訪問。

套接字( socket ) :
套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信。

信號:信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一箇中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什麼時候到達。信號是進程間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發生了。信號機制經過POSIX實時擴展後,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。信號事件的發生有兩個來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障);軟件來源。信號分爲可靠信號和不可靠信號,實時信號和非實時信號。進程有三種方式響應信號1.忽略信號2.捕捉信號3.執行缺省操作。

自旋鎖:
何謂自旋鎖?它是爲實現保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是爲了解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多隻能有一個保持者,也就說,在任何時刻最多隻能有一個執行單元獲得鎖。但是兩者在調度機制上略有不同。對於互斥鎖,如果資源已經被佔用,資源申請者只能進入睡眠狀態。但是自旋鎖不會引起調用者睡眠,如果自旋鎖已經被別的執行單元保持,調用者就一直循環在那裏看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。

基址尋址方式中,操作數的有效地址是基址寄存器內容加上形式地址(位移量)

512mb內存的機器,將4G的文件數字重新排大小。
bitmap問題

線程可以被殺死嗎
可以,但是不推薦這麼做,會導致鎖無法被解鎖,造成其他線程被死鎖。

進程和線程的關係,區別。
線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),堆是共享的,也就是說,(全局變量,堆,靜態變量是共享的)但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
線程與進程的區別歸納:
線程和進程的區別在於,子進程和父進程有不同的代碼和數據空間,而多個線程則共享數據空間,每個線程有自己的執行堆棧和程序計數器爲其執行上下文。多線程主要是爲了節約CPU時間,發揮利用,根據具體情況而定。線程的運行中需要使用計算機的內存資源和CPU。
a.地址空間和其它資源:進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。
b.通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。
c.調度和切換:線程上下文切換比進程上下文切換要快得多。
d.在多線程OS中,進程不是一個可執行的實體。

Linux進程間的通信方式:管道、有名管道、信號量、消息隊列、共享內存、信號、socket
Windows進程間的通信方式:管道、信號量、消息隊列、共享內存、socket
Linxu線程間的通信方式:互斥量、條件變量、信號量、信號
Windows線程間的通信方式:互斥量、信號量、事件(Event)、臨界區(Critical Section)

linux文件操作
在Linux下使用文件描述符來表示設備文件和普通文件。文件描述符是一個整型的數據,所有對文件的操作都通過文件描述符實現。文件描述符的範圍是0~OPEN_MAX,系統中有3個已經分配的文件描述符,即標準輸入、標準輸出、和標準錯誤,他們的文件描述符的值分別爲0、1、2。

打開文件 頭文件:<fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
open()函數根據用戶設置的標誌flags和模式mode在路徑pathname下建立或者打開一個文件。當函數成功時,返回一個整型的文件描述符,出錯時返回-1.

關閉文件close()函數 頭文件: <unistd.h>
#include<unistd.h>
int close(int fd);
close()函數關閉一個文件描述符,關閉以後此文件描述符不再指向任何文件,從而描述符可以再次使用。當函數執行成功的時候返回0,如果有錯誤發生,返回-1.
如果每次打開文件不關閉,則會將系統的文件描述符耗盡,導致不能再打開文件:

讀取文件read()函數
頭文件: <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read()函數從文件描述符fd對應的文件中讀取count字節,放到buf開始的緩衝區。如果count的值爲0,read()函數返回0,不進行其他操作;讀取成功時,文件對應的讀取位置指針向後移動,移動的大小爲讀取的字節數。
如果read()函數讀取成功,返回讀取的字節數;當返回值爲-1的時候,讀取函數有錯誤發生;如果已經達到文件的末尾,返回0.

寫文件write()函數 頭文件:<unistd.h> 
ssize_t write(int fd, const void *buf, size_t count);
與read()函數的含義類似,write()函數向文件描述符fd寫入數據,數據的大小有count指定,buf爲要寫入數據的指針。寫入成功返回寫入數據的字節數,寫入出錯則返回-1。當操作的對象是普通文件時,寫文件的位置從文件的當前開始,如果在打開文件時指定了O_APPEND項,每次寫操作時,會將寫操作的位置移到文件的結尾處。

文件偏移lseek()函數 頭文件:<unistd.h>
文件偏移量指的是當前文件操作位置相對於文件開始位置的偏移。當打開一個文件時,如果沒有指定O_APPEND參數,文件的偏移量爲0。如果指定了O_APPEND選項,文件的偏移量與文件的長度相等,即文件的當前操作位置移到了末尾。
off_t lseek( int fildes, off_t offset, int whence)
函數對文件描述符fildes所代表的文件,按照操作模式whence和偏移量的大小off_t,重新設定文件偏移量。如果lseek()函數操作成功,則返回新的文件偏移量的值;如果失敗返回-1。由於文件的偏移量可以爲負值,判斷lseek()是否操作成功時,不要使用小於0的判斷,要使用是否等於-1來判斷。參數offet和whence搭配使用,具體含義如下:
  whence值爲SEEK_SET時,offset爲相對文件開始處的值;
  whence值爲SEEK_CUR時,offset爲相對當前位置的值;
  whence值爲SEEK_END時,offset爲相對文件結尾的值;
 
建立內存映射函數mmap()
mmap()函數將普通文件映射到內存中,普通文件被映射到進程地址空間後,進程可以像訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。 mmap()系統調用使得進程之間通過映射同一個普通文件實現共享內存。
mmap()映射後,讓用戶程序直接訪問設備內存,相比較在用戶控件和內核空間互相拷貝數據,效率更高。在要求高性能的應用中比較常用。mmap映射內存必須是頁面大小的整數倍,面向流的設備不能進行mmap,mmap的實現和硬件有關。

設備文件輸入輸出控制ioctl()函數
ioctl()函數通過對文件描述符的發送命令來控制設備。
頭文件:<sys/ioctl.h>
int ioctl(int d, int rquest, …);
ioctl()函數通過對文件描述符發送特定的命令來控制文件描述符所代表的設備。參數d時一個已經打開的設備。通常情況下ioctl()函數出錯會返回-1,成功返回0或者大於1的值,取決於對應設備的驅動程序對命令的處理。
使用ioctl()像其他的系統調用一樣:打開文件,發送命令,查詢結果。ioctl()函數像一個雜貨鋪,對設備的控制通常都通過這個函數來執行。具體對設備的操作方式取決於設備驅動程序的編寫。

SMP的全稱是"對稱多處理"(Symmetrical Multi-Processing)技術,是指在一個計算機上彙集了一組處理器(多CPU),各CPU之間共享內存子系統以及總線結構。

linux下五種IO模型:
阻塞IO模型
非阻塞IO模型
IO複用模型
信號驅動IO
異步IO模型

異步IO
異步IO的概念和同步IO相對。當一個異步過程調用發出後,調用者不能立刻得到結果。實際處理這個調用的部件在完成後,通過狀態、通知和回調來通知調用者。在一個CPU密集型的應用中,有一些需要處理的數據可能放在磁盤上。預先知道這些數 據的位置,所以預先發起異步IO讀請求。等到真正需要用到這些數據的時候,再等待異步IO完成。使用了異步IO,在發起IO請求到實際使用數據這段時間 內,程序還可以繼續做其他事情。
同步IO

  1. 同步,就是我調用一個功能,該功能沒有結束前,我死等結果。
  2. 異步,就是我調用一個功能,不需要知道該功能結果,該功能有結果後通知我(回調通知)
  3. 阻塞, 就是調用我(函數),我(函數)沒有接收完數據或者沒有得到結果之前,我不會返回。
  4. 非阻塞, 就是調用我(函數),我(函數)立即返回,通過select通知調用者

同步IO和異步IO的區別就在於:數據拷貝的時候進程是否阻塞!
阻塞IO和非阻塞IO的區別就在於:應用程序的調用是否立即返回!

內存溢出:(Out Of Memory—OOM)
系統已經不能再分配出你所需要的空間,比如你需要100M的空間,系統只剩90M了,這就叫內存溢出
內存泄漏: (Memory Leak)----》強引用所指向的對象不會被回收,可能導致內存泄漏,虛擬機寧願拋出OOM也不會去回收他指向的對象
意思就是你用資源的時候爲他開闢了一段空間,當你用完時忘記釋放資源了,這時內存還被佔用着,一次沒關係,但是內存泄漏次數多了就會導致內存溢出

內存分配方式有三種:
(1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
(2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
(3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。

可重入內核 & 可重入函數
可重入內核在ULK(深入理解linux內核)中的定義是指若干個進程可以同時在內核態下執行,也就是說多個進程可以在內核態下併發執行內核代碼。在單處理器上,只能實現
微觀上的串行,宏觀上的並行,即任意時刻,只有一個進程真正執行,其他進程處於阻塞或者等待狀態。這裏的可重入,是指可以多個進程進入內核,並不是重複/重新進入內核
對於linux來說,可重入內核代碼包含可重入函數和非可重入函數。
可重入函數是指運行時只改變局部數據結構,不改變全局數據結構;
不可重入函數是指運行該函數時也需要改變全局數據結構。
如果有多個進程進入不可重入函數時,需要相應的鎖機制(互斥鎖,自旋鎖)來保證同一時刻只有一個進程改變涉及到的全局數據。
可重入函數的理解其實比較麻煩,可以從以下闡述:
1.可重入是與多線程無關的,一個函數被同一個線程調用2次以上,得到的結果具有可再現性。則這個函數是可重入的。
2.可重入講究的是結果可再現性,因此,使用全局(靜態)變量的函數,再次調用其函數的結果是不可再現的,這就是前面說的爲何要求該函數只修改局部變量
故可重入函數,描述的是函數被多次調用但是結果具有可再現性
可重入函數條件:
1,不在函數內部使用靜態或者全局數據
2,不返回靜態或者全局數據,所有的數據都由函數調用者提供
3,使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據
4, 如果必須訪問全局數據,使用互斥鎖(自旋鎖)來保護
5,不調用不可重入函數
6,可重入函數必須是線程安全的
補充一點,可重入函數必定是線程安全的,但是線程安全的,不一定是可重入的。不可重入函數,函數調用結果不具有可再現性,可以通過互斥鎖等機制,使之能安全的同時被多個線程調用,那麼,這個不可重入函數就是轉換成了線程安全。
可重入函數主要用於多任務環境中,一個可重入的函數簡單來說就是可以被中斷的函數,也就是說,可以在這個函數執行的任何時刻中斷它,轉入OS調度下去執行另外一段代碼,而返回控制時不會出現什麼錯誤;而不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等,所以它如果被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。
可重入函數也可以這樣理解,重入即表示重複進入,首先它意味着這個函數可以被中斷,其次意味着它除了使用自己棧上的變量以外不依賴於任何環境(包括 static),這樣的函數就是purecode(純代碼)可重入,可以允許有該函數的多個副本在運行,由於它們使用的是分離的棧,所以不會互相干擾。

死鎖的一些結論:
參與死鎖的進程數至少爲兩個
參與死鎖的所有進程均等待資源
參與死鎖的進程至少有兩個已經佔有資源
死鎖進程是系統中當前進程集合的一個子集
死鎖會浪費大量系統資源,甚至導致系統崩潰。

死鎖產生的原因
競爭不可搶佔資源引起死鎖
通常系統中擁有的不可搶佔資源,其數量不足以滿足多個進程運行的需要,使得進程在運行過程中,會因爭奪資源而陷入僵局,如磁帶機、打印機等。只有對不可搶佔資源的競爭 纔可能產生死鎖,對可搶佔資源的競爭是不會引起死鎖的。
競爭可消耗資源引起死鎖
進程推進順序不當引起死鎖
進程在運行過程中,請求和釋放資源的順序不當,也同樣會導致死鎖。例如,併發進程 P1、P2分別保持了資源R1、R2,而進程P1申請資源R2,進程P2申請資源R1時,兩者都會因爲所需資源被佔用而阻塞。
信號量使用不當也會造成死鎖。進程間彼此相互等待對方發來的消息,結果也會使得這 些進程間無法繼續向前推進。例如,進程A等待進程B發的消息,進程B又在等待進程A 發的消息,可以看出進程A和B不是因爲競爭同一資源,而是在等待對方的資源導致死鎖。

產生死鎖的四個必要條件:
5.1 互斥條件:
5.2 不可剝奪條件:
5.3 請求與保持條件:
5.4 循環等待條件:

處理死鎖的方法:
預防死鎖:通過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或幾個條件,來防止死鎖的發生。
避免死鎖:在資源的動態分配過程中,用某種方法去防止系統進入不安全狀態,從而避免死鎖的發生。
檢測死鎖:允許系統在運行過程中發生死鎖,但可設置檢測機構及時檢測死鎖的發生,並採取適當措施加以清除。
解除死鎖:當檢測出死鎖後,便採取適當措施將進程從死鎖狀態中解脫出來。

I/O交通管制程序:記錄設備,控制器以及通道的狀態,從而達到獨立I/O調度。

I/O接口與CPU之間的I/O總線有數據線,控制線和地址線。數據線和地址線都是單向傳輸的,從CPU傳送給I/O接口。

當CPU發現所請求的內存地址中沒有指令,就會發出缺頁中斷。

ARM寄存器簡介:
R0‐R7 也被稱爲低組寄存器。所有指令都能訪問它們。它們的字長全是 32 位,復位後
的初始值是不可預料的。
R8‐R12 也被稱爲高組寄存器。這是因爲只有很少的 16 位 Thumb 指令能訪問它們, 32位的指令則不受限制。它們也是 32 位字長,且復位後的初始值是不可預料的 。
在這裏插入圖片描述
 主堆棧指針(MSP),或寫作 SP_main。這是缺省的堆棧指針,它由 OS 內核、異常服務例程 以及所有需要特權訪問的應用程序代碼來使用。  進程堆棧指針(PSP),或寫作 SP_process。用於常規的應用程序代碼(不處於異常服用例 程中時)。

CPSR 狀態寄存器就是
在這裏插入圖片描述
CPSR的低8位(包括I、F、T和M[4:0])稱爲控制位,程序無法修改,除非CPU運行於特權模式下,程序才能修改控制位!
N、Z、C、V均爲條件碼標誌位。它們的內容可被算術或邏輯運算的結果所改變,並且可以決定某條指令是否被執行!意義重大!
它包含了條件標誌位、中斷禁止位、當前處理器模式標誌以及其他的一些控制和狀態位。

PC寄存器的指向問題
如上圖所示,在執行add r0, r1, #5指令時,第二條指令正在譯碼階段,而第三條指令正在取指階段。在執行第一條指令時,PC寄存器應指向第三條指令。也即,當處理器爲三級流水線結構時,PC寄存器總是指向隨後的第三條指令。
當處理器處於ARM狀態時,每條ARM指令爲4個字節,所以PC寄存器的值爲當前指令地址 + 8字節
當處理器處於Thumb狀態時,每條Thumb指令爲2字節,所以PC寄存器的值爲當前指令地址 + 4字節
此外,在ARM9中,採用了五級流水線結構,是在ARM7的三級流水線結構後面添加了兩個新的過程。因此,指令的執行過程和取指過程還是相隔一個譯碼過程,因而PC還是指向當前指令隨後的第三條指令。

SPSR:程序狀態保存寄存器
SPSR用來進行異常處理,有以下功能
1.保存ALU中的當前操作信息。
2.控制允許和禁止中斷。
3.設置處理器的運行模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章