Win32程序(五)
来源:互联网 发布:在淘宝怎么开店 编辑:程序博客网 时间:2024/05/20 22:37
进程与线程(process and thread)
我们习惯以进程(process)表示一个执行中的程序,并且认为它是CPU调度单位,事实上线程才是调度单位。
核心对象
首先让我解释什么叫做”核心对象“(kernel object)。你可以说核心对象是系统的一种资源,系统对象一旦产生,任何应用程序都可以开启并使用该对象。系统给予核心对象一个计数值(usage count)作为管理之用。核心对象包括下列几种:
核心对象产生方法eventCreateEventmutexCreateMutexsemaphoreCreateSemaphorefileCreateFilefile-mappingCreateFileMappingprocessCreateProcessthreadCreateThread前三者用于线程的同步化:file-mapping对象用于内存映射文件(memory mapping file),process和thread对象则是本节的主角。这些核心对象的产生方式(也就是我们所使用的API)不同,但都会获得一个handle作为识别;每被使用一次,其对应的计数值就加1,核心对象的结束方式相当一致,调用CloseHandle即可。”process对象"只是一个数据结构,系统用它来管理进程。
一个进程的诞生与死亡
执行一个程序,必然产生一个进程(process)。最直接的程序执行方式就是在shell中以鼠标双击某一个可执行文件图标,执行起来的APP进程其实是shell(如Windows9x的资源管理器)调用CreateProcess激活的。让我们看看整个流程:
- shell调用CreateProcess激活App.exe。
- 系统产生一个”进程核心对象“,计数值为1。
- 系统为此进程建立一个4GB的地址空间。
- 加载器为必要的代码加载到上述地址空间中,包括App.exe的程序、数据、以及所需的动态链接数据库(DLLs)。加载器如何知道要加载哪些DLLs呢?它们被记录在可执行文件(PE文件格式)的.idata section中。
- 系统为此进程建立一个线程,称为主线程(primary thread)。线程才是CPU时间的分配对象。
- 系统调用C runtime函数库的Startup code。
- Startup code 调用 App程序的 WinMain函数。
- App程序开始运行。
- 使用者关闭App主窗口,使WinMain中的消息循环结束掉,于是WinMain结束。
- 回到Startup code
- 回到系统,系统调用ExitProcess结束进程。
产生子进程
你可以写一个程序,专门用来激活其他的程序。关键就在于你会不会使用CreateProcess。这个API函数有众多参数:
CreateProcess(LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTP lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INTORMATION lpProcessInformation
);
第一个参数 lpApplicationName指定可执行文件名,第二个参数lpCommandLine指定欲传给新进程的命令行(command line)参数。如果你指定了lpApplicationName,但没有扩展名,系统并不会主动为你加上.EXE扩展名;如果没有指定完整路径,系统就只在当前工作目录中寻找,但如果你指定lpApplicationName,为NULL的话,系统会以lpCommandLine的第一个”段落“(我的意思其实是术语中所谓的token)作为可执行文件名;如果这个我文件名没有制定的扩展名,就采用默认的”.EXE“扩展名;如果没有指定路径,Windows就依照五个搜寻路径来寻找可执行文件,分别是:
- 调用者的可执行文件所在目录
- 调用者的当前工作目录
- Windows目录
- Windows System目录
- 环境变量中的path所设定的各目录
建立新进程之前,系统必须做出两个核心对象,也就是“进程对象”和“线程对象”。CreateProcess的第三个参数和第四个参数分别指定这两个核心对象的安全属性。至于第五个参数(TRUE或FALSE)则用来设定这些安全属性是否要被继承。
第六个参数dwCreationFlags可以是许多常数的组合,会影响到进程的建立过程。这些常数中比较常用的是CREATE_SUSPENDED,它使得子进程产生之后,其主线程立刻被暂停执行。
第七个参数lpEnvironment可以指定进程所使用的环境变量区。通常我们会让子进程继承父进程的环境变量,那么这里要指定NULL。
第八个参数lpCurrentDirectory用来设定子进程的工作目录与工作驱动器。如果指定NULl,子进程就会使用父进程的工作目录与工作驱动器。
第九个参数lpStartupInfo是一个指向STARTUPINFO结构的指针。这是一个放大的结构,可以用来设定窗口的标题、位置与大小,详情请看API使用手册。
最后一个参数是一个指向PROCESS_INFORMATION结构的指针:
typedef struct _PROCESS_INFORMATION
{
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
当系统为我们产生“进程对象“和”线程对象“时,他会把两个对象的handle填入此结构的相应字段中,应用程序可以从这里获得这些handles。
如果一个进程想结束自己的生命,只要调用:
VOID ExitProcess(UINT fuExitCode);
就可以了。如果一个进程想结束另一个进程的生命,可以使用:
BOOL TerminateProcess(HANDLE hProcess, UINT fuExitcode);
很显然,只要你有某个进程handle,就可以结束他的生命。TerminateProcess并不被建议使用,倒不是因为它权力太大,而是一般结束进程时,系统会通知该进程所开启(所使用)的所有DLLs,但如果你以TerminateProcess结束一个进程,系统不会做这件事,而这恐怕不是你所希望的。
前面我曾说过所谓”“割断脐带”这件事情,这要你把子进程以CloseHandle关闭,就达到了目的。下面就是例子:
PROCESS_INFORMATION ProcInfo;
BOOL fSuccess;
fScuuess=CreateProcess(...,&ProcInfo);
if(fSuccess){
CloseHandle(ProcInfo.hThread);
CloseHandle(ProcInfo.hProcess);
}
一个线程的诞生与死亡
执行程序代码,是线程的工作。当一个线程建立起来后,主线程也产生。所以每一个Windows程序一开始就有了一个线程。我们可以调用CreateThread产生额外的线程,系统会帮我们完成下列事情:
- 配置“线程对象”,其handle将成为CreateThread的返回值。
- 设定计数值为1.
- 配置线程的context。
- 保留线程的堆栈。
- 将context中的堆栈指针缓存器(SS)和指令指针缓存器(IP)设定妥当。
程序若欲产生一个新线程,调用CreateThread即可办到:
CreateThread(LPSECURITY_ATTRIBUTES
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
第一个参数表示安全属性的设定以及继承,请参考API手册。Windows 9x忽略这一参数。第二个参数设定堆栈的大小。第三个参数设定“线程函数”名称,而该函数的参数则由这里的第四个参数设定。第五个参数如果是0,表示让线程立刻开始执行,如果是CREATE_SUSPENDED,则是要求线程暂停执行(那么我们必须调用ResumeThread才能令其重新开始)。最后一个参数是一个指向DWORD的指针,系统会把线程的ID放在这里。
线程的结束有两种情况,一种是寿终正寝,一种是未得善终。前者是线程函数正常终结退出,那么线程也就自然而然终结了。这时候系统会调用ExitThread做些善后清理工作(其实线程中也可以自行调用此函数以结束自己)。但是若线程函数是一个无穷循环,则有两种方法终结此线程。一是进程结束,二是别的线程强制以TerminateProcess将它终结掉。
线程优先级(priority)
优先级是线程调度的重要依据。优先级高的线程,永远先获得CPU的青睐。操作系统会是视情况调整各个线程的优先级。例如前台线程的优先级应该调高些,后台线程的优先级应该调低些。
线程的优先级范围从0(最低)到31(最高)。当你产生线程时,并不是直接以数值指定其优先级,而是采用两个步骤。第一个步骤是指定“优先级等级(priority Class)”给线程,第二个步骤是指定“相对优先级”给该进程所拥有的线程。下表是对优先级的描述,
等级代码优先级值idleIDLE_PRIORITY_CLASS4normalNORMAL_PRIORITY_CLASS9(前台)或7(后台)highHIGH_PRIORITY_CLASS13realtimeREALTIME_PRIORITY_CLASS24其中的代码在CreateProcess的dwCreationFlags参数中指定。如果你不指定,系统默认给的是NORMAL_PRIORITY_CLASS,除非父进程是IDLE_PRIORITY_CLASS(那么子进程也会是IDLE_PRIORITY_CLASS)。- “idle”等级只有在CPU时间将被浪费掉时(就是之前所说的空闲时间)才执行。该等级最适合于系统监视软件,或屏幕保护软件。
- “normal”是默认等级。系统可以动态改变优先级,但只限于“normal”等级。当进程变成前台时进程优先级提升为9,当进程变成后台时,优先级降低为7。
- “high”等级是为了满足立即反应的需要,例如使用者按下Ctrl+ESC时立刻把工作管理器(task manager)带出场。
- “realtime”等级几乎不会被一般的应用程序使用。就连系统中控制鼠标、键盘、驱动器状态重新扫描、Ctrl+Alt+Del等的线程都比“realtime”的优先级还低。这种等级使用在“如果不在某个时间范围内被执行的话,数据就要遗失”的情况。这个等级一定得在正确评估之下使用,如果你把这样的等级指定给一般的(并不会常常被阻塞的)线程,多任务环境恐怕会瘫痪,因为这个线程有如此高的优先级,其他线程再没有机会被执行。
- Win32程序(五)
- win32程序
- win32程序
- Win32汇编教程五
- Win32 Windows编程 五
- win32(五)
- Win32控制台程序与Win32程序
- Win32 线程知识点梳理五
- Win32程序模型
- win32程序基础
- win32 更改程序图标
- Win32程序使用GDI+
- 学习win32程序步骤
- 第一个WIN32程序
- Win32程序(2) 菜单
- win32基础程序
- Win32 Asm 窗口程序
- Win32应运程序
- Win7系统应用技巧集锦
- 三维偏序
- java中Class对象详解
- WebService学习总结(一)——WebService的相关概念
- 操作系统的安装与启动基本原理
- Win32程序(五)
- HDOJ 1040 As Easy As A+B
- ckeditor的配置信息
- 为什么rvalue reference是lvalue
- WebService学习总结(三)——使用JDK开发WebService
- leetcode 177 Nth Highest Salary
- 写给我的2014,无良,无底线的老板
- Ueditor和CKeditor 两款编辑器的使用与配置
- 自定义log4j的appender