《C++多线程编程实战》——2.4 进程的实现

简介:

本节书摘来自异步社区出版社《C++多线程编程实战》一书中的第2章,第2.4节,作者: 【黑山共和国】Milos Ljumovic(米洛斯 留莫维奇),更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.4 进程的实现

在现代的多任务系统中,进程控制块(Process Control Block,PCB)储存了高效管理进程所需的许多不同数据项。PCB是操作系统为了管理进程,在内核中设置的一种的数据结构。操作系统中的进程用PCB来表示。虽然这种数据结构的细节因系统而异,但是常见的部分大致可分为三大类:

进程标识数据;
进程状态数据;
进程控制数据。


ee860636f354f494a5f5e605f59ec0a692026c97

图2.6 

PCB是管理进程的中心。绝大多数操作系统程序(包括那些与调度、内存、I/O资源访问和性能监控相关的程序)都要访问和修改它。通常,要根据PCB为进程构建数据。例如,某PCB内指向其他PCB的指针以不同的调度状态(就绪、阻塞等)创建进程队列。

操作系统必须代表进程来管理资源。它必须不断地关注每个进程的状态、系统资源和内部值。下面的程序示例演示了如何获得一个进程基本信息结构地址,其中的一个特征就是PCB的地址。另一个特征是唯一的进程ID。为简化示例,我们只输出从对象中读取的进程ID。

准备就绪
确定安装并运行了Visual Studio。

操作步骤
我们再来创建一个操控进程的程序。这次,我们从进程基本信息结构中获取进程ID。请执行以下步骤。

1. 创建一个新的默认C++控制台应用程序,名为NtProcessDemo

2. 打开NtProcessDemo.cpp

3. 添加下面的代码:

#include "stdafx.h"
#include <Windows.h>
#include <Winternl.h>
#include <iostream>

using namespace std;

typedef NTSTATUS(NTAPI* QEURYINFORMATIONPROCESS)(
  IN HANDLE ProcessHandle,
  IN PROCESSINFOCLASS ProcessInformationClass,
  OUT PVOID ProcessInformation,
  IN ULONG ProcessInformationLength,
  OUT PULONG ReturnLength OPTIONAL
  );

int _tmain(int argc, _TCHAR* argv[])
{
  STARTUPINFO startupInfo = { 0 };
  PROCESS_INFORMATION processInformation = { 0 };

  BOOL bSuccess = CreateProcess(
    TEXT("C:\\Windows\\notepad.exe"), NULL, NULL,
    NULL, FALSE, NULL, NULL, NULL, &startupInfo,
    &processInformation);

  if (bSuccess)
  {
    cout << "Process started." << endl << "Process ID:\t"
      << processInformation.dwProcessId << endl;
    PROCESS_BASIC_INFORMATION pbi;
    ULONG uLength = 0;

    HMODULE hDll = LoadLibrary(
      TEXT("C:\\Windows\\System32\\ntdll.dll"));

    if (hDll)
    {
      QEURYINFORMATIONPROCESS QueryInformationProcess =
        (QEURYINFORMATIONPROCESS)GetProcAddress(
        hDll, "NtQueryInformationProcess");

      if (QueryInformationProcess)
      {
        NTSTATUS ntStatus = QueryInformationProcess(
          processInformation.hProcess,
          PROCESSINFOCLASS::ProcessBasicInformation,
          &pbi, sizeof(pbi), &uLength);

        if (NT_SUCCESS(ntStatus))
        {
          cout << "Process ID (from PCB):\t"
            << pbi.UniqueProcessId << endl;
        }
        else
        {
          cout << "Cannot open PCB!" << endl
            << "Error code:\t" << GetLastError()
            << endl;
        }
      }
      else
      {
        cout << "Cannot get "
          << "NtQueryInformationProcess function!"
          << endl << "Error code:\t"
          << GetLastError() << endl;
      }
      FreeLibrary(hDll);
    }
    else
    {
      cout << "Cannot load ntdll.dll!" << endl
        << "Error code:\t" << GetLastError() << endl;
    }
  }
  else
  {
    cout << "Cannot start process!" << endl
      << "Error code:\t" << GetLastError() << endl;
  }
  return 0;
}```
示例分析
该例中,我们使用了一些其他头文件:`Winternl.h`和`Windows.h`。`Winternl.h`头文件包含了大部分Windows内部函数的原型和数据表示,例如`PROCESS_BASIC_INFORMATION`结构的定义:

typedef struct _PROCESS_BASIC_INFORMATION {
  PVOID Reserved1;
  PPEB PebBaseAddress;
  PVOID Reserved2[2];
  ULONG_PTR UniqueProcessId;
  PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;`
操作系统在调用内核态和用户态之间的子例程时会用到该结构。

结合PROCESSINFOCLASS::ProcessBasicInformation枚举,我们通过UniqueProcessId特征获取进程标识符,如上面的代码所示。

首先,定义QEURYINFORMATIONPROCESS,这是从ntdll.dll中加载的NtQueryInformationProcess函数的别名。当通过GetProcAddressWin32 API获得该函数的地址时,就可以询问PROCESS_BASIC_INFORMATION对象了。注意PROCESS_BASIC_INFORMATION结构的PebBaseAddress字段是一个指针,指向新创建进程的PCB。如果还想进一步研究PCB,检查新创建的进程,则必须在运行时使用ReadProcessMemory例程。因为PebBaseAddress指向属于新创建进程的内存。

相关文章
|
9月前
|
监控 Linux 测试技术
C++零拷贝网络编程实战:从理论到生产环境的性能优化之路
🌟 蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕C++与零拷贝网络编程,从sendfile到DPDK,实战优化服务器性能,毫秒级响应、CPU降60%。分享架构思维,共探代码星辰大海!
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
程序员 编译器 C++
【实战指南】C++ lambda表达式使用总结
Lambda表达式是C++11引入的特性,简洁灵活,可作为匿名函数使用,支持捕获变量,提升代码可读性与开发效率。本文详解其基本用法与捕获机制。
434 90
|
C语言 C++
【实战指南】 C/C++ 枚举转字符串实现
本文介绍了在C/C++中实现枚举转字符串的实用技巧,通过宏定义与统一管理枚举名,提升代码调试效率并减少维护错误。
654 97
|
缓存 NoSQL Unix
【实战指南】守护进程服务实现
本文介绍了在Linux系统中实现守护进程异常重启的几种方案。通过理解僵死进程和信号处理机制,提出了基于SIGCHLD信号监听、轮询proc文件系统及waitpid接口的三种方法,并给出了C++实现代码。最终选择轮询方式以提升稳定性,确保服务进程在崩溃后能自动重启,保障系统可靠性。
561 96
|
监控 Linux C++
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展(2)
本文是《4步实现C++插件化编程》的延伸,重点介绍了新增的插件“热拔插”功能。通过`inotify`接口监控指定路径下的文件变动,结合`epoll`实现非阻塞监听,动态加载或卸载插件。核心设计包括`SprDirWatch`工具类封装`inotify`,以及`PluginManager`管理插件生命周期。验证部分展示了插件加载与卸载的日志及模块状态,确保功能稳定可靠。优化过程中解决了动态链接库句柄泄露问题,强调了采纳用户建议的重要性。
617 110
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展(2)
|
9月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
1220 2
|
9月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
431 1
|
9月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
397 1
|
人工智能 程序员 C++
【实战经验】C/C++右移高位补0还是1?
本文探讨了C/C++中右移运算时高位补0还是补1的问题。通过示例代码分析,揭示了右移规则:无符号类型高位补0;有符号类型根据正负决定(正数补0,负数补1)。文中列举了可能导致错误的场景,并提供了两种规避措施——使用无符号类型和掩码校正,确保结果符合预期。最后总结指出,右移运算虽常见,但若处理不当易引发隐晦Bug,需谨慎对待。
858 100

热门文章

最新文章