【第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.rhkb.cn/news/36352.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构中的引用管理对象体系

数据结构中的引用管理对象体系 &#xff08;注&#xff1a;似复刻变量即实例对象&#xff09; 引用管理对象的&#xff0c;有引用就能管理到它所指向的对象&#xff0c;我们拿引用最终的目的就是管理那些我们需要管理的最终直接对象&#xff0c;引用也是对象&#xff0c;同时…

Java 异常处理

一、引言 在 Java 编程中,异常处理是一个至关重要的部分。程序在运行过程中可能会遇到各种意外情况,如文件不存在、网络连接中断、数组越界等。如果不进行适当的处理,这些异常可能会导致程序崩溃,影响用户体验。Java 提供了一套完善的异常处理机制,允许开发者捕获和处理这…

数据驱动进化:AI Agent如何重构手机交互范式?

如果说AIGC拉开了内容生成的序幕&#xff0c;那么AI Agent则标志着AI从“工具”向“助手”的跨越式进化。它不再是简单的问答机器&#xff0c;而是一个能够感知环境、规划任务并自主执行的智能体&#xff0c;更像是虚拟世界中的“全能员工”。 正如行业所热议的&#xff1a;“大…

skywalking微服务链路追踪

是什么&#xff1f; skywalking是一个优秀的国产开源框架&#xff0c;2015年由个人吴晟&#xff08;华为开发者&#xff09;开源 &#xff0c; 分布式链路追踪就是将一次分布式请求还原成调用链路&#xff0c;将一次分布式请求的调用情况集中展示&#xff0c;比如各个服务节点…

DR-CAN 卡尔曼滤波笔记

Kalman Filter&#xff08;卡尔曼滤波&#xff09; Optimal(最优化) Recursive(递归) Data Processing(数据处理) Algorithm(算法) 1 递归算法_Recursive Alorithm 1.1 公式推演 1.2 案例 1.3编程实现 % 设置迭代次数 n 5000000;% 生成测量值序列 % rand(n 1, 1) 生成一个…

HyperAD:学习弱监督音视频暴力检测在双曲空间中的方法

文章目录 速览摘要1. 引言2. 相关工作弱监督暴力检测双曲空间中的神经网络 3. 预备知识双曲几何切空间&#xff08;Tangent Space&#xff09;指数映射与对数映射&#xff08;Exponential and Logarithmic Maps&#xff09;3.1 双曲图卷积网络&#xff08;Hyperbolic Graph Con…

Freeze-Omni:冻结 LLM,实现语音对话

写在前面:语音LLM 大型语言模型(LLM)的强大能力,为构建智能语音对话系统提供了无限可能。然而,将 LLM 与语音模态结合,并非易事。直接微调 LLM,容易导致灾难性遗忘,丧失其原有的知识和能力;而训练数据不足,又难以充分发挥 LLM 的潜力。 如何才能在保留 LLM 强大能力…

践行健康养生,拥抱美好人生

在当今快节奏的社会浪潮中&#xff0c;人们在忙碌奔波时&#xff0c;健康常被抛诸脑后。可一旦身体亮起红灯&#xff0c;才惊觉健康无价。其实&#xff0c;只要巧妙运用养生之道&#xff0c;就能轻松守护健康&#xff0c;让生活重回正轨。 养生始于饮食。我们要巧妙搭配食物&am…

上海亚商投顾:沪指窄幅震荡 深海科技概念持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天窄幅震荡&#xff0c;三大指数涨跌互现。深海科技概念持续活跃&#xff0c;巨力索具、东方海洋、海洋…

Java 大视界 -- Java 大数据在智能体育赛事直播数据分析与观众互动优化中的应用(142)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

基于Spring Boot的图书管理系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

SpringBoot对接DeepSeek

文章目录 Spring Boot 集成 DeepSeek API 详细步骤1. 创建API Key1.访问 [DeepSeek控制台](https://platform.deepseek.com/usage) 并登录。2.点击 Create API Key 生成新密钥。3.复制并保存密钥&#xff08;需在Spring Boot配置文件中使用&#xff09;。 2. 创建Spring Boot工…

Web 小项目: 网页版图书管理系统

目录 最终效果展示 代码 Gitee 地址 1. 引言 2. 留言板 [热身小练习] 2.1 准备工作 - 配置相关 2.2 创建留言表 2.3 创建 Java 类 2.4 定义 Mapper 接口 2.5 controller 2.6 service 3. 图书管理系统 3.1 准备工作 - 配置相关 3.2 创建数据库表 3.2.1 创建用户表…

PCAN安装驱动、使用PcanView监听发送报文

首先将PCAN插入电脑USB接口&#xff0c;winR快捷键输入compmgmt.msc&#xff0c;弹出计算机管理界面&#xff0c;装过驱动则显示PCAN-USB。未装过驱动的会显示XCAN-USB&#xff0c;按照如片步骤安装驱动&#xff0c;这里不在文字赘述。 PCAN Windows驱动下载&#xff1a; htt…

Pytest项目_day01(HTTP接口)

HTTP HTTP是一个协议&#xff08;服务器传输超文本到浏览器的传送协议&#xff09;&#xff0c;是基于TCP/IP通信协议来传输数据&#xff08;HTML文件&#xff0c;图片文件&#xff0c;查询结果等&#xff09;。 访问域名 例如www.baidu.com就是百度的域名&#xff0c;我们想…

利用knn算法实现手写数字分类

利用knn算法实现手写数字分类 1.作者介绍2.KNN算法2.1KNN&#xff08;K-Nearest Neighbors&#xff09;算法核心思想2.2KNN算法的工作流程2.3优缺点2.4 KNN算法图示介绍 3.实验过程3.1安装所需库3.2 MNIST数据集3.3 导入手写数字图像进行分类3.4 完整代码3.5 实验结果 1.作者介…

HTML中滚动加载的实现

设置div的overflow属性&#xff0c;可以使得该div具有滚动效果&#xff0c;下面以div中包含的是table来举例。 当table的元素较多&#xff0c;以至于超出div的显示范围的话&#xff0c;观察下该div元素的以下3个属性&#xff1a; clientHeight是div的显示高度&#xff0c;scrol…

ubuntu20.04系统没有WiFi图标解决方案_安装Intel网卡驱动

文章目录 1. wifi网卡配置1.1 安装intel官方网卡驱动backport1.1.1 第四步可能会出现问题 1.2 ubuntu官方的驱动1.3 重启 1. wifi网卡配置 我的电脑是华硕天选4&#xff08;i7&#xff0c;4060&#xff09;&#xff0c;网卡型号intel ax201 ax211 ax210通用。 参考文章&#…

Anacoda进入自己的集成环境CLI中

鼠标左键单击那个"播放"图标&#xff0c;在弹出的菜单中选择"Open Terminal"&#xff0c;即可进入。 还有一种是通过Scripts/active.bat文件的方式&#xff0c;是Windows下的方案&#xff0c;在当前目录下执行cmd&#xff0c;输入active切换Anacoda集成环…

设备健康管理系统是什么,设备健康管理系统多少钱?

想象一下&#xff0c;你的汽车在仪表盘报警前 3 天&#xff0c;手机就收到 “发动机轴承剩余寿命 1500 公里” 的提醒 —— 这就是 ** 设备健康管理系统&#xff08;EHM&#xff09;** 的日常。在制造业&#xff0c;设备故障每年造成全球 3.4 万亿美元损失&#xff0c;而 80% 的…