運行庫
入口函數和程序初始化
-
程序並非從main函數開始,首先運行的代碼是入口函數,負責准備好main函數執行所需要的環境,並且負責調用main函數。
-
GLIBC入口函數和MSVC CRT入口函數的實現略
-
I/O指代任何操作系統理解為文件的事務。在Linux里有文件描述符(File Descriptor),在Windows里由句柄(Handle)。句柄可以防止用戶隨意讀寫操作系統內核的文件對象。
-
IO初始化函數需要在用戶空間中建立stdin、stdout、stderr機器對應的FILE結構,使得程序進入main之后可以直接使用printf、scanf等函數。
-
堆初始化
C/C++運行庫
-
C語言運行庫的組成部分大部分是C語言標准庫:
-
標准輸入輸出(stdio.h)
-
文件操作(stdio.h)
-
字符操作(ctype.h)
-
字符串操作(string.h)
-
數學函數(math.h)
-
資源管理(stdlib.h)
-
格式轉換(stdlib.h)
-
時間/日期(time.h)
-
斷言(assert.h)
-
各種類型上的常數(limits.h & float.h)
-
變長參數(stdarg.h)
-
非局部跳轉(setjmp.h)
-
運行庫與多線程
-
CRT的多線程困擾
-
線程的訪問權限
可以訪問進程內存里的所有數據,甚至包括其他線程的堆棧,但實際運用中線程也擁有自己的私有存儲空間。
-
棧:盡管並非完全無法被其他線程訪問,但一般情況下仍然可以認為是私有的數據
-
線程局部存儲(Thread Local Storage):某些操作系統為線程單獨提供的私有空間,但容量很有限
-
寄存器:存放的數據是執行流的基本數據,為線程私有。
-
-
多線程運行庫
-
對於C/C++標准庫來說,線程相關的部分是不屬於標准庫的內容的,跟網絡、圖形圖像一樣,屬於標准庫之外的系統相關庫。主流的CRT在設計時都會考慮:
-
提供那些多線程操作的接口
-
本身要能夠在多線程的環境下正確運行
-
-
CRT不支持多線程的問題
-
errno:錯誤信息被其他線程覆蓋
-
strtok:不同線程調用這個函數會把它內部的局部靜態變量弄混亂
-
malloc/new與free/delete:堆分配/釋放函數或關鍵字在不加鎖的情況下是線程不安全的。
-
異常處理:不同的線程拋出的異常會彼此沖突,從而造成信息丟失的情況。
-
printf/fprintf及其他IO函數:流輸出函數同樣是線程不安全的,因為它們共享了同一個控制台或文件輸出。
-
其他線程不安全函數:包括與信號相關的一些函數
-
-
-
-
CRT改進
-
使用TLS
- errno必須稱為各個線程的私有成員。在glibc中,errno被定義為一個宏。函數_errno_location在單線程庫版本中直接返回全局變量errno的地址;在多線程版本中,不同線程調用該函數返回的地址不同。
-
加鎖:在多線程版本的運行庫中,線程不安全的函數內部都會自動地進行加鎖。
-
改進函數調用方式
-
修改所有的線程不安全的函數的參數列表,改成某種線程安全的版本。比如,strtok_s()。
-
最好的做法是不改變任何標准庫函數的圓形,只是對標准庫的實現進行一些改進,使得它能夠在多線程環境下運行,做到向后兼容。
-
-
-
線程局部存儲(Thread Local Storage)實現
如果要定義一個全局變量為LTS類型,值需要在定義前加上相應的關鍵字即可。對於GCC,這個關鍵字是__thread;對於MSVC,這個關鍵字是__declspec(thread)
-
Windows TLS的實現
-
線程私有變量會放在PE文件的 .tls段。當線程啟動時,會從堆中分配一塊足夠的內存,然后把tls段的內容復制過來,因此每個線程都有自己獨立的副本。
-
tls表中保存了所有TLS變量的構造函數和析構函數的地址。TLS表本身位於PE文件的.rdata段
-
每個線程都有一個線程環境塊(TEB),保存線程的堆棧地址、線程ID等,其中有一個域是一個TLS數組,可以訪問到TLS變量。
-
-
顯式LTS
-
在Windows平台下,系統提供了TlsAlloc、TlsGetValue、TlsSetValue、TlsFree這個4個API
-
Linux對應的API是pthread_key_create、pthread_getspecific、pthread_setspecific、pthread_key_delete。
-
相對於隱式的TLS變量,顯式的TLS變量的使用十分麻煩,而且有諸多限制。並不推薦使用
-
-
C++全局構造和析構
不想看,太枯燥了
fread實現
-
緩沖
-
行緩沖
-
全緩沖
-
-
文本換行
-
Windows:\r\n 存儲方式:0x0D(用CR表示)、0x0A(用LF表示)這兩個字節
-
Linux/Unix:\n
-
Mac OS:\r
-
-
調用軌跡:
-
fread
-
->fread_s:增加緩沖移除保護,加鎖
-
->_fread_nolock_s:循環讀取、緩沖
-
->_read:換行符轉換
-
->ReadFile:Windows文件讀取API
-