【第14节】windows sdk编程:进程与线程介绍

news2025/3/25 21:17:59

目录

一、进程与线程概述

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 结构体
        // 如果创建进程失败,返回 false
        return 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; // 用于保存线程ID
    HANDLE 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.coloradmin.cn/o/2319023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32U575RIT6单片机(四)

作业: 使用I2C获取SHT20传感器温湿度 使用I2C获取AP3216C三合一传感器: 光照, 接近, 红外 三个功能 合并的传感器 #ifndef SHT20_H #define SHT20_H#include "stdint.h" #include "i2c.h" #include "stdio.h" //1、确定从机的设备地址(代码不…

EMQX安装与配置

EMQX安装与配置 EMQX安装与配置 https://www.emqx.com/zh/downloads-and-install/broker?osUbuntucd /usr/local/srcwget https://www.emqx.com/zh/downloads/broker/4.4.19/emqx-4.4.19-otp24.3.4.2-1-ubuntu16.04-amd64.deb sudo apt install ./emqx-4.4.19-otp24.3.4.2-1…

JVM逃逸分析作用和原理

JVM逃逸分析作用和原理 在JVM的性能优化中&#xff0c;我们通常会关注内存分配、垃圾回收等问题。而逃逸分析&#xff08;Escape Analysis&#xff09;是JVM中一种精妙的优化技术&#xff0c;它可以在对象分配时判断该对象是否会在方法或线程之外被访问&#xff0c;从而影响其…

拓展 Coco AI 功能 - 智能检索 Hexo 博客

在之前的文章中&#xff0c;我们成功让 Coco AI 检索 Hugo 博客&#xff0c;这对于博客作者来说是一大福音。然而&#xff0c;从 Hexo 迁移到 Hugo 的成本不容小觑&#xff0c;毕竟大多数开发者对 Node.js 更熟悉&#xff0c;而 Golang 相对陌生。那么&#xff0c;既然 Coco AI…

爬虫基础之爬取猫眼Top100 可视化

网站: TOP100榜 - 猫眼电影 - 一网打尽好电影 本次案例所需用到的模块 requests (发送HTTP请求) pandas(数据处理和分析 保存数据) parsel(解析HTML数据) pyecharts(数据可视化图表) pymysql(连接和操作MySQL数据库) lxml(数据解析模块) 确定爬取的内容: 电影名称 电影主演…

LS-NET-006-思科MDS 9148S 查看内存

LS-NET-006-思科MDS 9148S 查看内存 方法一&#xff1a;使用 show version​ 命令 该命令可显示设备的基本系统信息&#xff0c;包括内存总量。 登录交换机的CLI&#xff08;通过控制台或SSH连接&#xff09;。输入命令&#xff1a; show version 在输出中查找类似以下内容…

小程序API —— 54 路由与通信 - 编程式导航

在小程序中实现页面的跳转&#xff0c;有两种方式&#xff1a; 声明式导航&#xff1a;navigator 组件编程式导航&#xff1a;使用小程序提供的 API 编程式导航 API 提供了五个常用的 API 方法&#xff1a; wx.navigateTo()&#xff1a;保留当前页面&#xff0c;跳转到应用内…

关于金融开发领域的一些专业知识总结

目录 1. 交易生命周期 1.1 证券交易所 1.1.1 交易前 1) 订单生成&#xff08;Order Generation&#xff09; 2) 订单管理&#xff08;Order Management&#xff09; 1.1.2 交易执行 3) 交易匹配&#xff08;Trade Matching&#xff09; 1.1.3 交易后 4) 交易确认&…

DeepSeek-R1深度解读

deepseek提出了一种通过强化学习&#xff08;RL&#xff09;激励大语言模型&#xff08;LLMs&#xff09;推理能力的方法&#xff0c;个人认为最让人兴奋的点是&#xff1a;通过RL发现了一个叫“Aha Moment”的现象&#xff0c;这个时刻发生在模型的中间版本中。在这个阶段&…

15-双链表-双链表基本操作

题目 来源 827. 双链表 - AcWing题库 思路 此题我只想说&#xff0c;千万千万别漏了头结点和尾结点&#xff0c;不然根本查不出来是哪里出了问题&#xff0c;因为传入的k会有问题&#xff1b;最左边插入&#xff0c;相当于是在头结点的右边插入&#xff08;也就是0号节点的右…

【小也的Java之旅系列】01 分布式、集群、微服务的区别

前言 做Java开发多年&#xff0c;一直以来都有想把Java做成一个系列的想法&#xff0c;最近整理自己的笔记发现有很多值得写的内容&#xff0c;但这些内容又往往杂乱不堪。CSDN上有很多高质量的Java博客&#xff0c;但大多不是从一个人成长的角度去写的。而我们——一个技术人…

基于视觉的核桃分级与套膜装置研究(大纲)

基于视觉的核桃分级与套膜装置研究&#xff1a;从设计到实现的完整指南 &#xff08;SolidWorks、OpenCV、STM32开发实践&#xff09; &#x1f31f; 项目背景与目标 1.1 为什么选择视觉分级与套膜&#xff1f; 产业痛点&#xff1a; 中国核桃年产量全球第一&#xff0c;但…

JimuReport与deepseek结合,颠覆现有BI模式

在数字化转型的浪潮中&#xff0c;企业对数据的依赖程度越来越高&#xff0c;如何高效地分析和利用数据成为关键。JimuReport凭借其强大的报表设计能力和灵活的数据处理功能&#xff0c;已经成为众多企业的首选工具。如今&#xff0c;它即将与DeepSeek深度结合&#xff0c;为企…

11、STL中的set使用方法

一、了解 set 是 C 标准模板库&#xff08;STL&#xff09;中提供的有序关联容器之一。基于红黑树&#xff08;Red-Black Tree&#xff09;实现&#xff0c;用于存储一组唯一的元素&#xff0c;并按照元素的值进行排序。 set的特性 唯一性 键是唯一的。无重复。 有序性 按升序…

操作系统——(管程、线程、进程通信)

目录 一、管程机制 &#xff08;1&#xff09;管程定义 &#xff08;2&#xff09;特点&#xff1a; 二、进程通信 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;高级通信机制 三、线程 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;与进程比较…

Sqlserver安全篇之_启用和禁用Named Pipes的案列介绍

https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/named-pipes-properties?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/client-protocols-named-pipes-properties-protocol-tab?viewsql-server-ver16 默认…

Web开发-JS应用原生代码前端数据加密CryptoJS库jsencrypt库代码混淆

知识点&#xff1a; 1、安全开发-原生JS-数据加密&代码混淆 2、安全开发-原生JS-数据解密安全案例 一、演示案例-WEB开发-原生JS&第三方库-数据加密 前端技术JS实现&#xff1a; 1、非加密数据大致流程&#xff1a; 客户端发送->明文数据传输-服务端接受数据->…

比特币牛市还在不在

在加密货币的风云世界里&#xff0c;比特币的一举一动始终牵动着投资者们的神经。近期比特币的涨幅动作&#xff0c;再次引发了市场对于牛市是否仍在延续的激烈讨论。 在深入探索比特币市场的过程中&#xff0c;获取全面且及时的资讯至关重要。您可以通过访问Techub News&#…

Python、MATLAB和PPT完成数学建模竞赛中的地图绘制

参加数学建模比赛时&#xff0c;很多题目——诸如统计类、数据挖掘类、环保类、建议类的题目总会涉及到地理相关的情景&#xff0c;往往要求我们制作与地图相关的可视化内容。如下图&#xff0c;这是21年亚太赛的那道塞罕坝的题目&#xff0c;期间涉及到温度、降水和森林覆盖率…

跨平台RTSP高性能实时播放器实现思路

跨平台RTSP高性能实时播放器实现思路 目标&#xff1a;局域网100ms以内超低延迟 一、引言 现有播放器&#xff08;如VLC&#xff09;在RTSP实时播放场景中面临高延迟&#xff08;通常数秒&#xff09;和资源占用大的问题。本文提出一种跨平台解决方案&#xff0c;通过网络层…