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

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

2.4 进程的实现

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

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


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

图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指向属于新创建进程的内存。

上一篇:CSS创建下拉菜单


下一篇:教你合理布局综合布线系统