Windows API(二)进程
进程常被定义为应用程序的一个运行实例。在Win32中,每个进程拥有4GB的地址空间。进程是惰性的,Win32进程什么也不执行,它只是拥有4GB的地址空间来存放应用程序所需的二进制代码和数据。
除地址空间外,每个程序还拥有别的资源,比如文件、动态内存分配和线程。这些不同的资源在进程的生命中被创建,当进程终止时,它们也被释放。
每个线程有自己的CPU寄存器组和栈。操作系统为每个独立的线程进行CPU的时间调度,操作系统以轮转方式向线程提供时间片。
1. Win32 应用程序
系统的加载器关心应用程序是基于CUI还是GUI,如果exe文件中指出是基于CUI的应用程序,加载器会自动为该程序生成一个控制台窗口。
1.1 进程的句柄
每个加载进进程地址空间的exe或dll文件都有一个唯一的实例句柄。exe文件的实例是作为WinMain的第一个参数hinstExe传递的,在调用装入资源的函数时通常需要该句柄的值。
1.2 进程的环境变量
每个进程都有相关联的环境块。环境块是分配在进程的地址空间中的一块内存。
一般子进程从它的父进程那里继承了一组完全一样的环境变量。不过父进程可以控制子进程继承哪些变量(通过CreateProcess的参数设置)。
可通过GetEnvironmentVariable和SetEnvironmentVariable获取及设置环境变量。
1.3 进程的错误模式
每个进程都有一组标志来告诉系统该怎样对严重的错误做出反应,进程可以调用SetErrorMode函数该告诉系统如何处理每种错误:
标志 | 值 | 描述 |
---|---|---|
SEM_FAILCRITICALERRORS | 0x0001 | 系统不显示严重错误处理消息框,把错误返回调用的进程 |
SEM_NOGPFAULTERRORBOX | 0x0002 | 系统不显示普通保护错误消息框,该标志只能被调试应用程序设置来用异常句柄自己处理普通保护(GP)错误 |
SEM_NOALIGNMENTFAULTEXCEPT | 0x0004 | 系统在找不到文件时不现实消息框 |
SEM_NOOPENFILEERRORBOX | 0x8000 | 系统自动修正内存排序错误,使它们对应用不可见,该标志对x86或Alpha处理器没有作用 |
缺省情况,子进程会继承父进程的错误模式。也可以在CreateProcess使设置不继承。
1.4 进程的当前驱动器和目录
可在环境变量中设置
1.5 系统版本
// 这个有点坑,程序员把主次版本颠倒了
DWORD GetVersion(void)
// 用这个
BOOL GetVersionEx(LPOSVERSIONINFOA lpVersionInformation);
printf("%X \n", GetVersion());
OSVERSIONINFO osv;
osv.dwOSVersionInfoSize = sizeof(osv);
BOOL ret = GetVersionEx(&osv);
if (ret)
{
printf("%X - %X - %X - %X - %s\n",
osv.dwMajorVersion, // 主版本
osv.dwMinorVersion, // 此版本
osv.dwBuildNumber, // 编译序号
osv.dwPlatformId, //
osv.szCSDVersion);
}
else
{
perror("GetVersionEx Error");
}
2. CreateProcess函数
BOOL CreateProcess(
_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
3. 终止进程
- 进程中某个线程调用了ExitProcess函数
- 进程中某个线程调用了TerminateProcess函数(可以终结当前进程,也可以杀死其他进程)
在主线程中使用return和ExitProcess及TerminateProcess有什么不同?参考:《Windows下return,exit和ExitProcess的区别和分析- -》
进程中的所有线程结束(ExitThread、TerminateThread)后会自动终止进程。
进程结束时,发生了什么:
- 余下所有线程都被终止
- 进程分配的内存被释放(线程分配的可能没有被释放),所有内核对象被关闭
- 进程内核对象状态变成了有信号态(WaitForSingleObject将会得到返回值)
- 进程的退出码从STILL、ACTIVE变成了由ExitProcess或TerminateProcess传递的代码
- 进程内核对象的使用计数被-1(进程内核对象不一定会变成0,需要所有引用进程调用CloseHandle或者进程关闭,或者调用GetExitCodeProcess)
4. 子进程
创建一个新线程,并等待结果:
STARTUPINFO si;
memset(&si, 0, sizeof(STARTUPINFO)); //初始化si在内存块中的值
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD dwExitCode;
char exec_file[] = TEXT("kernel_handle_inherit.exe"); //
if (!CreateProcess(exec_file, //执行模块
cmd, //命令行参数
NULL,
NULL,
TRUE, //继承父进程的句柄表
CREATE_NEW_CONSOLE, // 为新进程创建一个新的控制台窗口
NULL,
NULL,
&si,
&pi))
{
perror("CreateProcess Error");
exit(1);
}
else
{
//尽早关闭子进程的主线程
::CloseHandle(pi.hThread);
// 等待子进程结束
WaitForSingleObject(pi.hProcess, INFINITE);
// 获取退出码
GetExitCodeProcess(pi.hProcess,&dwExitCode);
// 关闭子进程,使用计数-1,系统释放进程内核对象
::CloseHandle(pi.hProcess);
}
4.1 运行分离的子进程
不等待子进程关闭:
PROCESS_INFORMATION pi;
if (!CreateProcess(,,,&pi))
{
perror("CreateProcess Error");
exit(1);
}
else
{
//尽早关闭子进程的主线程
::CloseHandle(pi.hThread);
// 关闭子进程,使用计数-1,不影响系统释放进程内核对象(否则将产生僵尸进程)
::CloseHandle(pi.hProcess);
}
下一篇: 网络爬虫
推荐阅读
-
python调用windows api锁定计算机示例
-
Python使用Windows API创建窗口示例【基于win32gui模块】
-
.Net Core 项目发布到Linux - CentOS 7(二)用Supervisor守护netcore进程
-
windows10秋季创意者更新16190 SDK下载:支持Fluent流畅设计API
-
c#两种方式调用google地球,调用COM API以及调用GEPLUGIN 与js交互,加载kml文件,dae文件。将二维高德地图覆盖到到三维谷歌地球表面。
-
windows任务管理器中隐藏进程的方法
-
单点登录 - API 认证系统 Passport(二)
-
Windows 10视觉UI将大变样 微软大改令其焕发第二春
-
操作系统学习(二)--进程描述和执行
-
[系列] go-gin-api 规划目录和参数验证(二)