进程和线程基础知识整理

概念清楚,基础扎实比一切别的都重要!

 

首先要了解核心对象的概念。核心对象是系统的一种资源(这种说法对GDI对象也适用),系统对象一旦产生,任何应用程序都可以开启并使用该对象。系统给予核心对象一个计数值作为管理之用。核心对象包括下列数种:

核心对象
产生方法
event
CreateEvent
mutex
CreateMutex
semaphore
CreateSemaphore
file
CreateFile
file-mapping
CreateFileMapping
process
CreateProcess
thread
CreateThread

前三者用于线程的同步化:file-mapping对象用于内存映射文件(memory mapping file),这些核心对象的产生方式(也就是我们所用的API)不同,但都会获得一个handle作为识别;每次使用一次,其对应的计数值就加1。核心对象的结束访是相当一致,调用CloseHandle即可。
“process对象”只是一个数据结构,系统用它来管理进程。程序代码的执行是线程的工作。
        
        一个进程的诞生诞生与死亡
执行一个程序,必然就产生一个进程(process)。最直接的程序执行方式就是在shell(如Window的资源管理器)中以鼠标双击某一个可执行文件图标(假设其为App.exe),执行起来的App进程其实是shell调用CreateProcess激活的。整个流程为:
  1) Shell调用CreateProcess激活App.exe。
  2) 系统产生一个“进程核心对象”,计数值为1。
  3) 系统为此进程建立一个4GB地址空间。
  4) 加载器将必要的代码加载到上述地址空间,包括App.exe的程序、数据,以及所需的动态链接函数库(DLLs)。加载器如何知道要加载哪些DLLs呢?它们被记录在可执行文件(PE文件格式)的.idata section中。
  5) 系统为此进程建立一个线程,称为主线程(primary thread)。线程才是CPU时间的分配对象。
  6) 系统调用C runtime函数库的Startup code。
  7) Startup code调用App程序的WinMain函数。
  8) App程序开始执行。
  9) 使用者关闭App主窗口,使WinMain中的消息循环结束掉,于是WinMain结束。
  10) 回到Startup code。
  11)  回到系统,系统调用ExitProcess结束进程。
可以说,通过这种方式执行起来的所有Window程序,都是shell的子进程。本来,母进程与子进程之间可以有某些关系存在,但shell在调用CreateProcess时已经把母子之间的脐带剪断了。 
 
产生子进程
你可以写一个程序,专门用来激活其它的程序。关键在于CreateProcess的使用。
BOOL CreateProcess(
  LPCTSTR lpApplicationName,    
// 制定可执行文件名      
  LPTSTR lpCommandLine,          // 传递给新进程的命令行参数
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
在建立新进程之前,系统必须做出两个核心对象,也就是“进程对象”和“线程对象”。第三个参数和第四个参数 LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,分别指定这两个核心对象的安全属性。
第五个参数(TRUE或者FALSE)用来设定这些安全属性是否要被继承。
第六个参数 dwCreationFlags可以使许多常数的组合,会影响到进程的建立过程。这些常数中比较常用的是CREATE_SUSPENDED,它会使得子进程产生之后,其主线程立刻被暂停执行。
第七个参数 lpEnvironment可以制定进程所使用的环境变量区。通常我们会让子进程继承父进程的环境变量,那么这里要指定NULL。
第八个参数 lpCurrentDirectory用来设定子进程的工作目录与工作驱动器。如果指定为NULL,子进程就会使用父进程的工作目录与工作驱动器。
第九个参数 lpStartupInfo是一个指向STARTUPINFO结构的指针。这是一个庞大的结构,可以用来设定窗口的标题、位置与大小,详情参考API时用手册。
最后一个参数 lpProcessInformation是一个指向 PROCESS_INFORMATION结构的指针:
typedef  struct  _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD dwProcessId;
  DWORD dwThreadId;
} PROCESS_INFORMATION;
当系统为我们产生“进程对象”和“线程对象”时,它会把两个对象的handle填如此结构的相关字段中,应用程序可以从这里获得这些handles。
如果一个进程想结束自己的生命,只要调用:
VOID ExitProcess(
 UINT uExitCode // 传入的一个退出数值标记
);
uExitCode
[in] Exit code for the process and all threads terminated as a result of this call. Use the GetExitCodeProcess function to retrieve the process's exit value. Use the GetExitCodeThread function to retrieve a thread's exit value.
Exiting a process causes the following:
1. All of the object handles opened by the process are closed.
2. All of the threads in the process, except the calling thread, terminate their execution. The entry-point functions of all loaded dynamic-link libraries (DLLs) are called with DLL_PROCESS_DETACH. After all attached DLLs have executed any process termination code, this function terminates the current process, including the calling thread.
3. The state of the process object becomes signaled, satisfying any threads that had been waiting for the process to terminate.
4. The states of all threads of the process become signaled, satisfying any threads that had been waiting for the threads to terminate.
5. The termination status of the process changes from STILL_ACTIVE to the exit value of the process.
如果进程想结束另一个进程的生命,可以使用:
BOOL TerminateProcess(
 HANDLE hProcess,
 UINT uExitCode
);
if a process terminates by calling TerminateProcess, the DLLs that the process is attached to are not notified of the process termination.
Exiting a process does not cause child processes to be terminated.
Exiting a process does not necessarily remove the process object from the operating system. A process object is deleted when the last handle to the process is closed.
 
一个线程的诞生与死亡
执行程序代码,是线程的工作。当一个进程建立起来后,主线程也产生。所以每一个Window程序已开始就有了一个线程。我们可以调用CreateThread产生额外的线程,系统会帮助我们完成下列事情:
  1) 配置“线程对象”,其handle将成为CteateThread的返回值。
  2) 设置计数值为1。
  3) 配置线程的context。
  4) 保留线程的堆栈。
  5) 将context中的堆栈指针缓存器(SS)和指令指针缓存器(IP)设定妥当。
看看上面的态势,的确可以显示出线程是CPU分配时间的单位。所谓工作切换(context switch)其实就是对线程context的切换。
程序与产生一个新线程,调用CreateThread即可办到:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,   
// 安全属性的设定与继承
SIZE_T dwStackSize,                          // 设置堆栈的大小
LPTHREAD_START_ROUTINE lpStartAddress,       // 设定“线程函数”名称
LPVOID lpParameter,                          // 为“线程函数”提供参数
DWORD dwCreationFlags,   // 如果为“0”,表示让线程立克开始执行,如果是CREATE_SUSPENDED, 
                        
// 则是要求线程停止执行(必须调用ResumeThread才能令其重新开始)
LPDWORD lpThreadId                           // 返回线程id
);
上面的“线程函数”是什么呢?让我们看个实例:
VOID ReadTime(VOID);
HANDLE hThread;
DWORD ThreadID;
hThread 
=  CreateThread(NULL,  0 , (LPTHREAD_START_ROUTINE)ReadTime, NULL,  0 & ThreadID);
… …
// -------------------------------------------------------------------------------------------------------------------
// thread函数不断利用GetSystemTime取系统时间,
// 并将结果显示在对话框_hWndDlg的IDE_TIMER字段上。
// -------------------------------------------------------------------------------------------------------------------
VOID ReadTime(VOID)
{
    Char str[
50 ];
    SYSTEMTIME st;
    While(
1 ){
        GetSystemTime(
& st);
        Sprint(str, “
% u: % u: % u”, st.wHour, st.wMinute, st.wSecond);
        SetDlgItemText(_hWndDlg, IDE_TIMER, str);
        Sleep(
1000 );     // 延迟一秒
    }
}
 
当CreateThread成功时,系统为我们把一个线程应该有的东西都准备好。线程的主体在哪里呢?就是所谓的“线程函数”。线程与线程之间,不必考虑控制权释放的问题,因为Win32操作系统的特点是强制性多任务的。
线程的结束有两种情况,一种是寿终正寝,一种是未得善终。前者是线程函数正常结束退出,那么县城也就自然而然终结了。这时候系统会调用ExitThread做些善后清理工作(其实现中也可以自行调用此函数以结束自己)。但是像上面那个例子,线程根本就是个无穷循环,如何终结?一是进程结束(自然也就导致线程的结束),而是别的线程强制以TerminateThread将它终结掉。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值