【第14節】windows sdk編程:進程與線程介紹

目錄

一、進程與線程概述

1.1 進程查看

1.2 何為進程

1.3 進程的創建

1.4 進程創建實例

1.5 線程查看

1.6 何為線程

1.7 線程的創建

1.8 線程函數

1.9 線程實例

二、內核對象

2.1 何為內核對象

2.2 內核對象的公共特點

2.3 內核對象句柄

2.4 內核對象的跨進程訪問


一、進程與線程概述

1.1 進程查看

方法1
????????在屏幕底部的任務欄上右擊鼠標,從彈出的快捷菜單中選擇“任務管理器”,隨后任務管理器窗口將會打開,您便可以在其中瀏覽當前運行的進程。

方法2
????????點擊開始菜單,在搜索欄中輸入“cmd”并打開命令提示符,然后在命令提示符中輸入“tasklist”命令來查看當前運行的進程。

方法3
????????在命令提示符中,首先鍵入“wmic”并按下回車,接著輸入“process”來查看進程信息。

1.2 何為進程

????????進程是指一個正在運行的程序實例。它最早在20世紀60年代由麻省理工學院和IBM公司的CTSS/360系統引入,并成為操作系統結構的基礎。進程是操作系統進行資源分配和調度的基本單位,代表程序在數據集上的執行過程。一個程序可能同時屬于多個進程。

????????進程本身并不執行代碼,而是作為線程的容器。每個進程在創建時都會生成一個主線程來執行代碼。如果主線程結束,系統會銷毀該進程的內核對象。

????????進程可以分為系統進程和用戶進程。系統進程負責完成操作系統的各種功能,相當于運行狀態下的操作系統本身;用戶進程則是由用戶啟動的程序實例。

????????在Windows系統中,進程進一步細分為線程,一個進程可以包含多個獨立運行的線程。與靜止的程序不同,進程是動態的。為了描述多任務操作系統中多道程序并發執行時的資源分配、程序執行的間斷性、通信可能性以及同步互斥等動態特性,引入了進程的概念來參與系統的并發執行。

1.3 進程的創建

CreateProcess()函數可以用來創建一個進程,原型如下所示:

BOOL WINAPI CreateProcess(_In_opt_ LPCTSTR lpApplicationName, //可執行文件名_Inout_opt  LPTSTR lpCommandLine,  //命令行_In_opt_  LPSECURITY_ATTRIBUTES  lpProcessAttributes,_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,_In       BOOL  bInheritHandles,      //句柄繼承是否_In       DWORD  dwCreationFlags,     //創建方式標志_In_opt   LPVOID   lpEnvironment,     //環境字符串塊_In_opt   LPCTSTR lpCurrentDirectory, //新進程的當前目錄_In       LPSTARTUPINFO lpStartupInfo, //進程配置結構體_Out_     LPPROCESS_INFORMATION lpProcessInformation
);


????????此函數執行成功后將創建一個進程內核對象,但需要注意的是,“進程內核對象”與“進程”所指的并非是同一個東西。因此,CreateProcess()函數可以創建成功并不代表進程本身就能正常加載并運行。

更詳細的參數解釋:
- `lpApplicationName`:要執行的可執行文件名稱。
- `lpCommandLine`:傳遞給新進程的命令行字符串,需要注意的是,此參數類型為PTSTR,這意味著CreateProces()在執行過程中可能會修改我們傳入的值。
- `lpProcessAttributes`:進程安全屬性,這些都屬于內核對象。
- `lpThreadAttributes`:線程安全屬性。
- `bInheritHandles`:表示新創建的子進程是否繼承父進程中的所有句柄,是的話子進程就可以訪問父進程中創建的所有句柄。
- `dwCreationFlags`:子進程的創建方式。
- `lpEnvironment`:指向保存有進程環境字符串的內存塊。
- `lpCurrentDirectory`:新創建子進程的當前目錄。
- `lpStartupInfo`:指向子進程創建配置結構體,此結構可以詳細控制子進程的各種創建狀態。
- `lpProcessInformation`:返回進程創建的詳細信息。

1.4 進程創建實例

bool CreateChildProcess(LPWSTR lpPath, BOOL bWait) {// 初始化 STARTUPINFO 結構體,設置其大小為結構體本身的大小STARTUPINFO si = {0};si.cb = sizeof(si);// 初始化 PROCESS_INFORMATION 結構體,用于接收新進程的信息PROCESS_INFORMATION pi = {0};// 嘗試創建子進程if (!CreateProcess(lpPath,   // 可執行文件的路徑NULL,     // 命令行參數(未使用)NULL,     // 進程句柄不可繼承NULL,     // 線程句柄不可繼承FALSE,    // 不繼承句柄0,        // 無特殊標志NULL,     // 使用父進程的環境變量NULL,     // 使用父進程的當前目錄&si,      // 指向 STARTUPINFO 結構體&pi)) {   // 指向 PROCESS_INFORMATION 結構體// 如果創建進程失敗,返回 falsereturn false;}// 如果需要等待子進程執行結束if (bWait) {// 無限等待子進程結束WaitForSingleObject(pi.hProcess, INFINITE);}// 關閉進程句柄CloseHandle(pi.hProcess);// 關閉線程句柄CloseHandle(pi.hThread);// 返回 true 表示成功創建子進程return true;
}

1.5 線程查看

????????使用PCHunter或者火絨劍工具可以查看指定進程的線程及線程的信息。

1.6 何為線程

????????線程是操作系統中的一個內核對象。在Windows操作系統的內核中,并沒有進程的概念,只有線程的概念。進程實際上是從邏輯上對一組線程及其相關資源進行的整合。

????????線程是“進程”中的一個單一順序的控制流,也被稱為輕量級進程(lightweight processes),是程序運行時的調度單位。一個線程可以創建多個子線程,而這些子線程又可以繼續創建更多的線程。

????????從資源占用的角度來看,線程所需的系統資源遠遠少于進程,但它在功能實現上并不遜色。因此,在開發項目時,應優先考慮創建新的線程,而不是啟動一個新的進程。

1.7 線程的創建

創建線程的函數原型如下:

HANDLE WINAPI CreateThread(_In_opt_  LPSECURITY_ATTRIBUTES lpThreadAttributes,_In_      SIZE_T                dwStackSize,  //棧大小,默認1MB_In_       LPTHREAD_START_ROUTINE lpStartAddress, //不能為0_In_opt_  LPVOID                lpParameter,_In_      DWORD                 dwCreationFlags,_Out_opt_ LPDWORD               lpThreadId
);


參數解釋:
- `dwStackSize`:指定線程可以擁有多少棧,屬于線程。
- `lpStartAddress`:線程函數起始地址。
- `lpParameter`:線程函數參數。
- `dwCreationFlags`:線程創建標記。
- `lpThreadId`:新創建線程的ID。

1.8 線程函數

????????像主線程的初始函數為mainCRTStartup/wmainCRTStartup一樣,我們創建線程時也需要指定一個線程函數。為了取得一致性,Windows對線程函數做了限定,其原型如下所示:

DWORD WINAPI ThreadProc(LPVOID lpParam) {return 0;
}

????????就像線程可指定線程函數一樣,主線程同樣可以指定自己的線程函數,在VisualStudio下,可以通過以下編譯選項指定主線程函數:

//指定程序入口函數為MyFun()
#pragma comment(linker,"/entry:\"MyFun\"")

1.9 線程實例

// 線程函數,顯示一個消息框
DWORD WINAPI ThreadProc(LPVOID lpParam) {// 顯示消息框MessageBox(NULL, (LPCWSTR)lpParam, L"CreateThread", MB_OK);return 0; // 線程正常退出
}// 創建子線程的函數
bool CreateChildThread() {DWORD dwThreadId = 0; // 用于保存線程IDHANDLE hThread = CreateThread(NULL,          // 默認安全屬性0,             // 默認堆棧大小ThreadProc,    // 線程函數(LPVOID)L"Hello from the new thread!", // 傳遞給線程的參數0,             // 默認創建標志&dwThreadId);  // 返回線程ID// 檢查線程是否創建成功if (hThread == NULL) {std::cerr << "CreateThread failed: " << GetLastError() << std::endl;return false; // 創建線程失敗,返回false}// 關閉線程句柄,避免資源泄漏CloseHandle(hThread);std::cout << "子線程創建成功,線程ID: " << dwThreadId << std::endl;return true; // 創建線程成功,返回true
}

二、內核對象

2.1 何為內核對象

