首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Hello World 背後的故事(二):從 CreateProcess 到 mainCRTStartup 的進程誕生之旅

     編輯:關於C語言

承接上文:進程有了,代碼還沒跑

上篇確立了連接器將 mainCRTStartup 寫入 PE 入口點。但操作系統如何將這個地址變為執行中的指令指針(RIP/EIP)?本文追蹤 **Windows 內核創建進程 → 主執行緒啟動 → CRT 入口** 的完整鏈路。

1. CreateProcess:進程對象誕生

  • 用戶調用 CreateProcess(或 Shell 執行)
  • 內核:分配 EPROCESS、建立位址空間、映射 PE 映像(映射區段)、初始化 PEB(進程環境塊)、載入 ntdll.dll/kernel32.dll、建立預設堆
  • **關鍵**:此時**尚無執行緒**,代碼**完全未執行**。進程處於「靜止」狀態。

2. 執行緒誕生:NtCreateThreadEx → RtlUserThreadStart

  • 系統為新進程創建**首個執行緒**(主執行緒)
  • 底層系統調用:NtCreateThreadEx(未公開,ntdll 導出)
  • 內核初始化 TEB(執行緒環境塊)、內核棧、用戶棧、上下文
  • 將執行緒初始 EIP 硬性設置為 RtlUserThreadStart (ntdll.dll) —— 這是**所有用戶態執行緒的統一入口**

3. RtlUserThreadStart:SEH 鏈建立與參數遞送

  • 職責:建立**結構化異常處理(SEH)鏈頭**
  • 兩個參數(由內核根據創建參數硬寫入上下文):
    1. UserThreadStartAddress:用戶指定的執行緒函數(主執行緒為 PE 入口點 mainCRTStartup)
    2. UserParameter:傳給執行緒函數的參數(主執行緒通常為 NULL)
  • 調用 BaseThreadInitThunk (kernel32.dll)

4. BaseThreadInitThunk:最後一跳

  • 極簡存根:將參數壓棧/寄存器,CALL UserThreadStartAddress
  • 對於主執行緒:CALL mainCRTStartup —— **用戶代碼正式開始執行!**

5. 完整調用棧回顧

內核創建進程
  └─ 內核創建主執行緒,EIP = RtlUserThreadStart
        └─ RtlUserThreadStart (ntdll)
              ├─ 建立 SEH 框架
              └─ BaseThreadInitThunk (kernel32)
                    └─ mainCRTStartup (EXE 入口,CRT 初始化)
                          ├─ _initterm(__xi_a, __xi_z)   // C++ 全局構造
                          ├─ _initterm_e(__xc_a, __xc_z) // C++ 全局構造 (異常安全)
                          ├─ 初始化 stdio、堆、區域設置、argv/argc/environ
                          └─ main(argc, argv, envp)      // 用戶主函數
                                └─ ... 用戶代碼 ...
                          ├─ exit(main_ret) / _cexit()   // 全局析構、I/O 刷新
                          └─ ExitProcess()               // 進程終止

6. 關鍵洞見

  1. 進程 ≠ 執行緒:進程是資源容器,執行緒是執行實體。CreateProcess 只建容器,首個執行緒由系統自動創建並指向 CRT 入口。
  2. RtlUserThreadStart 是萬能執行緒啟動器:無論 CreateThread、_beginthreadex、線程池、COM STA,最終都經過它建立 SEH 並跳轉用戶函數。
  3. PE 入口點 = 主執行緒的 UserThreadStartAddress:連接器寫入的 AddressOfEntryPoint 直接成為主執行緒的起始地址。
  4. CRT 初始化 = mainCRTStartup 內部流程:堆、I/O、區域設置、全局對象構造、參數解析,皆在用戶態完成,無系統調用參與。

下期預告

深入 mainCRTStartup 源碼(crt0.c、cinitexe.c 等),逐行拆解:

  • 安全 Cookie 初始化(/GS)
  • 堆創建與 _heap_init
  • I/O 緩衝區 _ioinit
  • 區域設置 _setdefaultprecision、_initterm 全局構造
  • 命令行解析 __getmainargs → argc/argv
  • 調用 main 並處理返回值

讓我們在 CRT 內部見證「Hello World」如何一步步被準備好。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved