Windows学习(007)--进程
来源:互联网 发布:ubuntu 14.04 安装gcc 编辑:程序博客网 时间:2024/06/05 22:42
进程
进程的组成
- 进程是“惰性”的,进程要做任何事情,都必须让线程来运行,线程执行进程地址空间中包含的代码。
- 一个进程可以拥有多个线程,所有的线程都能够在进程的地址空间 中“同时”运行代码,每个线程都有他自己的CPU寄存器以及自己的堆栈。
- 每个进程必须有一个线程,当系统创建亿个进程时,会自动创建亿个线程,这个线程我们称为主线程,如歌没有线程要执行进程地址空间的代码,进程就会失去了存在的意义,此时系统会自动销毁进程以及他的地址空间。
- 造作系统会以轮询的方式为每个线程分配CPU时间片,从而营造出“并行”假象。
- 如果计算机拥有多个CPU,操作系统会更复杂的方式为线程分配CPU时间。
- 问题:在单核的情况下,为什么操作系统不以多进程,而是以多线程的方式运行程序,多线
程和多进程不是一样吗?
答:1,空间不一样。2,切换成本不一样。
内核对象
操作系统用来管理进程。
地址空间
包涵了可执行文件DLL模块的代码和数据。
应用程序类型
控制台用户界面(CUI)
控制台程序是基于文本的,他们一般不会创建窗口或进程消息,而且不需要GUI。
图形用户界面(GUI)
GUI程序是一个图形化的前端,它可以创建窗口,可以拥有菜单,能通过对话框与用户交互,还能更新使用所有标准的“视窗化”的东西,Windows的机会所有附件应用程序都是GUI程序。
区别
- 在VS中,CUI程序的链接器开关为/SUBSYSTEM:CONSOLE,GUI程序的链接器开关为/SUBSYSTEM:WINDOWS,在加载时,会获取此值,如果是一个文本控制台,操作系统会使用“命令提示符”程序来启动这个程序,否则他只会加载这个,由程序自己来管理自己的窗口。
入口函数不同。
int main(int argc, char *argv[], char *envp[]);int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow);
启动函数不同,GUI的启动函数为WinMainCRTStartup或wWinMainCRTStartup,CUI的启动函数为mainCRTStartup或wmainCRTStartup。
进程实例句柄
- 加载到进程地址空间的每一个执行体或DLL文件都被赋予了亿个独一无二的实例句柄。
- HMODULE和HINSTANCE为同一类型,无区别。
- 获取实例句柄
HMODULE WINAPI GetModuleHandle( _In_opt_ LPCTSTR lpModuleName);
命令行参数
获取CommandLine
LPTSTR WINAPI GetCommandLine(void);//返回的是一个不能改的指针 LPWSTR* CommandLineToArgvW( _In_ LPCWSTR lpCmdLine, _Out_ > > int*pNumArgs);//返回是一个指针的指针,使用要释放 HLOCAL WINAPI LocalFree(_In_ HLOCAL hMem);
环境变量
- 环境变量相当于全局的命令行参数
获取环境变量
strEnvi_ = GetEnvironmentStrings();
打印环境变量
LPTCH strEnvi = strEnvi_;if (strEnvi_){ while (*strEnvi_) { _tprintf(TEXT("%s\n"), strEnvi_); strEnvi_ += lstrlen(strEnvi_) + 1; }}
删除获取到环变量指针
FreeEnvironmentStrings(strEnvi_);
进程路径
所在路径
- 会变化了,在VS调试中,会定义到我们源代码的位子;在运行exe时,是程序的位置。
获取所在路径
BOOL GetCurrentDirectory( LPTSTR pstrDirName, LPDWORD lpdwLen ) const;
设置所在路径
BOOL SetCurrentDirectory( LPCTSTR pstrDirName );
Module路径
- 是来定义我们的exe程序的位置。
获取Module路径
DWORD WINAPI GetModuleFileName( _In_opt_ HMODULE hModule, _Out_ LPTSTR lpFilename, _In_ DWORD nSize );
当前路径(默认路径)
获取当前路径
DWORD WINAPI GetFullPathName( _In_ LPCTSTR lpFileName, _In_ DWORD nBufferLength, _Out_ LPTSTR lpBuffer, _Out_ LPTSTR *lpFilePart );
设置当前目录
- 当“所在目录”被修改,“当前目录”也被修改(针对于盘)。比如,c盘的”所在目录”被更改,d盘的“所在目录“不被更改,则,c盘的”当前目录”就会被更改,d盘的”当前目录“”不被更改,反之亦然。
- 在环境变量里面,设置了当前目录。所以我们可以通过更改环境变量来更改当前目录。
BOOL WINAPI SetEnvironmentVariable( _In_ LPCTSTR lpName, _In_opt_ LPCTSTR lpValue );
创建进程(CreateProcess)
函数原型
BOOL WINAPI CreateProcess( _In_opt_ LPCTSTR lpApplicationName, _Inout_opt_ LPTSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCTSTR lpCurrentDirectory, _In_ LPSTARTUPINFO lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation);
函数概述
创建一个内核对象,进程内核对象并不是代表进程本身,它是操作系统用来管理这个进程的数据结构,该结构当中有一个使用计数,会在进程被创建时候设置为,然后系统会为新进程穿件一个虚拟地址空间,将所需的代码遗迹数据加载到进程的地址空间当众后,系统会为新进程的主线程创建一个线程内核对象,主线程最终会调用新进程的main函数.
如果系统成功创建了新的进程和主线程,函数会返回true.
参数详解
pszApplicationName
指定信进程要使用的执行体文件的名称
- 当在pszApplicationName中传一个文件路径时,如果给的是一个相对路径,那么他只会在当前目录(指的是exe所在的目录)下来进行查找
- 当这个值为NULL时,pszCommandLine会被作为命令行参数传
pszCommandLine
传给新进程的命令行字符串
如果pszApplicationName为NULL,我们可以在pszCommandLine中传递一个文件的路径,如果我们传的的是一个相对路径,它会按照一下顺序来搜索:
- 所在目录
- 当前目录
- Windows系统目录(System32目录)
- Windows目录
- Path环境变量中列出的目录
pszCommandLine参数还有一个要注意的地方,CreateProcess内部会修改这个参数,所以我们不应该传入一个常量指针
psaProcess
设置进程内核对象的安全属性
psaThread
设置主线程内核对象的安全属性
bInheritHandles
是否被继承
fdwCreate
进程创建方式标志
CREATER_NO_WINDOW
标志指示系统不要为应用程序创建任何控制台窗口.可以使用这个标志来知行没有用户界面的控制台程序.
pvEnvironment
pvEnvironment参数指向一个内存块,其中包含了信进程要使用的环境字符串.大多是时候,这个参数传入的值都是NULL,这导致子进程继承其父进程使用的一组环境字符串.另外,还可以使用GetEnvironmentStrings函数.
pszCurDir
pszCurDir参数允许父进程设置子进程的当前驱动器和目录,如果这个参数为NULL,则新进程的工作目录与新进程的应用程序一样.
psiStartInfo
typedef struct _STARTUPINFO { DWORD cb; //初始化为sizeof ( STARTUPINFO ) PSTR lpReserved; //保留。必须初始化为N U L L PSTR lpDesktop; //启动程序的桌面名称 PSTR lpTitle; //用于设定控制台窗口的名称。 DWORD dwX; //屏幕位子 DWORD dwY; //屏幕位子 DWORD dwXSize; //屏幕宽度 DWORD dwYSize; // 屏幕高度 DWORD dwXCountChars; //控制台可用,控制台宽度 DWORD dwYCountChars; //控制台可用,控制台高度 DWORD dwFillAttribute; //用于设定子应用程序的控制台窗口使用的文本和背景颜色 DWORD dwFlags; //组合值 WORD wShowWindow; //窗口可用,窗口如何显示 WORD cbReserved2; //保留。必须被初始化为0 PBYTE lpReserved2; //保留。必须被初始化为N U L L HANDLE hStdInput; //设置标准输入 HANDLE hStdOutput; //设置标准输出 HANDLE hStdError; //设置标准错误输出 } STARTUPINFO, *LPSTARTUPINFO; //dwFlags STARTF_USESIZE // 使用dwXSize 和dwYSize 成员 STARTF_USESHOWWINDOW //使用wShowWindow 成员 STARTF_USEPOSITION //使用dwX 和dwY 成员 STARTF_USECOUNTCHARS //使用dwXCountChars 和dwYCount Chars 成员 STARTF_USEFILLATTRIBUTE //使用dwFillAttribute 成员 STARTF_USESTDHANDLES //使用hStdInput 、hStdOutput 和hStdError 成员 STARTF_RUNFULLSCREEN //强制在x86 计算机上运行的控制台应用程序以全屏幕方式启动运行 STARTF_FORCEOFFFEEDBACK //改变鼠标为等待 STARTF_FORCEONFEEDBACK //不改变鼠标为等待 STARTF_PREVENTPINNING //窗口无法被固定在任务栏上 STARTF_TITLEISAPPID //设置任务栏和开始菜单的状态
ppiProcInfo
typedef struct _PROCESS_INFORMATION {HANDLE hProcess;HANDLE hThread;DWORD dwProcessId;DWORD dwThreadId;} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
关闭进程
入口函数返回
最正确的方式,只有这样才能保证主线程的所有资源都已经被正确的释放
入口函数在返回时,为确保以下几件事情已经完成:
- 主线程所创建的任何对象都已经被正确的销毁
- 操作系统会正确的释放线程的堆栈
- 将进程的退出代码设置成入口函数的返回值
- 递减内核对象的使用计数
进程中的一个线程调用ExitProcess
不应调用此函数来结束进程,当函数被调用的时候会被强制结束进程,并将退出代码设置为uExitCode.但此时的线程并未正确结束,会导致线程无法被正确清理
VOID ExitProcess(UINTuExitCode);
另一个进程中的线程调用TerminateProcess
不应该用此函数来结束进程,此函数能够结束其它进程
BOOL TerminateProcess( HANDLE hProcess, DWORD uExitCode)
进程中的所有线程都自然死亡
很少碰到这种情况,理论纯在
引用计数
- 内核对象的清理,取决内核对象里面的引用计数,内核对象里面的引用计数不为0,内核对象就不会被清理.如果的使内核对象堆积,不被清理,会导致内核所使用的内存不足,从而导致系统崩溃.
- 比如上述ExitProcess函数,使进程退出了,但不会改变进程的内核对象里面的引用计数,从而导致这个进程被强制退出了,但是这个进程的内核对象没有被清理.
- 我们来想想这个情景,如果我们在进程A中,用CreateProcess创建了一个进程B,那么当进程B退出时,B进程的退出代码会被得到吗?既然这个进程消亡了,那么这个进程的所有东西都没意义了,内核对象没了,退出代码获取不到.其实不然.
- CreateProcess函数里面存在的坑.当该函数创建了一个进程后,这个进程的引用计数被设置成了2.因为在创建了一个进程,这个内核对象的引用计数被加1,然后在给我们的PROCESS_INFOMATION赋值时也会给引用计数加1.
- 这样当上述B进程结束时,B进程的内核对象的引用计数就会被减1,被设置为1,没有被释放,B进程退出后,我们仍可以使用B的内核对象获取B进程的退出代码了.
- 然后再将B进程的内核对象引用计数减1,使用CloseHandle.这样B的引用计数就成了0,从而导致内核对象被释放了.
进程权限
DOS
- 在DOS下,直接操作物理内存,操作系统也好,进程也好,处于同一等级.
- 会造成系统不稳定
- 进程毫无保密性
虚拟内存
- 杜绝了操作系统物理内存或是操作系统代码的可能,保证了系统的稳定性
- 进程是完全封闭的,A进程,B进程毫无关联.但是我们软件需求,导致进程开放了一些接口
- 在开放了这些接口后,你必须要拥有一定的权限(用户赋予),才能进行高危操作
XP
- 在XP下,权限被分为管理员权限和用户权限.
- 分了之后发现这种划分并不明显,有些软件在进行操作时,会非常麻烦
- 这就会导致了,电脑上的大多数软件都拥有管理员权限,这就造成权限划分无意义.
- 在进程中有这样一个特性,一个拥有管理员权限的进程,创建了一个子进程,那么这个子进程就继承父进程的权限,拥有了管理员权限.以前就有网页木马利用这个机制,比如,IE(管理员权限)支持一个插件功能,它会有自动下载和安装功能,因为IE会为此创建一个进程,那么这个插件也会继承管理员权限,也不会有提示
UAC
- 带过滤表的权限
- UAC不会拥有管理员的所有操作
- 如果要求进程提升权限,那么必须在进程的边界上进行提升.进程边界是指的是在进程启动之时,来提升权限对进程进行提权,操作都是单次的
通过RT_MANIFEST提升权限
值 效果 requireAdministrator 程序必须以管理员权限启动;否则不会运行 highestAvailable 程序以当前可用的高权限运行.如果用户使用一个管理管理账户登录,会出现要求批准提升权限的一个对话框.如果用户使用亿个普通用户账户登录,应用程序就用这些标准权限来启动(不会提示用户提升权限) asinvoker 程序使用与主调程序一样的权限来启动提权操作
BOOL ShellExecuteEx(_Inout_ SHELLEXECUTEINFO *pExecInfo);typedef struct _SHELLEXECUTEINFO {DWORD cbSize;ULONG fMask;HWND hwnd;LPCTSTR lpVerb;LPCTSTR lpFile;LPCTSTR lpParameters;LPCTSTR lpDirectory;int nShow;HINSTANCE hInstApp;LPVOID lpIDList;LPCTSTR lpClass;HKEY hkeyClass;DWORD dwHotKey;union {HANDLE hIcon;HANDLE hMonitor;} DUMMYUNIONNAME;HANDLE hProcess;} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
权限继承
管理员程序->CreateProcess->子程序正常运行(继承提升后的权限)
UAC程序->CreatrProcee->子程序执行失败(ERROR_ELEVATION_REQUIRED)
- Windows学习(007)--进程
- Windows 进程APC学习笔记
- windows学习笔记2进程
- Windows API 函数学习(12)---进程
- 学习windows powershell(进程和服务)
- [学习记录] windows下多进程实例
- WINDOWS API学习笔记之创建进程
- windows API学习笔记 之 结束进程
- windows进程
- windows 进程
- windows进程
- windows进程
- windows进程
- Windows进程
- windows 进程
- windows - 进程
- Windows进程
- windows----进程
- GCC编译器一些参数区别(-I -l -L)
- POJ 1732 Phone numbers 笔记
- Linux下ipv6配置系列一:如何配置Linux系统ipv6环境
- C语言基础
- django ajax提交评论并自动刷新功能的实现
- Windows学习(007)--进程
- QTP参数化
- opencv中怎么修改图像大小,增加几行
- TensorFlow入门(二)——基本用法
- Hibernate中表与表之间关系的处理(一对多)
- C++ string append方法的常用用法
- 设计模式-单例模式的Java代码体现Runtime类
- 初学Vue-cli
- 【计算机视觉】全景相机标定(MATLAB/opencv)