程序、进程和线程
程序是计算机指令的集合,以文件的形式存储于磁盘上。
进程:通常被定义为一个正在运行的程序的实例,是一个程序在其自身地址空间的一次执行活动。
一个程序可以有多个进程(如打开多个记事本程序),一个进程也可以同时访问多个程序。
进程是资源申请、调度和独立运行的单位,因此,它使用的是系统的运行资源;而程序不是,不占用系统的运行资源。
进程由两部分组成:
1.操作系统用来管理进程的内核对象。内核对象:操作系统内部分配的一个内存块,数据结构,不能被应用程序直接访问,需通过windows提供的函数操作。
2.地址空间。包含所有可执行模块或DLL模块的代码和数据,还包含动态内存分配的空间,如线程堆栈和堆分配空间。
进程是不活泼的,从来不执行任何东西,只是线程的容器。若要是进程完成某项操作,必须有一个在它环境中运行的线程。此线程负责执行包含在进程地址空间的代码。(进程只是线程的执行环境)
单个进程可以包含多个线程,这些线程一起“同时”执行进程地址空间中的代码。
每个进程至少拥有一个线程,来执行进程的地址空间的代码。当创建一个进程时,操作系统会自动创建该进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。
进程地址空间
系统赋予每个进程独立的虚拟地址空间。对于32位的进程来说,这个地址空间为4GB。
每个进程有它自己的私有地址空间。如,进程A、B均在地址0x12345678上存有数据结构,但A不能访问B,B也不能访问A的数据结构。
4GB是虚拟的地址空间,只是内存的一个范围。在能成功地访问数据不会出现非法访问之前,必须赋予物理存储器(物理内存+页文件),或将物理存储器映射到各个部分的地址空间。
4GB虚拟地址空间中,2GB是内核方式分区,供内核代码、设备驱动程序、设备I/O高速缓冲、非页面内存池的分配和进程页面表的使用呢等,而用户分区使用的地址空间为2GB-64KB,此分区为进程的私有地址空间所在的地方,一个进程不能读取、写入或者以任何方式访问驻留在该分区的另一个进程的数据,对于所有的应用程序来说,该分区是维护进程大部分数据的地方。其余的64KB地址空间为空指针使用的分区。
线程的构成:
1.线程的内核对象。操作系统用它对线程实施管理。
2.线程堆栈。用于维护线程在执行代码时所需要的所有参数和局部变量。
线程运行
操作系统为每一个运行的线程安排一定的CPU时间--时间片。线程在自己的时间段内运行,时间片终止的话,操作系统就会选择另外一个线程继续运行。
系统通过一种循环的方式为线程提供时间片,线程在自己的时间片内运行,时间片很短,给用户的感觉就是同时运行一样。
如果计算机拥有多个CPU,线程就能真正意义上的同时运行。
关于多进程和多线程
多线程好处:每个线程均独立的完成一个任务。移植到多CPU后,就可以并发运行
使用多进程的话,每个进程需系统分配私有的4GB的虚拟地址空间。多线程共享同一进程的地址空间。
互斥对象(唯一一个与线程内核相关的对象:可将互斥对象的ID设为线程ID)
互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。
互斥对象包含一个使用数量,一个线程ID和一个计数器。
ID用于标识系统中哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
互斥对象作用:一个线程在一个时间段内对一种资源进行的访问的时候,其他的线程不能在该时间段内对该资源进行访问
(包含共享资源)
ReleaseMutex不能释放不同线程的互斥对象;通过判断线程与互斥对象的ID是否相等,不等则不能释放
哪个线程拥有互斥对象,就应该是那个线程去释放该互斥对象。
互斥对象保护全局变量
结果线程交替:
操作系统会在某线程结束后,会自动将线程所拥有的互斥对象的线程ID设为0,并将级数清零。操作系统维护了线程和与线程相关的互斥对象的信息,知道是哪个线程终止,并清理。
命名的互斥对象(只运行一个实例)
hMutex = CreateMutex(NULL,TRUE,"tickets"); //若此处不具名,则后面不起作用 if (hMutex) { if (ERROR_ALREADY_EXISTS == GetLastError() ) { cout<<"only one instance can run!"<<endl; return ; } }