您的位置:首页 > 其它

进程概述及创建(VC_Win32)

2012-11-04 19:07 435 查看

进程概述

程序:

定义:  计算机指令集合,它以文件的形式存储在磁盘上
与进程关系:  一个程序可以对应多个进程

定义:

通常被定义为一个正在运行的程序实例,是一个程序在其自身的地址空间中的一次执行活动
组成:

内核对象:  内核对象也是用系统用来存放进程的统计信息的地方.内核对象是操作系统内部分配的一个内存块,该内存块是一种数据结构,其成员维护该对象的各种信息.由于内核对象的数据结构只能被内核访问使用,因此应用程序在内存中无法找到该数据结构,并直接改变其内容,只能通过windows提供的一些函数来实现内核对象的操作
地址空间:  它包含所有课执行模块或dll模块的代码和数据,另外,它也包含动态分配的空间

注意:

进程重来不执行任何东西,它只是线程的容器,若要使进程完成某项操作,它必须拥有一个在它环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码,也就是说,真正完成代码执行的是线程,而进程只是线程的容器,或者说进程的执行环境
单个进程可能包含若干个子线程,这些线程都是"同时"执行底层空间中的代码,每个进程都至少拥有一个线程,来执行进程的地址空间中代码,当创建一个进程空间时,操作系统会自动创建这个进程的第一个线程,也就是主线程,也就是执行main函数huowinmain函数的线程,可以把main函数或winmain函数看做是主线程的进入点函数,此后,主线程可以创建其他线程

进程地址空间:

系统赋予每个进程独立的虚拟的地址空间,每个进程都有自己的私有的地址空间,各自的线程都可以访问各自进程地址空间中的数据,但是一般情况下各个进程的线程是无法直接访问其他进程的地址空间的数据

创建进程函数详解

函数原型

BOOL CreateProcess(
LPCTSTR lpApplicationName,                 // name of executable module
LPTSTR lpCommandLine,                      // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
BOOL bInheritHandles,                      // handle inheritance option
DWORD dwCreationFlags,                     // creation flags
LPVOID lpEnvironment,                      // new environment block
LPCTSTR lpCurrentDirectory,                // current directory name
LPSTARTUPINFO lpStartupInfo,               // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);


参数说明:

lpapplicationname:

指向一个NULL结尾的、用来指定可执行模块的字符串.这个字符串可以使可执行模块的绝对路径,也可以是相对路径,在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径.

这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于 lpcommandline 参数的最前面并由空格符与后面的字符分开.这个被指定的模块可以是一个win32应用程序.

如果适当的子系统在当前计算机上可用的话,它也可以是其他类型的模块(如ms-dos 或 os/2).在windows nt中.

如果可执行模块是一个16位的应用程序,那么这个参数应该被设置为NULL并且因该在lpcommandline参数中指定可执行模块的名称.16位的应用程序是以dos虚拟机或win32上的windows(wow) 为进程的方式运行.

lpcommandline:

指向一个NULL结尾的、用来指定要运行的命令行.这个参数可以为空,那么函数将使用参数指定的字符串当作要运行的程序的命令行.

如果lpapplicationname和lpcommandline参数都不为空,那么lpapplicationname参数指定将要被运行的模块,lpcommandline参数指定将被运行的模块的命令行.新运行的进程可以使用getcommandline函数获得整个命令行.c语言程序可以使用argc和argv参数.

如果lpapplicationname参数为空,那么这个字符串中的第一个被空格分隔的要素指定可执行模块名.

如果文件名不包含扩展名,那么.exe将被假定为默认的扩展名.

如果文件名以一个点(.)结尾且没有扩展名,或文件名中包含路径,.exe将不会被加到后面.

如果文件名中不包含路径,windows将按照如下顺序寻找这个可执行文件:

当前应用程序的目录.
父进程的目录.
windows 95:windows系统目录,可以使用getsystemdirectory函数获得. windows nt:32位windows系统目录.可以使用getsystemdirectory函数获得,目录名是system32.
在windows nt中:16位windows系统目录.不可以使用win32函数获得这个目录,但是它会被搜索,目录名是system.
windows目录.可以使用getwindowsdirectory函数获得这个目录.
列在path环境变量中的目录.

如果被创建的进程是一个以ms-dos或16位windows为基础的应用程序,lpcommandline参数应该是一个以可执行文件的文件名作为第一个要素的绝对路径,因为这样做可以使32位windows程序工作的很好,这样设置lpcommandline参数是最强壮的.

lpprocessattributes:

指向一个security_attributes结构体,这个结构体决定是否返回的句柄可以被子进程继承.

如果lpprocessattributes参数为空(NULL),那么句柄不能被继承.在windows nt中:security_attributes结构的lpsecuritydescriptor成员指定了新进程的安全描述符.

如果参数为空,新进程使用默认的安全描述符.在windows95中:security_attributes结构的lpsecuritydescriptor成员被忽略.

lpthreadattributes:

指向一个security_attributes结构体,这个结构体决定是否返回的句柄可以被子进程继承.

如果lpthreadattributes参数为空(NULL),那么句柄不能被继承.在windows nt中,security_attributes结构的lpsecuritydescriptor成员指定了主线程的安全描述符.

如果参数为空,主线程使用默认的安全描述符.在windows95中:security_attributes结构的lpsecuritydescriptor成员被忽略.

binherithandles:

指示新进程是否从调用进程处继承了句柄.

如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承.被继承的句柄与原进程拥有完全相同的值和访问权限.

dwcreationflags:

指定附加的、用来控制优先类和进程的创建的标志.以下的创建标志可以以除下面列出的方式外的任何方式组合后指定.

进程创建标志

create_default_error_mode:  新的进程不继承调用进程的错误模式.createprocess函数赋予新进程当前的默认错误模式作为替代.应用程序可以调用seterrormode函数设置当前的默认错误模式.这个标志对于那些运行在没有硬件错误环境下的多线程外壳程序是十分有用的.对于createprocess函数,默认的行为是为新进程继承调用者的错误模式.设置这个标志以改变默认的处理方式.
create_new_console:  新的进程将使用一个新的控制台,而不是继承父进程的控制台.这个标志不能与detached_process标志一起使用.
create_new_process_group:  新进程将使一个进程树的根进程.进程树种的全部进程都是根进程的子进程.新进程树的用户标识符与这个进程的标识符是相同的,由lpprocessinformation参数返回.进程树经常使用generateconsolectrlevent函数允许发送ctrl+c或ctrl+break信号到一组控制台进程.
create_separate_wow_vdm:  (只适用于windows nt)这个标志只有当运行一个16位的windows应用程序时才是有效的.

如果被设置,新进程将会在一个私有的虚拟dos机(vdm)中运行.另外,默认情况下所有的16位windows应用程序都会在同一个共享的vdm中以线程的方式运行.单独运行一个16位程序的优点是一个应用程序的崩溃只会结束这一个vdm的运行;其他那些在不同vdm中运行的程序会继续正常的运行.同样的,在不同vdm中运行的16位windows应用程序拥有不同的输入队列,这意味着如果一个程序暂时失去响应,在独立的vdm中的应用程序能够继续获得输入.
create_shared_wow_vdm:  (只适用于windows nt)这个标志只有当运行一个16位的windows应用程序时才是有效的.如果win.ini中的windows段的defaultseparatevdm选项被设置为真,这个标识使得createprocess函数越过这个选项并在共享的虚拟dos机中运行新进程.
create_suspended:  新进程的主线程会以暂停的状态被创建,直到调用resumethread函数被调用时才运行.
create_unicode_environment:  如果被设置,由lpenvironment参数指定的环境块使用unicode字符.如果为空,环境块使用ansi字符.
debug_process:  如果这个标志被设置,调用进程将被当作一个调试程序,并且新进程会被当作被调试的进程.系统把被调试程序发生的所有调试事件通知给调试器.如果你使用这个标志创建进程,只有调用进程(调用createprocess函数的进程)可以调用waitfordebugevent函数.
debug_only_this_process:  如果此标志没有被设置且调用进程正在被调试,新进程将成为调试调用进程的调试器的另一个调试对象.如果调用进程没有被调试,有关调试的行为就不会产生.
detached_process:  对于控制台进程,新进程没有访问父进程控制台的权限.新进程可以通过allocconsole函数自己创建一个新的控制台.这个标志不可以与create_new_console标志一起使用.

进程优先级

dwcreationflags参数还用来控制新进程的优先类,优先类用来决定此进程的线程调度的优先级.如果下面的优先级类标志都没有被指定,那么默认的优先类是normal_priority_class,除非被创建的进程是idle_priority_class.在这种情况下子进程的默认优先类是idle_priority_class.可以下面的标志中的一个:

high_priority_class :  指示这个进程将执行时间临界的任务,所以它必须被立即运行以保证正确.这个优先级的程序优先于正常优先级或空闲优先级的程序.一个例子是windows任务列表,为了保证当用户调用时可以立刻响应,放弃了对系统负荷的考虑.确保在使用高优先级时应该足够谨慎,因为一个高优先级的cpu关联应用程序可以占用几乎全部的cpu可用时间.
idle_priority_class :  指示这个进程的线程只有在系统空闲时才会运行并且可以被任何高优先级的任务打断.例如屏幕保护程序.空闲优先级会被子进程继承.
normal_priority_class :  指示这个进程没有特殊的任务调度要求.
realtime_priority_class :  指示这个进程拥有可用的最高优先级.一个拥有实时优先级的进程的线程可以打断所有其他进程线程的执行,包括正在执行重要任务的系统进程.例如,一个执行时间稍长一点的实时进程可能导致磁盘缓存不足或鼠标反映迟钝.

lpenvironment:

指向一个新进程的环境块.

如果此参数为空,新进程使用调用进程的环境.一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的.每个字符串都是name=value的形式.因为相等标志被当作分隔符,所以它不能被环境变量当作变量名.与其使用应用程序提供的环境块,不如直接把这个参数设为空,系统驱动器上的当前目录信息不会被自动传递给新创建的进程.对于这个情况的探讨和如何处理,请参见注释一节.环境块可以包含unicode或ansi字符.

如果lpenvironment指向的环境块包含unicode字符,那么dwcreationflags字段的create_unicode_environment标志将被设置.

如果块包含ansi字符,该标志将被清空.请注意一个ansi环境块是由两个零字节结束的:一个是字符串的结尾,另一个用来结束这个快.一个unicode环境块石油四个零字节结束的:两个代表字符串结束,另两个用来结束块.

lpcurrentdirectory:

指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径.这个字符串必须是一个包含驱动器名的绝对路径.如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录.这个选项是一个需要启动启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件.

lpstartupinfo:

指向一个用于决定新进程的主窗体如何显示的startupinfo结构体.
//用于指定新进程的主窗口如何显示,成员参见msdn
typedef struct _STARTUPINFO {
DWORD   cb;
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;


lpprocessinformation:

指向一个用来接收新进程的识别信息的process_information结构体.
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; //进程句柄
HANDLE hThread; //主线程句柄
DWORD dwProcessId; //进程标识id
DWORD dwThreadId;  //主线程标识id
} PROCESS_INFORMATION;


返回值:

如果函数执行成功,返回非零值.
如果函数执行失败,返回零,可以使用getlasterror函数获得错误的附加信息

说明:

createprocess函数用来运行一个新程序.winexec和loadmodule函数依旧可用,但是它们同样通过调用createprocess函数实现.

另外createprocess函数除了创建一个进程,还创建一个线程对象.这个线程将连同一个已初始化了的堆栈一起被创建,堆栈的大小由可执行文件的文件头中的描述决定.线程由文件头处开始执行.

新进程和新线程的句柄被以全局访问权限创建.对于这两个句柄中的任一个,如果没有安全描述符,那么这个句柄就可以在任何需要句柄类型作为参数的函数中被使用.当提供安全描述符时,在接下来的时候当句柄被使用时,总是会先进行访问权限的检查,如果访问权限检查拒绝访问,请求的进程将不能使用这个句柄访问这个进程.

这个进程会被分配给一个32位的进程标识符.直到进程中止这个标识符都是有效的.它可以被用来标识这个进程,或在openprocess函数中被指定以打开这个进程的句柄.进程中被初始化了的线程一样会被分配一个32位的线程标识符.这个标识符直到县城中止都是有效的且可以用来在系统中唯一标识这个线程.这些标识符在process_information结构体中返回.

当在lpapplicationname或lpcommandline参数中指定应用程序名时,应用程序名中是否包含扩展名都不会影响运行,只有一种情况例外:一个以.com为扩展名的ms-dos程序或windows程序必须包含.com扩展名.

调用进程可以通过waitforinputidle函数来等待新进程完成它的初始化并等待用户输入.这对于父进程和子进程之间的同步是极其有用的,因为createprocess函数不会等待新进程完成它的初始化工作.举例来说,在试图与新进程关联的窗口之前,进程应该先调用waitforinputidle.

首选的结束一个进程的方式是调用exitprocess函数,因为这个函数通知这个进程的所有动态链接库(dlls)程序已进入结束状态.其他的结束进程的方法不会通知关联的动态链接库.注意当一个进程调用exitprocess时,这个进程的其他县城没有机会运行其他任何代码(包括关联动态链接库的终止代码).

exitprocess, exitthread, createthread, createremotethread,当一个进程启动时(调用了createprocess的结果)是在进程中序列化进行的.在一段地址空间中,同一时间内这些事件中只有一个可以发生.这意味着下面的限制将保留:

在进程启动和dll初始化阶段,新的线程可以被创建,但是直到进程的dll初始化完成前它们都不能开始运行.
在dll初始化或卸下例程中进程中只能有一个线程. *直到所有的线程都完成dll初始化或卸下后,exitprocess函数才返回.
在进程中的所有线程都终止且进程所有的句柄和它们的线程被通过调用closehandle函数终止前,进程会留在系统中.进程和主线程的句柄都必须通过调用closehandle函数关闭.如果不再需要这些句柄,最好在创建进程后立刻关闭它们.

当进程中最后一个线程终止时,下列的事件发生:

所有由进程打开的对象都会关闭.
进程的终止状态(由getexitcodeprocess函数返回)从它的初始值still_active变为最后一个结束的线程的结束状态.
主线程的线程对象被设置为标志状态,供其他等待这个对象的线程使用.
进程对象被设置为标志状态,供其他等待这个对象的线程使用.

假设当前在c盘上的目录是/msvc/mfc且有一个环境变量叫做c:,它的值是c:/msvc/mfc,就像前面lpenvironment中提到过的那样,这样的系统驱动器上的目录信息在createprocess函数的lpenvironment参数不为空时不会被自动传递到新进程里.一个应用程序必须手动地把当前目录信息传递到新的进程中.为了这样做,应用程序必须直接创建环境字符串,并把它们按字母顺序排列(因为windows nt和windows 95使用一种简略的环境变量),并把它们放进lpenvironment中指定的环境块中.类似的,他们要找到环境块的开头,又要重复一次前面提到的环境块的排序.

一种获得驱动器x的当前目录变量的方法是调用getfullpathname("x:",..).这避免了一个应用程序必须去扫描环境块.如果返回的绝对路径是x:/,就不需要把这个值当作一个环境数据去传递了,因为根目录是驱动器x上的新进程的默认当前目录.

由createprocess函数返回的句柄对于进程对象具有process_all_access的访问权限.

由lpcurrentdirectory参数指定的当前目录室子进程对象的当前目录.lpcommandline参数指定的第二个项目是父进程的当前目录.

对于windows nt,当一个进程在指定了create_new_process_group的情况下被创建时,一个对于setconsolectrlhandler(NULL,true)的调用被用在新的进程上,这意味着对新进程来说ctrl+c是无效的.这使得上层的外科程序可以自己处理ctrl+c信息并有选择的把这些信号传递给子进程.ctrl+break依旧有效,并可被用来中断进程/进程树的执行.

进程创建

创建过程:

定义两个结构体变量,用于新进程的主界面出现的样式和存放进程创建后的相关信息
创建进程
在进程创建后进行后续的清理工作

  具体流程图



注意:

在创建一个进程时,系统会为该进程建立一个警察内核对象和一个线程内核对象,而该内核对象有有一个计数器,系统会为这两个对象赋予初始的计数为1,在createprocess函数返回之前,它将打开创建的进程对象和线程对象,并将每个对象与进程和线程相关的句柄放在其最后一个参数:processinformation结构体变量的对应成员中.当createprocess函数在其内部打开这些对象时,每个对象的使用计数就变为2,如果在父进程中不需要使用子进程的这两个句柄则可以调用closehandle函数关闭它们,系统会将子进程的进程内核对象和线程对象的计数器减1,当子进程终止运行时,系统会将这些使用计数器减1,这时子进程的进程内核对象和线程内核对象都为0,这两个内核对象就能够被释放了,所以在编程中,当不需要这些内核对象时,总应该调用closehandle函数关闭它们
代码样例:

定义必要结构体变量:

//指定新进程的主界面出现的样式
STARTUPINFO sui;
//用于接收创建新进程后新进程的一些信息
PROCESS_INFORMATION pi;


初始化 startupinfo:

ZeroMemory(&sui,sizeof(STARTUPINFO));


创建进程:

//打开 vim 编辑器
CreateProcess("c:\\program files\\vim\\vim73\\gvim.exe",NULL,NULL,NULL,
true,0,NULL,NULL,&sui,&pi);


进程创建后的清理操作:

//关闭进程句柄
CloseHandle(pi.hProcess);
//关闭主线程句柄
CloseHandle(pi.hThread);


程序源码:

#include<windows.h>
#include<cstdlib>
using namespace std;
void main()
{
STARTUPINFO sui;
PROCESS_INFORMATION  pi;

ZeroMemory(&sui,sizeof(STARTUPINFO));

if(!CreateProcess("c:\\program files\\vim\\vim73\\gvim.exe",NULL,NULL,NULL,
true,0,NULL,NULL,&sui,&pi))
{
MessageBox(NULL,"创建子进程失败!","警告",MB_OK);
return;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
system("pause");
}


运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: