您的位置:首页 > 其它

Win32知识点整理(2)

2014-08-06 10:31 141 查看
1、动态库和静态库的区别?

1)静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

2)静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。

3)每一个lib文件就是若干函数(假设只有函数)的定义。

lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。

lib有静态lib和动态lib之分。

静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。

动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持。

2、Win32怎么使用静态库?

1)创建新的静态库项目。

从“文件”菜单中,选择“新建”,然后选择“项目…”。

在“项目类型”窗格中,选择“Visual C++”下的“Win32”。

在“模板”窗格中,选择“Win32 控制台应用程序”。

为项目选择一个名称,如“MathFuncsLib”,并将其输入“名称”字段。为解决方案选择一个名称,如“StaticLibrary”,并将其输入“解决方案名称”字段。

按“确定”启动“Win32 应用程序向导”。在“Win32 应用程序向导”对话框的“概述”页中,按“下一步”。

在“Win32 应用程序向导”的“应用程序设置”页中,选择“应用程序类型”下的“静态库”。

在“Win32 应用程序向导”的“应用程序设置”页中,清除“附加选项”下的“预编译头”复选框。

按“完成”创建项目。

2)在控制台应用程序中使用静态库的功能。

创建新的控制台应用程序后,该向导将为您创建一个空程序。源文件的名称与您在前面为项目选择的名称相同。在本示例中,名为“MyExecRefsLib.cpp”。

若要使用在静态库中创建的算术例程,必须引用该静态库。为此,请选择“项目”菜单中的“引用…”。在“属性页”对话框中,展开“通用属性”节点,并选择“引用”。然后选择“添加新引用…”按钮。有关“引用...”对话框的更多信息,请参见“<Projectname> 属性页”对话框 ->“通用属性”->“框架和引用”。

显示“添加引用”对话框。此对话框列出了所有可以引用的库。“项目”选项卡列出了当前解决方案中的所有项目,以及它们包含的所有库。在“项目”选项卡中,选择“MathFuncsLib”。然后选择“确定”。有关“添加引用”对话框的更多信息,请参见“添加引用”对话框。

若要引用静态库的头文件,必须修改包含目录路径。为此,请在“属性页”对话框中,展开“配置属性”节点,然后展开“C/C++”节点,并选择“常规”。在“附加包含目录”旁边,键入“MathFuncsLib.h”头文件所在位置的路径。

现在,可以在应用程序中使用“MyMathFuncs”类了。使用以下代码替换“MyExecRefsLib.cpp”的内容。

3、WIn32中文件操作的API?

CreateFile打开文件

要对文件进行读写等操作,首先必须获得文件句柄,通过该函数可以获得文件句柄,该函数是通向文件世界的大门。

ReadFile从文件中读取字节信息。

在打开文件获得了文件句柄之后,则可以通过该函数读取数据。

WriteFile向文件写入字节信息。

同样可以将文件句柄传给该函数,从而实现对文件数据的写入。

CloseHandle关闭文件句柄。

打开门之后,自然要记得关上。

GetFileSize获取文件大小。

由于文件大小可以高达上数G(1G需要30位),因此一个32位的双字节类型无法对其精确表达,因此返回码表示低32位,还有一个出口参数可以传出高32位。该函数同样需要文件句柄作为入口参数。

CopyFile复制文件

注意:只能复制文件,而不能复制目录

MoveFileEx移动文件

既可以移动文件,也可以移动目录,但不能跨越盘符。(Window2000下设置移动标志可以实现跨越盘符操作)

DeleteFile删除文件

4、windows地址空间的划分?

1)32bit进程指针可以表达的范围从 0x00000000-0xffffffff 共计4GB。只能使用本进程的内存空间。

空指针赋值分区 [0x00000000]-[0x0000ffff] 帮助捕获空指针赋值,试图访问将引发异常。OS自动管理,无法手动分配。

用户模式分区 [0x00010000]-[0x7fffffff] 2 GB,进程地址空间的驻留地,不能使用指针来访问其他进程该在该分区的空间。

64KB禁入分区:内核模式分区 操作系统代码载入地,供所有进程共享。

2)进程地址空间中的区域Region-$

可用地址空间的状态:闲置、未分配。

区域起始地址分配粒度:64K。

区域大小的分配粒度:OS页面的整数倍 x86,x64页面大小为4K。

VirtualAlloc:预订区域

VirtualFree:释放预订的区域

3)区域类型

闲置:区域的虚拟地址没有任何后备存储器。该地址尚未预订,应用程序既可以从基地址开始预订区域,也可以从闲置区域内任何位置开始预订区域(这里的任何位置也要符合区域起始地址的64K限制)。

私有:区域的虚拟地址以系统的页交换文件为后备存储器。

映像:区域的虚拟地址一开始以映像文件(exe,dll)为后备存储器,但此后不一定以映像文件为后备存储器。

已映射:区域虚拟地址一开始以内存映像文件为后备存储器。

4)物理存储器

地址空间区域预订以后需要还需要为其分配物理存储区才能使用,分配的过程叫:调拨,反之:撤销调拨。现代操作系统可以将磁盘空间模拟成内存空间一样,供应用程序使用。其中磁盘模拟为内存区域存储的文件,就叫页交换文件。

