Windows程序设计(1)——Win32运行原理(二)
3 创建进程
3.1 进程和线程
进程通常被定义为一个存在运行的程序的实例。进程是一个正在运行的程序,它拥有自己的虚拟地址空间,拥有自己的代码、数据和其他系统资源。一个进程也包含一个或者多个运行在此进程内的线程。
程序是指一连串的静态的指令,而进程是一个容器,它包含了一系列运行在这个程序实例上下文中的线程使用的资源。
每个进程至少拥有一个在它的地址空间中运行的线程。操作系统创建进程后,会创建一个线程执行进程中的代码。通常把这个线程称为该进程的主线程。
3.2 应用程序的启动过程
应用程序必须有一个入口函数,它在程序开始运行的时候被调用。如:
int main(int argc, char* argv[]);
操作系统事实上并不是真正的调用main函数,而是去调用C/C++运行期启动函数,此函数会初始化C/C++运行期库。它会保证在用户的代码执行之前所有的全局的或者静态的C++对象能够被正确的创建,执行这些对象的构造函数中的代码。在控制台应用程序中,C/C++运行期启动函数会调用程序入口函数main。
在Win32程序的启动过程中。应用程序的启动过程就是进程的创建过程,操作系统是通过调用CreateProcess函数来创建新的进程的。当一个线程调用CreateProcess函数的时候,系统会创建一个进程内核对象,其使用计数被初始化为1。此进程内核对象不是进程本身,仅仅是一个系统用来管理这个进程的小的数据结构。系统然后会为新的进程创建一个虚拟地址空间,加载应用程序运行时所需要的代码和数据。
系统接着会为新进程创建一个主线程,这个主线程通过执行C/C++运行期启动代码开始运行,C/C++运行期启动代码又会调用main函数。如果系统成功创建了新的进程和其主线程,CreateProcess函数会返回TRUE,否则返回FALSE。
一般将创建进程称为父进程,被创建的进程称为子进程。系统在创建进程时会为新进程指定一个STARTUPINFO类型的变量,这个结构包含了父进程传递给子进程的一些显示信息。
typedef struct _STARTUPINFO {
DWORD cb; // 本结构长度,总是应该设为为sizeof(STARTUPINFO)
LPTSTR lpReserved; // 保留字段
LPTSTR lpDesktop; // 指定桌面名称
LPTSTR lpTitle; // 操控台应用程序使用,指定控制台窗口标题
DWORD dwX; // 指定新创建窗口的位置坐标和大小信息
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars; // 控制台应用程序使用,指定控制台窗口的大小
DWORD dwYCountChars;
DWORD dwFillAttribute; // 控制台应用程序使用,指定控制台窗口的前/背景色
DWORD dwFlags; // 标志,标识本结构中哪些成员的值是有效的
WORD wShowWindow; // 窗口显示方式
WORD cbReserved2; // 保留
LPBYTE lpReserved2; // 保留
HANDLE hStdInput; // 控制台应用程序使用,内个标准句柄
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
一个进程可能调用GetStartupInfo函数来取得父进程创建自己时使用的STARTUPINFO结构。事实上,Windows系统就是通过调用这个函数来取得当前进程的创建信息的。
VOID GetStartupInfo(
LPSTARTUPINFO lpStartupInfo // startup information
);
定义一个STARTUPINFO结构的对象以后,总要在使用此对象将对象的cb成员初始化为STARTUPINFO结构的大小,如:
STARTUPINFO si = {sizeof(si)};
::GetStartupInfo(&si);
3.3 CreateProcess函数
CreateProcess函数创建一个新的进程和该进程的主线程。函数原型如下:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 可执行文件/模块的名称
LPTSTR lpCommandLine, // 指定传递给执行模块的参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全性,值为NULL表示使用默认安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全性,值为NULL表示使用默认安全属性
BOOL bInheritHandles, // 指定当前进程中的可继承句柄是否可被新进程继承
DWORD dwCreationFlags, // 指定新进程的优先级和其他创建标志
LPVOID lpEnvironment, // 指定新进程使用的环境变量
LPCTSTR lpCurrentDirectory, // 指定新进程使用的当前目录
LPSTARTUPINFO lpStartupInfo, // 指定新进程中主窗口位置、大小和标准句柄等
LPPROCESS_INFORMATION lpProcessInformation // 返回新建进程的标志信息,如ID和句柄
);
这里很多的变量类型都与平时使用的不太一样,它一般有如下的定义:
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef float FLOAT;
typedef void far *LPVOID;
typedef int INT;
typedef unsigned int UINT;
lpApplicationName和lpCommandLine参数指定了新的进程将要使用的可执行文件的名称和传递给新进程的参数,如果lpApplicationName为空,那么将lpCommandLine中第一个空白分隔之前的字符串做为模块名。如下启动记事本:
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
char *szCommandLine = "notepad";
::CreateProcess(NULL, szCommandLine, NULL, NULL,
FALSE, NULL, NULL, NULL, &si, &pi);
如果lpCommandLine参数中的第一个单词没有后缀,则.exe后缀将被添加进来。CreateProcess函数将会按照以下路径去搜索可执行文件:
- 调用进程的可执行文件所在的目录。
- 调用进程的当前目录。
- Windows的系统目录(system32目录)。
- Windows目录。
- 在名称为PATH的环境变量中列出的目录。
如果文件名中包含了目录,系统会直接在这个目录中查找可执行文件。可以给启动的进程传递参数,如下参数,将使用记事本打开主进程当前目录下的ReadMe.txt文件:
char* szCommandLine = "notepad ReadMe.txt";
lpProcessInformation参数是一个指向PROCESS_INFORMATION结构的指针。CreateProcess函数在返回之前会初始化此结构成员。结构定义如下:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // 新建进程的内核句柄
HANDLE hThread; // 新建进程中主线程的内核句柄
DWORD dwProcessId; // 新建进程的ID
DWORD dwThreadId; // 新建进程的主线程的ID
} PROCESS_INFORMATION;
创建一个新的进程促使系统创建一个进程内核对象和一个线程内核对象。在创建它们的时候,系统将每个对象的使用计数初始化为1。然后在CreateProcess返回之前,这个函数打开此进程内核对象和线程内核对象的句柄,并将它们的值传给上述结构的hProcess和hThread成员。此时这些内核对象的使用计数为2。因此父进程中必须有线程调用CloseHandle关闭这两个内核对象的句柄,否则子进程已经终止了,该进程的进程内核对象和主线程的内核对象也不会被释放。
进程和线程都分被分配一个唯一的ID号,且二者不会相同。但该ID会被重新利用,也就是说当一个ID的进程终止后,后续的进程或者线程是有可能再次使用该ID的。
3.4 实例
如下实例打开系统的记事本。
#include <stdio.h>
#include <windows.h>
int main(int argc, char* argv[])
{
TCHAR szCommandLine[] = "notepad";
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
BOOL bRet = ::CreateProcess(
NULL, // 该参数留空
szCommandLine, // 命令行参数
NULL, // 默认进程安全性
NULL, // 默认线程安全性
FALSE, // 指定当前进程内的句柄不被子进程继承
NULL, // 不指定优先级和创建参数
NULL, // 使用本进程的环境变量
NULL, // 使用本进程的驱动器和目录
&si,
&pi);
if (bRet)
{
// 关闭不使用的句柄
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
printf("Process ID: %d\n", pi.dwProcessId);
printf("Thread ID: %d\n", pi.dwThreadId);
}
getchar();
return 0;
}
运行结果如:
Windows程序设计(1)——Win32运行原理(二)的更多相关文章
- Windows程序设计(1)——Win32运行原理(一)
CPU保护模式与Windows系统 1 Windows多任务 2 虚拟内存 3 处理器的特权级别 内核对象 1 内核对象有什么用 2 对象句柄 3 使用计数 1 CPU保护模式与Windows系统 8 ...
- Windows程序设计(1)——Win32运行原理(三)
进程控制 1 获得系统进程 2 终止当前进程 3 终止其他进程 4 进程控制 4.1 获得系统进程 使用toolhelp模块可以实现获取系统中当前运行当中的进程列表. 思路如下,使用CreateToo ...
- 重学c#系列——c#运行原理(二)
前言 c# 是怎么运行的呢?是否和java一样运行在像jvm的虚拟机上呢?其实差不多,但是更广泛. c# 运行环境不仅c#可以运行,符合.net framework 开发规范的都可以运行. c# 程序 ...
- Nginx+FastCGI运行原理(二)
1.4 PHP与PHP-FPM的安装及优化(2) 标签rlimit_files用于设置PHP-FPM对打开文件描述符的限制,默认值为1024.这个标签的值必须和Linux内核打开文件数关联起来,例如, ...
- Python+Appium运行简单的demo,你需要理解Appium运行原理!
坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:oschina.云+社区.知乎等各大平台都有. 目录 一.Appium 的理念 四个原则 1.Web-Selenium 的运行原理 2.Appium ...
- Java运行原理及内存分析
Java运行原理及内存分析 一.Java运行原理 二.Java内存分析
- java_自定义标签运行原理
一.自定义标签运行原理: 二.文字说明 1.IE->web服务器 2.Web服务器->jsp 3.遇到自定义标签,首先实例化标签所对应的标签处理器类 4.调用setPageContext方 ...
- 第二章--Win32程序运行原理 (部分概念及代码讲解)
学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...
- C++学习笔记1(Windows程序运行原理及程序编写流程)
窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与Lessonecl调用规范的比较,初学者常犯错误及注意事项.以下是应用程序与操作 ...
随机推荐
- Java 内存查看与分析
1:gc日志输出 在jvm启动参数中加入 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplication ...
- Codeforces 490F Treeland Tour(离散化 + 线段树合并)
题目链接 Treeland Tour 题目就是让你求树上LIS 先离散化,然后再线段树上操作.一些细节需要注意一下. #include <bits/stdc++.h> using name ...
- pdf转word工具
pdf转word工具及安装:http://blog.sina.com.cn/s/blog_6172011c0102vxir.html pdf去加密:http://www.downxia.com/dow ...
- spring boot教程(一):入门篇(非原创,总结笔记性质)
一,什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发 ...
- 实机桌面上给虚拟机安装系统(分区,恢复GHO)
在虚拟机里安装系统大家都会了.我这里介绍一种方法无须进入虚拟机里操作.全部在实机里完成对虚拟机安装系统(分区.恢复GHO).这里要使用到的工具如下1分区工具DISKGENIUS2虚拟光驱3Ghost镜 ...
- 使用cacheBuilder时捕获内部指定异常
由于cacheBuilder是另起线程获取,对call方法中的抛出的异常进行了封装.所以我们在最外层捕获时是无法直接指定异常类型捕获的, 获取异常的原因判断实例类型 public static voi ...
- python去除停用词(结巴分词下)
python 去除停用词 结巴分词 import jieba #stopwords = {}.fromkeys([ line.rstrip() for line in open('stopword. ...
- 工作总结 mvc 调页面传参数 参数值会一直保存 在这个页面上的
意思是 两个页面均可以 获取到id 和 goodsType 都可以获取 id goodsType post 的 还多带点属性值 form data 页面上带过去的 (新增 编辑)
- selenium之 文件上传方法
文件上传是所有UI自动化测试都要面对的一个头疼问题 首先,我们要区分出上传按钮的种类,大体上可以分为两种,一种是input框,另外一种就比较复杂,通过js.flash等实现,标签非input 我们分别 ...
- GG链路过多port不足导致的报错OGG-01223
假设我们GG同步链路在增多.就有可能出现这个报错.在日志中能体现. 2014-05-20 13:32:38 WARNING OGG-01223 TCP/IP error 111 (Connection ...