????????Windows操作系統雖然是用C語言和匯編語言編寫的,但它是一個面向對象的操作系統,系統中廣泛使用了對象的概念。例如,窗口、設備環境、代表DLL的模塊、程序實例、文件、進程、畫筆、畫刷等都是對象。從本質上說,這些對象就是一個個結構體變量。然而,Windows系統不希望程序員直接定義、訪問或修改這些結構體變量,因此它們被完全保護了起來。在使用這些對象時,都有一個共同點:必須先獲取它們的句柄,然后通過調用相應的API來操作這些對象。

????????簡單來說,句柄就像是訪問對象的“鑰匙”,或者用程序員的話來說,句柄是對象的索引。

????????根據對象的不同用途,Windows將對象分為多個類別,主要包括用戶對象(user對象)、圖形設備接口對象(GDI對象)和內核對象。其中,內核對象通常與系統的全局功能相關。

????????常見的Windows內核對象包括:進程、線程、訪問令牌、文件、文件映射、I/O完成端口、郵槽、管道、互斥體、信號量、事件、計時器和線程池等。

2.2 內核對象的公共特點

所有內核對象都遵循統一的使用模式:
(1)第一步:創建對象:創建一個內核對象,一般都是使用CreateXXX的方式,比如:
??? - `CreateProcess`:創建進程。
??? - `CreateThread`:創建線程。
??? - `CreateFile`:創建文件。
??? - `CreateFileMapping`:創建文件映射。
??? - `CreateEvent`:創建事件對象。
??? - `CreateSemaphore`:創建信號量。
(2)第二步:打開對象,得到句柄 (可與第一步合并在一起,表示創建的時后就打開)。
(3)第三步:通過API訪問對象。
(4)第四步:關閉句柄。
(5)第五步:句柄全部關完,對象自動銷毀。

????????所有的內核對象都由操作系統內核管理,并且可以在不同進程之間訪問,這就是所謂的“內核對象是跨進程的”。在許多情況下,我們需要在不同的進程中訪問同一個內核對象,比如實現進程間的同步或共享數據。

????????每個內核對象都有一個引用計數。可以理解為每個內核對象的結構體中都有一個字段用來記錄引用計數。當一個進程創建或打開這個內核對象時,引用計數會增加1;當進程終止或關閉句柄時,引用計數會減少1。當引用計數減到0時,內核對象就會被銷毀。

????????舉個例子,如果內核對象M由進程A創建,之后進程B也使用了這個對象,那么即使進程A退出了,M也不會被銷毀,因為它仍然被進程B使用。只有當沒有任何進程使用這個內核對象時,它才會被自動銷毀。

????????在創建內核對象時,每個對象都會有一個安全屬性。這個安全屬性定義了對象的使用方式,比如是否可讀可寫,以及對象具有的權限等。一旦內核對象以某種安全屬性創建,它就只能在規定的權限范圍內工作。通常,在創建內核對象時,我們需要指定一個`SECURITY_ATTRIBUTES`結構體來設置安全屬性。如果傳入`NULL`,系統會使用默認的安全屬性。

2.3 內核對象句柄

????????在Windows操作系統中,操作對象需要通過句柄來實現,內核對象也不例外。不過,內核對象的句柄是與進程相關的,這意味著同一個內核對象在不同進程中的句柄值是不同的。這一點與GDI對象不同,GDI對象的句柄值是全局有效的,不同進程可以使用相同的句柄值訪問同一個GDI對象。由此可見,不同類型的對象在管理方式上存在差異。

????????每個進程都有一個句柄表,用于記錄該進程打開的所有內核對象。可以簡單地把句柄表理解為一個一維的結構體數組,而句柄值就是這個數組中的索引。因此,內核對象的句柄值僅對當前進程有效。

????????句柄表中的每一項不僅記錄了通過該句柄訪問內核對象的權限,還指明了該句柄是否可以被其子進程繼承。

2.4 內核對象的跨進程訪問

????????要訪問內核對象,進程的句柄表中必須有一個句柄項指向該內核對象。對于創建該內核對象的進程,句柄會直接插入到其句柄表中。而對于其他進程,通常有三種方式可以跨進程訪問內核對象:

(1)父進程繼承給子進程:當父進程創建子進程時,如果指定了句柄可繼承的屬性,子進程會繼承父進程中所有可繼承的句柄。需要注意的是,即使子進程繼承了句柄,它也不知道具體繼承了哪些句柄以及它們的值,這些信息只能由父進程通過進程間通信的方式傳遞給子進程。

(2)通過命名內核對象:在進程A中創建內核對象時,可以為該對象命名。進程B可以通過名稱打開該內核對象。如果內核對象無法命名或沒有唯一標識,則無法使用這種方式。

(3)使用`DuplicateHandle`函數:通過這個函數,可以將一個句柄從一個進程傳遞給另一個進程,從而在目標進程中獲得源進程中內核對象的句柄。

????????通常,獲取內核對象句柄的方式有以下四種:
(1)創建對象:例如,使用`CreateFile`創建新文件時,會同時打開該文件對象并獲得句柄。
(2)繼承句柄:子進程繼承父進程句柄表中的句柄。
(3)顯式打開:例如,使用`OpenFile`、`OpenMutex`、`OpenProcess`等函數顯式打開某個對象。
(4)`DuplicateHandle`:通過該函數間接打開對象并獲得句柄。

????????需要注意的是,句柄的概念可以理解為“具有指定權限和屬性的訪問句柄”。句柄代表對對象的一次打開操作,其權限決定了可以通過該句柄執行的操作。例如,如果打開文件時申請的權限是只讀,那么即使當前用戶擁有寫權限,使用該句柄調用`WriteFile`也會被拒絕訪問。

????????句柄的屬性中,一個重要特性是它是否可以被繼承給子進程。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/73932.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/73932.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/73932.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python簡單爬蟲實踐案例

學習目標 能夠知道Web開發流程 能夠掌握FastAPI實現訪問多個指定網頁 知道通過requests模塊爬取圖片 知道通過requests模塊爬取GDP數據 能夠用pyecharts實現餅圖 能夠知道logging日志的使用 一、基于FastAPI之Web站點開發 1、基于FastAPI搭建Web服務器 # 導入FastAPI模…

uniapp工程中解析markdown文件

在uniapp中如何導入markdown文件&#xff0c;同時在頁面中解析成html&#xff0c;請參考以下配置&#xff1a; 1. 安裝以下3個依賴包 npm install marked highlight.js vite-plugin-markdown 2. 創建vite.config.js配置文件 // vite.config.js import { defineConfig } fro…

sass介紹

1、Sass簡介 Sass 是一種 CSS 的預編譯語言。它提供了 變量&#xff08;variables&#xff09;、嵌套&#xff08;nested rules&#xff09;、 混合&#xff08;mixins&#xff09;、 函數&#xff08;functions&#xff09;等功能&#xff0c;并且完全兼容 CSS 語法。Sass 能…

[JavaScript]如何利用作用域塊避免閉包內存泄漏?

出自《你不知道的JavaScript》上卷 以下是本書給出的反例: function process (data) {...} var bigdata{...} process(bigdata); var btn document.getElementById(x); btn.addEventListener(click, function click{...});click會被回調在其他位置, 在addEventListener函數內…

leetcode hot100(五)

11. 盛最多水的容器 給定一個長度為 n 的整數數組 height 。有 n 條垂線&#xff0c;第 i 條線的兩個端點是 (i, 0) 和 (i, height[i]) 。 找出其中的兩條線&#xff0c;使得它們與 x 軸共同構成的容器可以容納最多的水。 返回容器可以儲存的最大水量。 說明&#xff1a;你…

Unity 云渲染本地部署方案

Unity Render Streaming 云渲染環境搭建 0.安裝 Unity Render Streaming 實現原理: 服務器與客戶端實現功能包括: 詳細內容見官方文檔&#xff1a; 官方文檔: https://docs.unity3d.com/Packages/com.unity.renderstreaming3.1/manual/tutorial.html Unity 流送云渲染介紹: …

洛谷 P3986 斐波那契數列

P3986 斐波那契數列 題目描述 定義一個數列&#xff1a; f ( 0 ) a , f ( 1 ) b , f ( n ) f ( n ? 1 ) f ( n ? 2 ) f(0) a, f(1) b, f(n) f(n - 1) f(n - 2) f(0)a,f(1)b,f(n)f(n?1)f(n?2) 其中 a, b 均為正整數&#xff0c;n ≥ 2。 問有多少種 (a, b)&…

【java面型對象進階】------繼承實例

繼承結構下的標準Javabean 代碼如下&#xff1a; package demo10;//定義員工父類 public class Employee {private String id;private String name;private double salary;//構造方法public Employee(){}public Employee(String id,String name,double salary){this.idid;thi…

Vitis 2024.1 無法正常編譯custom ip的bug(因為Makefile里的wildcard)

現象&#xff1a;如果在vivado中&#xff0c;添加了自己的custom IP&#xff0c;比如AXI4 IP&#xff0c;那么在Vitis&#xff08;2024.1&#xff09;編譯導出的原本的.xsa的時候&#xff0c;會構建build失敗。報錯代碼是&#xff1a; "Compiling blank_test_ip..."…

【圖論】并查集的學習和使用

目錄 并查集是什么&#xff1f; 舉個例子 組成 父親數組&#xff1a; find函數&#xff1a; union函數&#xff1a; 代碼實現&#xff1a; fa[] 初始化code: find code&#xff1a; 遞歸實現: 非遞歸實現: union code : 畫圖模擬&#xff1a; 路徑壓縮&#xff1a…

Java使用FFmpegFrameGrabber進行視頻拆幀,結合Thumbnails壓縮圖片保存到文件夾

引入依賴 <dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.17</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>ja…

mysql與redis的日志策略

MySQL 和 Redis 在日志記錄方面采用了不同的策略&#xff0c;分別對應寫前日志&#xff08;Write-Ahead Logging, WAL&#xff09;和寫后日志&#xff08;Write-After Logging&#xff09;。以下是它們的詳細說明&#xff1a; 1. MySQL&#xff1a;寫前日志&#xff08;Write-A…

nacos安裝,服務注冊,服務發現,遠程調用3個方法

安裝 點版本下載頁面 服務注冊 每個微服務都配置nacos的地址&#xff0c;都要知道 服務發現 2個是知道了解 遠程調用基本實現 遠程調用方法2&#xff0c;負載均衡API測試 遠程調用方法3&#xff0c;注解 負載均衡的遠程調用&#xff0c; 總結 面試題

Ubuntu Qt: no service found for - “org.qt-project.qt.mediaplayer“

1、前言 在一次項目過程中&#xff0c;因項目需求&#xff0c;需要將windows開發的Qt項目遷移到ubuntu系統中&#xff0c;且在某個功能項中需要播放音頻&#xff0c;在windows系統中能夠正常運行&#xff0c;但在ubuntu系統中卻顯示defaultServiceProvider::requestService(): …

Blender制作次表面材質

效果: 主要用沃羅諾伊紋理做出云絮感 然后EV開啟次表面設置

用 pytorch 從零開始創建大語言模型(四):從零開始實現一個用于生成文本的GPT模型

從零開始創建大語言模型&#xff08;Python/pytorch &#xff09;&#xff08;四&#xff09;&#xff1a;從零開始實現一個用于生成文本的GPT模型 4 從零開始實現一個用于生成文本的GPT模型4.1 編寫 L L M LLM LLM架構4.2 使用層歸一化對激活值進行標準化4.3 使用GELU激活函數…

vmware tools灰化

Windows7 32位的某些版本&#xff0c;已經不被vmware支持。下面是解決方法&#xff1a; 安裝kb4474419補丁包&#xff1a;https://www.catalog.update.microsoft.com/Search.aspx?qKB4474419網絡共享。必須要虛擬機和主機可通信。此方法不錯&#xff0c;但是操作起來太麻煩。…

ubuntu高并發內核參數調優 - (壓測客戶端調優)

業務上要求集群提供10w并發&#xff0c;10w并發聽上去不是很難&#xff0c;但10w并發持續1小時呢 在業務上線之前還需要我們自己對業務進行壓測&#xff0c;俗稱benchmark。 壓測的服務器也是需要進行性能調優的&#xff0c;以下列出調優前后的參數對比&#xff0c;更直觀的分析…

html5制作2048游戲開發心得與技術分享

2048游戲開發心得與技術分享 這里寫目錄標題 2048游戲開發心得與技術分享項目概述技術架構1. 核心技術棧2. 項目結構 核心功能實現1. 數據結構設計2. 移動邏輯實現3. 觸摸支持 性能優化1. DOM操作優化2. 事件處理優化 開發心得1. 代碼組織2. 調試技巧3. 用戶體驗優化 項目亮點技…

dify+deepseek聯網搜索:免費開源搜索引擎Searxng使用(讓你的大模型也擁有聯網的功能)

docker安裝SearXng 項目地址:https://github.com/searxng/searxng-docker 第一步 git clone下來 git clone https://github.com/searxng/searxng-docker.git第二步 進入 searxng-docker目錄中修改docker-compose.yaml(直接復制粘貼) cd searxng-dockerdocker-compose.yaml …