20170602Windows09_进程

来源:互联网 发布:关闭淘宝手机支付 编辑:程序博客网 时间:2024/06/03 16:58

进程:

进程及线程:

1:进程是“惰性”的,进程本身只是划定了一块区域,这块区域的需要执行的代码都是静态存在的。进程做的任何事情,都必须让线程来执行,线程执行进程地址空间的代码。
2:一个进程可以拥有多个线程,所有的线程都能够在进程的地址空间中“同时运行代码”,每个线程都有他自己的一组CPU寄存器以及自己的栈。
3:每个进程都必须至少有一个线程,进程在创建的时候,会自动创建一个线程,称为主线程,如果没有线程要执行进程地址空间中的代码,进程就失去了存在的意义,此时,系统会自动销毁进程以及他的地址空间。
4:线程和进程实际不是包含关系,两者是分开的,线程是执行进程里面的代码,线程必须执行进程里面的代码才有意义,进程必须要有线程来执行才有意义。
5:Windows是一个多进程的操作系统,多进程在运行的时候,是多个进程里面的多个线程在运行。多进程的运行实际就是多线程的运行。
6:操作系统会以轮询的方式为每个线程分配CPU时间片,每个线程运行一个很短的事件,多个进程轮流运行,从而营造出同时运行的假象。
7:如果计算机拥有多个CPU,操作系统会以更复杂的方式为线程分配CPU时间片。
8:如果每个进程做一件事情,那么是不是线程和进程就一样了呢?实际并不是,CPU是以轮询的方式执行,如果是进程间的切换,其切换成本太大。
    线程之间的切换,是切换的线程上下文,进程切换是切换的进程上下文。进程上下文的切换需要进行大量的内存操作,需要将新进程的状态加载到内存,而线程上下文的切换,只需要保存到线程的状态。线程比进程更小,更方便、快捷。
9:进程在操作系统中有两个含义,1:内核对象,2:地址空间。进程地址空间在操作系统中进行了包装,包装成了内核对象,其内核对象也是代表地址空间。进程内核对象能做的就是地址空间能做的。

应用程序类型

1:控制台用户界面(CUI):通过文本方式交互的都称为CUI。控制台程序的入口函数为main函数。
2:图形用户界面(GUI):基于鼠标交互的界面。入口函数为WinMain。
3:GUI和CUI编译出来的都为程序,区别在于两者的呈现方式不同,使用CUI的程序,里面同样可以CreateWindow,也可以使用视窗等,即使使用的GUI,也可以新建或附加一个控制台,因为两者的本质都是程序,只是其便是方法不同,VS下新建控制台用户程序,他会在属性->链接器->系统->子系统中设置为SUBSYSTEM:CONSOLE,是编译的时候,我们给的一个附加选项,如果为SUBSYSTEM:CONSOLE,编译器就会找main函数,如果为SUBSYSTEM:WINDOWS,他就会找WinMain函数。如果不设置这个附加项,那么随便写什么都可以编译通过,他会自动地推导。如下图所示:

4:当设定了附加项后,编译器会将我们设定的这个值设定在生成文件的头部,操作系统在运行这个文件的时候,会检查这个位,如果检查到CONSOLE,他就会以CMD命令提示符工具来打开这个程序,让我们的程序附加到命令提示符这个软件上,Windows会拿命令提示符来运行,实际自己的程序是没有界面的,是用的命令提示符的。如果检查到WINDOWS的时候,他什么都不做,直接进行加载,因为这样的程序,窗口都是自己写的,不需要使用命令提示符的窗口。
5:我们写的main函数,里面只是实现了我们的逻辑,在这些逻辑之前,VS的链接器会链接一些程序进来,如果为GUI程序,则会使用WinMainCRTStartup这个函数来加载并启动我们的WinMain,如果为CUI程序,则会使用mainCRTStartup或wmainCRTStartup函数来启动我们的程序,这些都是编译器来做的一件事情。
6:在逆向中,首先就需要找到这个入口点,然后向下看。

进程句柄:

1:
#include <windows.h>#include <tchar.h>int WINAPI _tWinMain(//WINAPI,_stdcall,CALLBACK,三者都是一样的,实际都是_stdcallHINSTANCE hInstance,//进程句柄HINSTANCE ,//前一个进程句柄,在cpp中,这样就是只做声明不做定义。LPTSTR lpCmdline,//命令行int nCmdShow//显示方式,一般为SW_SHOW或者SW_HIDE){return 0;}

2:参数详解:
    1:hPrevInstance:前一个进程句柄,一般来说不要使用他,可以只做声明不做定义。
    2:hInstance:当前进程句柄,官方说明为:加载到进程地址空间的每一个执行体或者DLL文件都被赋予了一块独一无二的实例句柄。实际代表的是程序的基地址。可以这样理解:
    在整个程序中,包含了很多dll,图片等资源或者其他东西,而这个HINSTANCE就是指的exe所在的位置。其值每次运行可能不一样,可以在属性->链接器->高级中的随机基址设置成false即可,每次的基址都是固定的0x400000,默认都是0x400000。也可以自己设置固定的基址。设置固定基址生成程序后,多次运行这个程序,每一个的基地址都是一样的,因为每个进程都是独立的一块进程空间,这个基址与内存地址没有直接关系。
    在控制台下,获取hInstance可以使用GetModuleHandle()来获取。在窗口中,也可以这样获取,获取的值与传递进来的hInstance是一样的。
    3:nCmdLine:命令行,nCmdLine的控件是一直存在的,程序启动就已经分配好了,我们不能去更改他,但凡已更改(可以改但不应该改,也不应该去释放),整个全局的就被更改了。在程序调试的时候,可以在属性->调试->命令参数这里添加命令行。
    在控制台下,可以使用,GetCommandLine()函数来获取cmdline。返回的是PTSTR,即TCAHE*。
    多个commandline规则是以空格隔开,获取的commandline也是一个以空格隔开的字符串,在windows中,可以使用CommandLineToArgvW()函数将commandline以空格分解成多个单独的命令。示例如下:
int main(){HMODULE m = GetModuleHandle(NULL);printf("0x%x\n", m);//程序的基地址,就是程序开头的位置。TCHAR* cmdline = GetCommandLine();//printf("%ws\n", cmdline);//为宽字节,用printf不方便_tprintf(TEXT("%s"), cmdline);//定义在tchar.h中int nNumArgv;//用于保存有多少个cmd命令。TCHAR** ppcmdline = CommandLineToArgvW(cmdline, &nNumArgv);//会分割,返回的是已经分配好的内存,存在释放的问题。需使用LocalFree释放if (ppcmdline != NULL){for (int i = 1; i < nNumArgv; ++i)//不打印第一个地址{_tprintf(TEXT("%s\n"), ppcmdline[i]);//把每个命令打印在不同行。}}LocalFree(ppcmdline);return 0;}

3:HMODULE和HINSTANCE实际上都是HINSTANCE,在原来的16位系统下,两者有区别,但是都是很久以前的事了。

环境变量:

1:环境变量就是系统当中全局的一些commandline,里面放了很多系统指定的一些东西,例如:system处于什么位置,安装程序path可以在哪些地方,程序执行的时候找到path等。
2:可以使用GetEnvironmentStrings()函数来获取环境变量,获取完成之后需要使用FreeEnvironmentStrings()来释放环境变量。如果函数成功,返回值是指向当前进程的环境块的指针。只能获取当前进程块中的环境变量。如果要获取系统的环境变量,可以使用,getenv()函数,这个函数在stdlib.h中。
3:
int main(){LPTCH pstrPath = GetEnvironmentStrings();_tprintf(TEXT("%s"), pstrPath);FreeEnvironmentStrings(pstrPath);return 0;}

进程路径:

    在进行VS开发的时候,CreateFile可以传递绝对路径和相对路径,当为相对路径的时候,他会以所在目录下操作文件(并不一定是文件所在目录)
1:所在目录、可以使用GetCurrentDirectory()函数获取。
    所在目录是指当前文件运行的所在目录,VS调试的时候,指向的是main函数文件所在目录,直接打开debug下的exe,指向的就是exe所在目录(不包含文件名),程序里面建立文件传递的相对路径就会以这个路径为相对路径。
    可以使用SetCurrentDirectory()函数修改所在目录。修改后,直接创建文件(只含文件名,相对路径)默认就在这个目录下创建的。
2:当前目录、
    CDEF每个盘都有自己的当前目录。可以使用GetFullPathName()函数获取。
    当前目录,获取到的所在目录意外就为CDEF盘的目录,如果VS安装在D盘(或其他盘),那么获取这个盘的当前目录就是VS的安装目录,只有这一个盘的当前目录是特殊的。此外,设置所在目录对当前目录会有影响,如果所在目录设置为任何盘符下的路径,这个盘符的当前目录也会设置为这个路径。其他盘符的当前目录不会改变。
    当前目录常用于存放临时文件。
3:Modul所在目录、可以使用GetModuleFileName()函数获取。
    指向的一直是exe文件目录,包含文件名。
4:当前目录是在程序启动的时候,就设置了一个环境变量,环境变量里面分别存储了每个盘符的当前目录。如果更改了所在目录,那么当前目录会被更改,但是环境变量不会被更改,我们可以通过更改环境变量来更改当前目录。
    总结:所在目录和module目录只有一个,当前目录个数与盘符个数相同,在环境变量中保存(但并不一定取决于环境变量中保存的值)。更改所在目录会更改对应盘符的当前目录,但其他盘符的当前目录不会变,但不会更改环境变量中的当前目录。更改环境变量里面的当前目录会更改当前目录(需要在未设置所在目录在当前盘符下才可以,否则当前目录还是与所在目录一样,但是环境变量更改不会失败)。一个盘符的当前目录优先取决于所在目录(是否在这个盘符,如果在,这个盘符的当前目录一定为这个,不管环境变量是否有设置或者为其他值),其次才取决于环境变量。修改当前目录可以通过修改环境变量来修改,但所在目录会优先影响对应盘符的当前目录(使之与所在目录相同)。
5:测试代码:
int _tmain(int, TCHAR*){TCHAR strCurDir[MAX_PATH] = { 0 }, strFullPathName[MAX_PATH] = { 0 }, strModulPath[MAX_PATH] = { 0 }, strEnvValue[MAX_PATH] = { 0 };//所在目录测试GetCurrentDirectory(MAX_PATH, strCurDir);//获取所在目录,SetCurrentDirectory(TEXT("D:\\360Downloads"));GetCurrentDirectory(MAX_PATH, strCurDir);//获取所在目录,已更改为D:\\360Downloads_tprintf(TEXT(":%s\n"), &strCurDir);//获取当前目录,GetFullPathName(TEXT("D:"), MAX_PATH, strFullPathName, nullptr);//优先为所在目录路径,其次为VS的安装路径(如果安装在这个盘且未设置所在目录在这个盘),最后为盘符最外路径(eg:  C:\\)。//环境变量LPTCH pstrPath = GetEnvironmentStrings();_tprintf(TEXT("%s\n"), pstrPath);GetEnvironmentVariable(TEXT("=C:"), strEnvValue, MAX_PATH);_tprintf(TEXT("EnvStr-C:   %s\r\n"), strEnvValue);GetEnvironmentVariable(TEXT("=D:"), strEnvValue, MAX_PATH);_tprintf(TEXT("EnvStr-D:   %s\r\n"), strEnvValue);//当前目录测试SetEnvironmentVariable(TEXT("=D:"), TEXT("D:\\360WiFi"));GetFullPathName(TEXT("D:"), MAX_PATH, strFullPathName, nullptr);//获取当前目录,这里与所在目录一样SetCurrentDirectory(TEXT("E:\\"));//所在目录被更改到其他地方后GetFullPathName(TEXT("D:"), MAX_PATH, strFullPathName, nullptr);//当前目录会变为与环境变量中的一样GetEnvironmentVariable(TEXT("=D:"), strEnvValue, MAX_PATH);_tprintf(TEXT("EnvStr-D:   %s\r\n"), strEnvValue);//module目录GetModuleFileName(nullptr, strModulPath, MAX_PATH);//获取module目录_tprintf(TEXT("mod:%s\n"), &strModulPath);system("pause");return 0;}





原创粉丝点击