课程设计(综合实验)报告
( 20 13 -- 2014 年度第 二 学期)
名 称:Windows体系编程
题 目:课内综合实验
院 系:
班 级:
学 号:
学生姓名:
指导教师:王新颖
实验学时:12学时
成 绩:
日期: 2014 年 5 月30日
第二篇:Windows体系编程重点、答案及提示
Windows体系编程考试重点、答案及提示(仅供参考)
说明:
本文档由xxxxxxx整理完成,在此向他们表示感谢。如有发现错误,请及时向xxxx提出,以便修改。
第一章
1.API简介和组成
API——Application Program Interface,应用程序编程接口。Win32环境下的任何语言都是建立在win32 API基础上,MFC,VB,VF等对其进行了封装。API是最底层的服务。SDK(software development kit)编程就是直接调用API函数进行编程。
API组成:包括一组函数、结构、宏定义。
2.API调用方法
API的调用方法:在文件的开头包含相应的头文件,然后在程序中直接调用它们就可以了。 在自己的程序中调用API函数:
1)包含要调用函数的声明文件;
2)连接到指定库文件(即lib文件)
(a)VC默认已经连接了常用的库文件
(b)在工程\设置\link\设定要连接的库文件,或者#pargma comment(lib,”mylib.lib”)
3)在API函数前加”::”符号,表示是一个全局函数
第二章
1.80386处理器三种工作模式(windows运行在保护模式下)
80386处理器有三种工作模式:实模式、保护模式和虚拟86模式。windows系统运行在保护模式下。
2.进程和线程之间的区别联系
? 进程是正在运行的应用程序的实例,拥有自己的代码、数据和其他系统资源,包含一个或多个线程
? 线程是进程内执行代码的独立实体。占有cpu时间片执行指令的是线程
? 进程至少包含一个主线程,主线程会创建其他线程。所有线程共享所属进程的内存空间和资源,且仅能访问属于它的进程内存
3.虚拟内存和分配(用户空间,系统空间)
在保护模式下,32位的Windows系统可寻址4GB的地址空间。机器上大小通常小于4GB,Windows使用虚拟内存技术将磁盘空间当作内存空间来使用。 各进程的地址空间被分成了用户空间和系统空间两部分.用户空间就是进程的私有地址空间.系统空间部分放置操作系统的代码,包括内核代码、设备驱动代码、设备缓冲区等.系统空间部分在所有的进程中是共享的。
32位os可寻址4GB的地址空间,也就是os的虚拟内存空间为4G,win98下,4M——2G给应用程序预留,win2000下,64K——2G给应用程序预留,2G——4G为OS预留,0——64k不被映射,目的是为了捕捉程序中的错误。
Windows采用分页机制管理内存,每页的大小为4k(x86处理器上),也就是说windows以4k为单位为应用程序分配内存。
4.内核模式和用户模式(创建进程不考)
Windows使用了两种访问模式:内核模式与用户模式
? 用户程序在用户模式下运行,系统程序(服务、驱动)在内核模式下运行
? 内核模式——操作的一种高特权模式,其中的程序代码能直接访问所有内存(包括所有
的用户模式进程和应用程序的地址空间)和硬件。也称为“管理员模式”、“保护模式”或“Ring 0”。如果再细致地对此进行分类:它又可以被分为单内核模式和微内核模式两种。单内核模式代码结构紧凑、执行速度快,但是缺乏层次;微内核正好相反。单内核模式的代表如Linux;微内核模式的代表如Windows.
第三章
1.创建线程的函数和参数的作用(编程题)
看课件和例子 重点
2.线程内核对象的组成及作用
线程上下文CONTEXT:每个线程自己的一组CPU寄存器,即线程上下文,反映该线程上次运行时CPU寄存器的状态
使用计数Usage count:记录线程内核对象的使用计数,值为0时,内核对象撤销; 暂停计数Suspend count:指明线程的暂停计数;0表示可调度,非0表示暂停
退出代码Exit code:指定线程的退出代码,即线程函数的返回值;运行期间,值为STILL_ACTIVE 是否受信Signaled:指示线程对象是否为受信,状态运行期间,值为FALSE,未受信,线程结束,值为TRUE,等待函数返回。
3.线程终止方法
线程函数自然返回
使用ExitThread 函数终止线程执行
使用TerminateThread 函数在一个线程中终止另一个线程的执行
进程终止运行,系统会自动终止此进程中的所有线程的运行
4.线程优先级的设置
参考课件和例子
5.线程同步方法,特点和比较
临界区对象:能够很好的保护共享数据,但是它不能够用于进程之间资源的锁定,因为它不是内核对象
互锁函数:为同步访问多线程共享变量提供了一个简单的机制。如果变量在共享内存,不同进程的线程也可以使用此机制。
事件内核对象:事件内核对象主要用于线程间通信。因为它是一个内核对象,所以可以跨进程使用,依靠通信,使各线程的工作协调进行,达到同步的目的。
信号量内核对象:允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目;
互斥内核对象:确保一个线程独占地访问资源,互斥内核对象的行为特征和关键代码段有点
类似,但是它是属于内核对象,而关键代码段是用户模式对象,这导致了互斥内核对象的运行速度比关键代码段要低。互斥内核对象可以跨进程使用
线程局部存储概念及原理
看一下课件和例子 可能会是读程序题或填空
第四章
1.窗口创建过程的几个步骤
(1) 注册窗口类(RegisterClassEx)
(2) 创建窗口(CeateWindowEx)
(3) 在桌面上显示窗口(ShowWindow)
(4) 更新窗口客户区(UpdateWindow)
(5) 进入无限的消息获取和处理消息的循环
2.MFC按层次划分的类
(1)根类:CObject
(2)应用程序体系结构类
(3)窗口、对话框和控件类
(4)绘图和打印类
(5)简单数据类型类
(6)数组、列表和映射类
(7)文件和数据库类
(8)Internet和网络工作类
(9)OLE类
(10)调试和异常类
3.动态链接库的概念
? 动态链接库是应用程序的一个模块,这个模块的作用是导出一些函数和数据供程序中其
他模块使用
? 动态链接库是程序的一部分,它在本质上和可执行文件没有区别,都是作为模块
被进程加载到自己的地址空间。(dll是进程的一个模块,exe也是进程的一个模
块)
? Dll在exe编译阶段不会被插到exe中,dll是在程序运行时才会掉入内存,具体分
为装载期和运行期。
? 如果有多个程序用到同一个dll,内存中只有一个dll,大家共享它。os仅通过分
页机制将这份代码映射到不同的进程中。
? Windows API中的所有函数都包含在DLL中。其中有3个最重要的DLL,Kernel32.dll,它
包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。
? 静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况
下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。
? 在使用动态库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL
导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。
DllMain是动态链接库的入口点。库的入口函数仅供os使用,windows在库装载、卸载、进程中创建和结束线程时调用DllMain,以便采取相应的动作。
? Dll能够定义两种函数,导出函数和内部函数。
? 导出函数可以被其他模块调用,以可以被定义这个函数的模块调用
? 内部函数只能被定义这个函数的模块调用
? 动态链接库的主要功能是向外导出函数,供进程中的其他模块使用
4.加载动态链接库的两种方法
? 装载期间动态链接:API函数就是这样调用的。编译期必须提供.h和.lib,运行时必须提
供.dll,与exe在同一个目录下或者在system32下
? 运行期间动态链接:编译器无需.h和.lib,在程序中显式的加载dll
补充:线程同步 使用临界区对象的一个例子
当多个线程在同一个进程中执行时,可能有不止一个线程同时执行同一段代码,访问同一段内存中的数据。多个线程同时读共享数据没有问题,但如果同时读和写,情况就不同了。下面是一个有问题的程序,该程序用两个线程来同时增加全局变量g_nCount1和g_nCount2的计数,运行一秒之后打印出计数结果。
下面是程序源码:
#include
#include
#include
int g_nCount1 = 0;
int g_nCount2 = 0;
BOOL g_bContinue = TRUE;
UINT __stdcall ThreadFunc(LPVOID);
int main(int argc, char* argv[])
{
UINT uId;
HANDLE h[2];
h[0] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId);
h[1] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId);
// 等待1秒后通知两个计数线程结束,关闭句柄
Sleep(1000);
g_bContinue = FALSE;
::WaitForMultipleObjects(2, h, TRUE, INFINITE);
::CloseHandle(h[0]);
::CloseHandle(h[1]);
printf("g_nCount1 = %d \n", g_nCount1);
printf("g_nCount2 = %d \n", g_nCount2);
return 0;
}
UINT __stdcall ThreadFunc(LPVOID)
{
while(g_bContinue)
{
g_nCount1++;
g_nCount2++;
}
return 0;
}
线程函数ThreadFunc同时增加全局变量g_nCount1和g_nCount2的计数。按道理来说最终在主线程中输出的它们的值应该是相同的,可是结果并不尽如人意。
以下是运行结果:
g_nCount1 = 160308255
g_nCount2 = 127273267
Press any key to continue
g_nCount1和g_nCount2的值并不相同。出现这种结果主要是因为同时访问g_nCount1和g_nCount2的两个线程具有相同的优先级。在执行过程中如果第一个线程取走g_nCount1的值准备进行自加操作的时候,它的时间片恰好用完,系统切换到第二个线程去对g_nCount1进行自加操作;一个时间片过后,第一个线程再次被调度,此时它会将上次取出的值自加,并放入g_nCount1所在的内存里,这就会覆盖掉第二个线程对g_nCount1的自加操作。变量g_nCount2也存在相同的问题。由于这样的事情的发生次数是不可预知的,所以最终的值就不相同了。
例子中,g_nCount1和g_nCount2是全局变量,属于该进程内所有线程共有的资源。多线程同步就要保证在一个线程占有公共资源的时候,其他线程不会再次占有这个资源。所以,解决同步问题,就是保证整个存取过程的独占性。在一个线程对某个对象进行操作的过程中,需要有某种机制阻止其他线程的操作,这就用到了临界区对象。