此前討論過一般認為的程式入口main()函式並非實際上程式真正的入口,在進入main()程式設計師設計的運算邏輯之前,執行庫需要完成一系列的準備工作,包括引數入棧、堆初始化以及i/o初始化。在作業系統中,i/o一般是指代硬體外設或磁碟,但從軟體的角度,任何具有輸入輸出操作的都可抽象為「檔案」,一般在系統中都是用控制代碼handle(類似指標)來指代程序中需要開啟的某一具體檔案,而程序中也配備中乙個二維陣列「開啟檔案列表」來管理該程序所依賴的所有「檔案」。最經典的莫過於標準輸入輸出三兄弟(標準輸入stdin、標準輸出stdout、標準錯誤輸出stderr),任何程式在執行之前必須首先完成這三兄弟的初始化。
以下將通過linux系統中ioinit.c中的i/o初始化程式的偽**進行檔案控制代碼管理初始化的分析。
在crt/src/ioinit.c中有乙個陣列
int _nhandle;
ioinfo * _pioinfo[64]; //等效於ioinfo _pioinfo[64][32]
/*這個二維陣列便是程序使用者態的開啟檔案表,所以程序可容納的控制代碼是64*32 = 2048,
驗證了控制代碼資源的有限性,而n_handle則記錄了該錶的實際元素個數。只所以使用而不是
二維陣列的原因是使用指標陣列更加節省空間,而如果使用二維陣列,則不論程式裡開啟了
幾個檔案都必須始終消耗2048個ioinfo的空間。*/
/*file結構中_file值和「開啟檔案列表」兩維陣列_pioinfo的下標直接關聯。
在windows中_file的5到10位用來表示兩維陣列的第一維下標,0到4位表示第二維下標。
_ioinit()函式初始化了__pioinfo陣列的第乙個二級陣列:
*/if( (pio = _malloc_crt( 32 * sizeof(ioinfo)) ) == null )
__pioinfo[0] = pio; //給開啟列表二維陣列的第乙個子陣列分配空間
_nhandle = 32;
for(; pio < __pioinfo[0]+32; pio++)
/*接下來_ioinit的工作是講一些預定義的開啟檔案給初始化,這包括兩部分:
1.從父程序繼承的開啟檔案控制代碼,檔乙個程序呼叫api建立新程序的時候,
可以選擇繼承自己的開啟檔案控制代碼,如果繼承,子程序可以直接使用父程序的開啟檔案控制代碼;
*/void getstartupinfo(startupinfo* lpstartupinfo);
/*關於cbreserved2和lpreserved2這兩個欄位的用途沒有正式文件說明,
但這兩個字段便是用來傳輸開啟檔案控制代碼的,當這兩個欄位都不為0時,
則意味著當前程序從父程序中繼承了一些開啟檔案控制代碼。
lpserverd2是乙個指標,指向一塊記憶體,該塊記憶體的結構如下:
位元組[0,3]:傳遞控制代碼的數量n
位元組[4,3+n]:每乙個控制代碼的屬性(各1byte,對應ioinfo結構的_osfile欄位)
位元組[4+n:]:每乙個控制代碼的值(對應n個intptr_t型別資料,同ioinfo結構的_osfhnd欄位)
*/typedef
struct _startupinfo
startupinfo
//_ioinit函式使用如下**獲得該記憶體區域的資料:
cfi_len = *(__unaligned int *) (startupinfo.lpreserved2);//__unaligned關鍵字告訴編譯器該指
//針可能指向乙個沒有進行資料對齊的位址,編譯器會插入一些**來避免發生資料未對齊而產生的錯誤。
posfile = (char *)(startupinfo.lpreserved2) + sizeof(int);//使posfile指向繼承控制代碼的_osfile欄位陣列
posfhnd = (__unaligned intptr_t *)(posfile + cfi_len);//使posfhnd指向繼承控制代碼的_osfhnd欄位陣列
cfi_len = __min( cfi_len, 32*64 );
for( i=1; _nhandleif( (pio=_malloc_crt(32*sizeof(ioinfo))) == null)
__pioinfo[i] = pio;
_nhandle += 32;//_nhandle總是等於當前二維陣列中已經分配空間的元素數量
for(; pio< __pioinfo[i] + 32; pio++)}/*
這個迴圈中,fh從0遞增,每次通過_pioinfo巨集轉換為開啟檔案列表中連續的對應元素,而posfile和postfhnd
則依次遞增以遍歷從父程序繼承來的控制代碼集合的每乙個資料。複製過程中一些不符合條件的控制代碼會被過濾
*/for(fh=0; fhif( (*posfhnd != (intptr_t)invalid_handle_value) &&
(*posfile & fopen) &&
((*posfile & fpipe) || ( getfiletype( (handle)*posfhnd ) != file_type_unknown ) ) )
//遍歷賦值,若當前的控制代碼存在並且檔案屬性為開啟,且檔案型別不為unknown或檔案為管道檔案,
//則可以進行賦值填充進子程序的乙個ioinfo資料結構中
}/*2.作業系統提供的stdin, stdout, stderr三個標準「檔案」的控制代碼。
每個程序只有實現了這三個標準輸入輸出檔案控制代碼,才能使用標準輸入輸出。
初始化stdin\stdout\stderr三個標準檔案的控制代碼結構,有可能從父程序中繼承來了,故而需要判斷
*/for(fh=0; fh<3; fh++)
else
pio->osfile |= fdev;
}else
pio->osfile |= ftext;
}/*如果0,1,2號控制代碼(stdin\stdout\stderr)無效(沒有從父程序中繼承),
那麼_ioinit會使用getstdhandle函式獲取預設的標準輸入輸出控制代碼。*/
//至此所有的i/o初始化完成了,所有的i/o函式可以自由使用了。
初始化 MyBatis初始化之載入初始化
在mybatis初始化過程中,大致會有以下幾個步驟 1.建立configuration全域性配置物件,會往typealiasregistry別名註冊中心新增mybatis需要用到的相關類,並設定預設的語言驅動類為xmllanguagedriver 3.構建defaultsqlsessionfacto...
編譯期初始化與執行期初始化
編譯期初始化 在源 被編譯過程中,編譯期安插一些 邏輯,完成確定的記憶體分配 並非實際分配記憶體,而是確定其大小占用,由編譯期安插 變數的初始化。如 全域性變數為內建型別,並且大小確定 int a 2 static int b 3 static的不同只是其只在本檔案中可見 static int c ...
C new是否執行初始化
本文 c 在new時的初始化的規律可能為 對於有建構函式的類,不論有沒有括號,都用建構函式進行初始化 如果沒有建構函式,則不加括號的new只分配記憶體空間,不進行記憶體的初始化,而加了括號的new會在分配記憶體的同時初始化為0。以下 include using namespace std int m...