運行庫


運行庫

入口函數和程序初始化

  • 程序並非從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

本章小結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM