《Windows核心编程》第二部分(进程)

第二部分

(1)进程

windows 2000可以使用多核,windows 98只能使用单核。
在第一部分中大体的分析了windows操作系统中的内核对象,本节中的进程则属于内核对象的一种。进程的内核对象则是我们经常说的PCB进程控制块。
进程:一个正在运行的一段程序的实例。进程是不活泼的。只是线程的容器。
一个进程包括内核对象进程控制块和所执行的代码块和数据。
每个进程包含一个环境块形式为:
0_1526988425966_d95bac57-90a3-46f1-b2b0-f4573f37ad23-image.png
是以键值对的形式,且键值中不能包含等号,空格也算在环境变量中,要以\0结尾。
初始化环境变量在windows 98下要修改AutoExec.bat文件。写入形如set Name=Value。windows 2000会继承本机的环境变量和当前用户的环境变量。修改可在以下界面修改0_1526988801275_97cb3c6a-9c48-406b-8e01-79bba00a39e8-image.png

进程的错误模式:每个进程的一组标志,告诉系统对严重错误如何处理。可调用SetErrorMode.

要创立进程,首先要编写一段可以执行的程序。
在windows中程序可分为两种:GUI(窗口应用呢)和CUI(命令行应用)。下面我们分别讨论这两种应用程序。
链接器开关:告诉链接器在链接时链接什么入口函数。
启动函数:入口点函数之前还有一个被称为启动函数的函数。该函数用来初始化C/C++运行库、构造全局和静态的C++对象
0_1526894950924_TIM截图20180521172859.png
需要注意的是使用什么字符集的函数,其中参数也必须相符合。

进程运行过程:首先是调用启动函数初始化一些数据,然后调用程序的入口函数,入口函数执行完之后返回值给启动函数,然后调用exit函数销毁资源,然后调用ExitProcess函数结束进程。

在入口函数,第一个参数为进程的内存基地址,可用来加载可执行文件。内存基地址是由链接器决定的,默认的是exe文件是0x400000,dll是0x10000000。
GetModuleHandle函数可以根据可执行文件的名称获得可执行文件加载的地址。
GetModuleHandleEx函数可以获得当前代码在哪个dll中运行,获得dll的基地址。

C运行库的启动代码在执行一个GUI应用程序时,会调用windows函数GetCommandLine来获得进程的完整命令行。

GetEnvironmentStrings,GetEnvironmentVariable,SetEnvironmentVariable,ExpandEnvironmentStrings函数对环境块相关操作函数

不使用了应该用FreeEnvironmentStrings释放内存

GetCurrentDirectory和SetCurrentDirectory来获得和设置当前驱动器和目录

GetFullPathName函数可以获得驱动器的当前目录

使用GetVersionEx可以获得window系统的版本号

VerifyVersionInfo用于对主机系统的版本与你的应用程序需要的版本进行比较。

CreateProcess函数用来创建进程

Bool CreateProcess(
//进程要调用的可执行文件,必须指定扩展名,如果没有指定路径会在当前页面查找
//为NULL的话,就默认第二个字符串的前面一段是程序名
PCTSTR pszApplicationName,
//进程要调用的命令行,要传进非常量字符串,内部会对其进行修改
PTSTR pszCommandLine,
//进程安全描述符
PSECURTITY_ATTRIBUTES psaProcess,
//线程安全描述符
PSECUTRITY_ATTRIBUTE psaThread,
//是否可以继承句柄标识
Bool hInheritHandles,
//进程创建方式,后面详细说明
DWROD fdwCreate,
//指定使用的环境字符串,NULL表示继承父进程
PVOID pvEnvironment,
//设置当前驱动器和目录
PCTSTR pszCurDir,
//新建进程初始值
PSTARTUPINFO psiStartInfo,
//函数调用成功后返回的进程和进程中线程的信息
PPROCESS_INFORMATION ppiProcInfo
);
参数中使用的几个结构体
typedef struct _STARTUPINFO
{
cb,
lpReserved,
lpDesktop,
lpTitle,
dwX,
dwY,
dwXSize,
dwYSize,
dwXCountChars,
dwYCountChars,
dwFillAttribute,
dwFlags,
wShowWindow,
cbReserved2,
lpReserved,
hStdInput,
hStdOutput,
hStdError
}STARTUPINFO, *LPSTARTUPINFO
typedef struct _PROCESS_INFORMATION
{
   HANDLE hProcess;
   HANDLE hThread;
   HANDLE dwProcessId;
   HANDLE dwThreadId;
}PROCESS_INFORMATION;
//在进程运行结束后一定要通过closeHandle来释放资源如下所示
PROCESS_INFORMATION pi;
BOOL Success=CreateProcess(...,&pi);
if(Success){
	CloseHandle(pi.hThread);
	CloseHandle(pi.hProcess);
}

进程终止:正常主线程返回,任何子线程调用ExitProcess,其他进程调用TerminateProcess。
只有正常返回才会释放所有资源。
虽然进程不会进行任何操作,但是操作系统可以在进程之后进行全面的清理。

GetExitCodeProcess此函数会查找进程内核对象并从内核对象的数据结构中取出退出代码。任何时候都可以调用此函数。如此时进程正在运行那么将会得到STILL_ALIVE。
要确定进程是否终止可以调用WaitForSingleObject函数,并传入进程句柄,会挂起当前线程,直到它所等待的对象变为已触发状态。

windows提供了一系列查看进程信息的方式。Windows NT通过EnumProcesses函数来枚举。其他的Windows一般可以通过ToolHelp API来枚举。

(2)进程的容器-作业

windows 2000提供了一个新的作业内核对象,能够将进程组合在一起,这样就可以对进程加上限制。(Windows 98不支持)

CreateJobObject()创建一个新的作业内核对象。
OpenJobObject()访问作业内核对象。
AssignProcessToJobObject()将进程添加进作业。
SetInformationJobObject()可以给作业加上限制。
QueryInformationJobObject()获取当前的限制信息或统计信息。
TerminateJobObject()撤销作业中的进程。

作业通知信息,
简单的通知信息可以在创建作业的父进程的句柄上进行监听,如果有通知过来就可以简单的获取到。
高级的通知信息必须要创建一个I/O完成端口,并调用SetInformationJobObject函数将作业与端口关联起来,让一个或多个线程通过函数GetQueuedCompletionStatus函数监听等待通知信息的到来。

猜你喜欢

转载自blog.csdn.net/hhouxiang/article/details/80653826