| model | 函數參數及自動變數 | 預設全體變數 | 預設常數 | 預設指標佔用空間 |
|---|---|---|---|---|
| small | data | data | data | 3 Bytes |
| compact | pdata | pdata | pdata | 3 Bytes |
| large | xdata | xdata | xdata | 3 Bytes |
Keil C51 和標準 C 首要的差別
別的要注意不要把 reentrant 和 thread-safe 搞混了. 二者狀態近似翻譯社 但講的是分歧的概念. 請參考: 可重入與履行緒安全 (reentrant vs thread-safe) Part1
Keil 的 C51 是以 ANSI C90 為其設計根本翻譯社 即便是如斯翻譯社 它和標準 C 語言 (ANSI C90) 之間還是有幾個滿大的差別:
我們由下表可以看出三種記憶體模型的函數參數及主動變數和預設全部變數都被放在溝通的記憶體區間, 並且除 SMALL MODEL 是仍然是放在內部記憶體 (data) 以外, COMPACT MODEL 及 LARGE MODEL 都是放在外部記憶體 (pdata翻譯社 xdata).
為什麼 Keil C51 預設是 non-reentrant
- Keil C51 因應 8051 的特征多了 bit, sbit, sfr翻譯社 sfr16 等四種資料型態. 一般天成翻譯公司們只會用到 bit, 其他三個是界說 CPU 的特別功能暫存器用的.
- Keil C51 在界說變數時多了儲存空間潤飾字, 用來點竄變數利用記憶體空間. 具體可參考這一篇
- 多了界說中止辦事常式 ISR 的方式及進入 ISR 時切換暫存器區段 (register bank) 的語法, 例如: static void UART0_ISR(void) interrupt 4 using 2
- 標準函數庫沒有依標準來實作, 如: printf()
- 在 Keil C51 中, 函數預設是 non-reentrant, 這點是最嚴重的差別. 天成翻譯公司們在利用時要有所警醒才好翻譯社 才不致於掉到圈套裡而不自知.
;============================================================== ; Reentrant Stack Initilization ; The following EQU statements define the stack pointer for ; reentrant functions and initialized it: ; IBPSTACK EQU 0 ; set to 1 if SMALL model is used. IBPSTACKTOP EQU 0FFH+1 ; set to highest location+1. ; XBPSTACK EQU 0 ; set to 1 if LARGE model is used. XBPSTACKTOP EQU 0FFFFH+1 ; set to highest location+1. ; PBPSTACK EQU 0 ; set to 1 if COMPACT model is used. PBPSTACKTOP EQU 0FFFFH+1 ; set to highest location+1. ;==============================================================
詳細資料可以參考 Keil C51 Application Notes 129: Function Pointers in C51
回到主題, 我們知道 8051 的 SP (Stack Pointer register) 只有 8 Bits 巨細 (只能記錄 256 個地址, 意思是: stack 區最大只能是 256 bytes). 而實際上 8051 CPU 在不擴充外部記憶體的情形下, 內部只有 128 byte 記憶體可用. 如果再扣除 Register Bank0 ~ Bank3 (32Bytes)翻譯社 還有 Bit Addressable Area (16Bytes)翻譯社 就只剩 80 Bytes 可用 (128-32-16=80). 而 8052 CPU 則略微好一點, 又多了 128 Bytes 可用, 但是 208 (80+128) Bytes 幾乎已是上限了.
- 不能持有靜態 (或全域) 極度數資料.
- 不可修改本身的程式碼 (會影響本身下一次呼喚時的行為)
- 不可呼喚弗成重入 non-reentrant 的函數
修正 Keil C51 釀成的 non-reentrant 問題
- 將靜態 (或全域) 變數, 改為使用區域變數.
- 需要將暫存的資料回傳給呼喚者時翻譯社
- 應改成由呼喚者供應回傳所需的暫存區 (所以函數的原型介面會有所更動, 可以對照標準函數庫 strtok(), strtok_r() 的差別).
- 假如呼喚者用 malloc() 動態取得暫存區, 也要記得 free().
- 若是函數的原型介面弗成以被更動, 也能夠將 malloc() 改由函數本身來呼喚翻譯社 但如許子程式會變得不容易維護. 首要是因為呼喚 malloc() 和 free() 在分歧一個函數內, 容易出錯而造成 memory leak, 故非不得已不建議採用.
- 真的有難題時 (只有 library, 沒有 source code)翻譯社 請改用雷同 thread-safe wrapper 的方法來避免産生 reentrancy: 重寫另外一個函數來庖代它, 內容為禁止間斷 (是中止不是 mutex); 呼喚該函數; 取出回傳資料; 復原間斷的本來設定. (要注意:是復原中止的原先設定而不是啟用間斷; 可能會有其他負感化)
reentrant function 一般具有以下前提:
什麼是 reentrant
若何將 non-reentrant function 改為 reentrant
要注意的是 Keil C51 函數預設是 non-reentrant 真實的原因是 "函數參數及區域變數都被安置在固定位置,而不再是動態使用堆疊區了".
void myFunc (long arg1, long arg2, long arg3) large reentrant
{
}
不過一旦用了方式一, 另外還需要在 STARTUP.A51 中針對用到的軟體堆疊區進行計劃
至於如何把本來是 non-reentrant 的函數改為 reentrant 函數呢? 偏向以下:
在此先來了解一下 reentrant 的界說, Wiki 網站上給 reentrancy 下的界說是
- 加上 keyword reentrant 把共用的函數改回 reentrant, 參看上面的例子.
- 不要有共用函數 (直接在名稱上區別用處)
對於運用 8051 CPU 的專案來講, 需要用到以 C 語言作為開辟對象的, 一般都是中型以上的專案了. 也許對於如許的專案來講翻譯社 208 Bytes 即要當全域變數區, 又要當堆疊區, 函數的參數傳遞區以及區域變數區真的太難了翻譯社 所以一般都會有擴充的外部記憶體可用. 正因為如斯, Keil C51 就把這 208 Bytes 儘量留給堆疊區 (這樣可以放廣大型專案的副程式呼叫深度). 然後把全域變數翻譯社 參數傳遞區和區域變數等等放到另一個區域去. 可是對 C 說話來講, 區域變數和部份參數的傳遞也都需要依托堆疊區, 所以 Keil C51 提出二種方法來因應:
意思是 reentrant function 可以在履行傍邊被中斷翻譯社 而且在前一次被挪用 (invocation) 執行竣事之前, 可以平安的再被呼喚. 我想這裡所謂的平安的再被呼叫應當是指產出的履行效果和沒有被中斷完全一致而且也不會附帶產生任何不預期的結果. Wiki 還說這個問題原始是起因於在單一履行緒的程式環境中 (意即:沒有利用 OS; 或者不是多工環境; 或只有單一個履行緒和這個 ISR 相幹), 程式的執行可能隨時被硬體間斷 (hardware interrupt) 打斷, 然後跳到中止辦事程式 (ISR) 履行. 是以 ISR 當中使用的任何副程式只要主程式中也有利用話都會激發 reentrant 問題. (換句話說, 任何 ISR 和主程式共用的副程式都會引發 reentrant 問題)
一般常見的方式有下列二種:
void myFunc (long arg1, long arg2翻譯社 long arg3) reentrant { }
還可以指定是哪種記憶體模型 (samll, compact翻譯社 large)
此中第一項是因為這二種資料經由編譯及保持之後都會佔用在 "固定位址" 上 (相對的主動變數應當要利用堆疊區, 不見得每次執行時都利用同樣的位址), 一旦發生 "間斷-再進入"翻譯社 這些資料將無法被包管和中斷前一致. 是以這也意味著, 不克不及用靜態 (或全域) 變數回傳資料給呼喚者.
- 方法一: 實作一個軟體堆疊區來因應參數傳遞及區域變數的需求. 但究竟結果軟體堆疊沒法像硬體堆疊的操作一樣快速簡練, 如果每一個函數都利用軟體堆疊就會拖慢系統的履行速度.
- 方式二: 把沒法用暫存器傳遞的函數參數和區域變數都配置在固定位置上翻譯社 並且履行呼喚樹剖析 (call tree analysis翻譯社 這個是 Keil C51 特有的) 來決議可重疊區域的大小, 用以下降記憶體的需求. 所以優點是速度較快, 錯誤謬誤是記憶體需求大翻譯社 而負作用就是函數釀成 non-reentrant.
In computing, a computer program or subroutine is called reentrant if it can be interrupted in the middle of its execution and then safely called again ("re-entered") before its previous invocations complete execution.
接著我們來看一下該若何解決本來 reentrant 函數被 Keil C51 變成 non-reentrant 的問題呢?
Keil C51 把方式二是看成預設值, 並且允許混用方式一. 需要利用方式一時, 只需在函數定義後面加上 keyword reentrant, 例如:
本文出自: http://magicjackting.pixnet.net/blog/post/105099235-c-%e8%aa%9e%e8%a8%80%3akeil-c51-%e5%92%8c%e6%a8%有關各國語文翻譯公證的問題歡迎諮詢天成翻譯公司02-77260931
請先 登入 以發表留言。