5、内存映射文件的作用?

应用程序究竟是应该打开文件,读取文件并关闭文件,还是打开文件,然后使用一种缓冲算法,从文件的各个不同部分进行读取和写入呢?M i c r o s o f t提供了一种两全其美的方法,那就是内存映射文件。

与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。一旦该文件被映射,就可以访问它,就像整个文件已经加载内存一样。

内存映射文件可以用于3个不同的目的:

1)系统使用内存映射文件,以便加载和执行. e x e和D L L文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。

2)可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I / O操作,并且可以不必对文件内容进行缓存。

3)可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。

Wi n d o w s确实提供了其他一些方法,以便在进程之间进行数据通信,但是这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为单个计算机上的多个进程互相进行通信的最有效的方法。

6、线程局部存储的意义?

1)为什么要有TLS?原因在于,进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。

如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为线程局部静态变量),就需要新的机制来实现。这就是TLS。

2)线程局部存储在不同的平台有不同的实现,可移植性不太好。幸好要实现线程局部存储并不难,最简单的办法就是建立一个全局表,通过当前线程ID去查询相应的数据,因为各个线程的ID不同,查到的数据自然也不同了。大多数平台都提供了线程局部存储的方法,无需要我们自己去实现。

3)Win32下:

每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。首先定义一些DWORD线程全局变量或函数静态变量,准备作为各个线程访问自己的TLS数组的索引变量。一个线程使用TLS时,第一步在线程内调用TlsAlloc()函数,为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:

global_dwTLSindex=TLSAlloc();

注意,此步之后,当前线程实际上访问的是这个TLS数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS数组索引变量,但实际上各个线程得到的可能是不同DWORD值。

其意义在于,每个使用TLS的线程获得了一个DWORD类型的线程局部静态变量作为TLS数组的索引变量。C/C++原本没有直接定义线程局部静态变量的机制,所以在如此大费周折。

第二步,为当前线程动态分配一块内存区域(使用LocalAlloc()函数调用),然后把指向这块内存区域的指针放入TLS数组相应的槽中(使用TlsValue()函数调用)。

第三步,在当前线程的任何函数内,都可以通过TLS数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。

最后,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域(使用LocalFree()函数),然后从TLS数组中放弃对应的槽(使用TlsFree()函数)。

7、Win32中线程同步的方式?

1、 临界区(CCriticalSection)

当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。具体应用方式:

1、 定义临界区对象CcriticalSection g_CriticalSection;

2、 在访问共享资源(代码或变量)之前,先获得临界区对象,g_CriticalSection.Lock();

3、 访问共享资源后,则放弃临界区对象,g_CriticalSection.Unlock();

2、 事件(CEvent)

事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。比如在某些网络应用程序中,一个线程如A负责侦听通信端口,另外一个线程B负责更新用户数据,利用事件机制,则线程A可以通知线程B何时更新用户数据。每个Cevent对象可以有两种状态:有信号状态和无信号状态。Cevent类对象有两种类型:人工事件和自动事件。

自动事件对象,在被至少一个线程释放后自动返回到无信号状态;

人工事件对象,获得信号后,释放可利用线程,但直到调用成员函数ReSet()才将其设置为无信号状态。在创建Cevent对象时,默认创建的是自动事件。

CEvent(BOOL bInitiallyOwn=FALSE,

BOOL bManualReset=FALSE,

LPCTSTR lpszName=NULL,

LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);

bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号;

bManualReset:指定要创建的事件是属于人工事件还是自动事件。TRUE为人工事件,FALSE为自动事件;

后两个参数一般设为NULL,在此不作过多说明。

2、BOOL CEvent::SetEvent();

将Cevent类对象的状态设置为有信号状态。如果事件是人工事件,则Cevent类对象保持为有信号状态,直到调用成员函数ResetEvent()将其重新设为无信号状态时为止。如果为自动事件,则在SetEvent()后将事件设置为有信号状态,由系统自动重置为无信号状态。

3、BOOL CEvent::ResetEvent();

将事件的状态设置为无信号状态,并保持该状态直至SetEvent()被调用为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。

一般通过调用WaitForSingleObject()函数来监视事件状态。

3、 互斥量(CMutex)

互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,

但是更节省资源,更有效率。

4、 信号量(CSemphore)

当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。CSemaphore类对象保存了对当前访问某一个指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零为止。

CSemaphore 类的构造函数原型及参数说明如下:

CSemaphore(

LONG lInitialCount = 1,

LONG lMaxCount = 1,

LPCTSTR pstrName = NULL,

LPSECURITY_ATTRIBUTES lpsaAttributes = NULL

);

lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值;

lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;

后两个参数在同一进程中使用一般为NULL,不作过多讨论;

一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就减1,只要当前可用资源计数大于0,就可以发出信号量信号。如果为0,则放入一个队列中等待。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。

BOOL ReleaseSemaphore( HANDLE hSemaphore, // hSemaphore:信号量句柄

              LONG lReleaseCount, // lReleaseCount:信号量计数值

              LPLONG lpPreviousCount // 参数一般为NULL);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: