《操作系统》实验指导书(2010)

更新时间:2024-05-18 10:43:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

附件:操作系统课程实验指导书

所属学校 武汉工程大学 课程名称 操作系统 课程教师 蔡敦波

《操作系统》课程实验指导书

课程代码:05121152

适用对象:计算机科学与技术、智能科学与技术 指导教师:蔡敦波

实验内容

实验一 熟悉Linux基本环境(2学时) 实验二 进程同步与互斥(3学时,综合) 实验三 Windows虚拟存储器管理(3学时)

实验一 熟悉Linux基本环境

(2学时)

一、实验目的:

(1)熟悉Linux下的基本操作,学会使用各种Shell命令操作Linux,形成对Linux的感性认识。

(2)学会使用vi编辑器编辑简单的C语言程序,并能对其编译、调试和运行。

二、实验原理

(1)参阅相关Linux操作系统的安装手册,熟悉Linux的基本安装和配置; (2)参阅相关Linux的命令参考手册,熟悉Linux下的操作命令。 (3)附1列出了本实验涉及的部分命令,其他命令的用法自查。

三、实验条件:

1. Linux操作系统; 2. SSH软件; 3. PC机 四、实验内容:

以管理员给定的账户和密码以ssh方式登陆Linux,并使用“ls”,“cat”“cd”,more等命令来实现基本的文件操作并观察Linux文件系统的特点;

使用“mkdir”命令建立一个以自己学号为名字的文件夹,进入该文件夹完成下面的内容(4)。

根据Linux手册,了解ln命令的功能和用法,思考:ln可否用于多个用户之间共享同一个文件?

使用vi编辑器编写一个C程序,将代码保存,并用gcc命令进行编译、链接和调试,运行程序。对本过程中间生成的汇编代码进行分析(必要时,补充汇编

1

语言的知识)。 五、实验步骤:

1. 使用软件putty.exe登陆到远程Linux系统:122.204.62.162(注:这是我

教研室的电脑,由于采用DHCP,可能IP地址会变化),用户名:ai08,密码:123456。登录后建立一个以自己学号为名字的文件夹,使用cd命令进入该文件夹,然后在该文件下进行所有的实验工作;或者,启动你所使用机器上的Vmware软件,启动Linux系统,输入用户名和密码。

2. 完成实验内容

3. 撰写实验报告

六、实验结论:

(1)Linux系统在用户登陆和操作界面以及文件系统上有哪些特点? (2)以一个具体的文件为例,说明该文件具有的属性和属性值的含义。

(3)给出你编写的C程序代码,依次列出编译、链接和运行三个步骤中键入的命令行。 附1:

登录Linux:

1. 使用程序putty连接远程计算机:输出远程计算机的IP地址,并连接

2. 用管理员给定账号和口令进行登录,成功出现$号(超级用户系统提示符,普通用户的系统提示符为$)。 常用命令练习:

注销(退出)系统:logout 或exit

3.练习使用命令ls(注意Linux命令区分大小写。)

使用ls 查看当前目录内容;使用ls 查看指定目录内容,如/目录,/etc目录

使用ls –all 查看当前目录内容;使用dir 查看当前目录内容

4.使用cd改变当前目录

cd .. 回到上层目录 ;cd / 回到根目录 5.pwd 显示当前路径 6.建立目录mkdir

mkdir 目录名 ; mkdir /home/s2001/newdir 7.删除目录:rmdir;

8.复制文件cp: 如 cp 文件名1 文件名2 9.移动文件或目录: mv 10.删除文件 rm

11. 显示文件内容:more (分页显示);

12. 显示文件:cat 文件名 建立文件:cat >文件名,ctrl+d结束输入

使用编辑器vim 编辑文件

目标: 使用vim编辑器输入如下程序代码,并保存为test.c文件。编译test.c并查看生成的汇编文件,分析汇编文件的指令序列与test.c中语句的对应关系(此部分为重点内容)。

2

test.c的代码如下:

操作步骤:

1、进入linux的文本模式之后,在命令行键入vim filename.c 然后回车。

下面作一些简单的解释:首先vim命令是打开vi编辑器。后面的filename.c是用户即将编辑的c文件名字,注意扩展名字是.c;当然,vim编辑器功能很强,可以用它来编辑其它格式的文件,比如汇编文件,其扩展名字是.s;也可以直接用vim打开一个新的未命名的文件,当保存的时候再给它命名,只是这样做不很方便。

最基本的命令I :当进入刚打开的文件时,不能写入信息,这时按一下键盘上的I键(insert),插入的意思,就可以进入编辑模式了。如下图所示:

a与i是相同的用法

2、当文件编辑完后,需要保存退出,这时需要经过以下几个步骤:1)按一下键盘上的Esc 键;2)键入冒号(:),紧跟在冒号后面是wq(意思是保存并退

3

出)。如果不想保存退出,则在第二步键入冒号之后,键入q!(不带w,机尾部保存)。如下图所示:

3、退出vim编辑器的编辑模式之后,要对刚才编写的程序进行编译。编译的命令是:gcc filename.c [-o outputfilename.out],其中gcc是c的编译器。参数:filename.c 是要编译的源文件的名称,outputfilename表示输出文件名称,中括号表示括号内部的内容可输入也可以不输入(中括号本身不再命令行中出现)。如果不输入outputfilename.out,默认的输出文件是a.out 。

4、最后一步是运行程序,方法如下:./outputfilename.out ,查看运行结果与预期是否相符。 5、执行 gcc –S test.c ,则gcc将生成test.c对应的汇编语言代码:文件名为test.s。使用more或者vim命令查看test.s的内容。如果使用more,则键入:more test.s;如果使用vim,则键入: vim test.s。 分析test.s 与 test.c的对应关系。

添加新用户、修改文件属性

添加新用户(在root下,按默认值回答)

adduser 用户名;如adduser s2001 ; 以新用户登录到系统 2.修改用户口令 passwd 用户名 3.控制文件属性

使用ls –l 查看文件属性

改变用户的所有权:chown 用户名 文件名

改变文件权限:chmod g+w 文件名;chmod o-r 文件名

或使用数字方式修改:如chmod 644文件名;chmod 755文件名

u (user用户),g ( group组),o (other其他); w 写访问权限,r 读访问权限, x 执行权限

4.查看相关命令的帮助:man 命令名

5.显示当前系统中已注册的用户信息:who 6.显示当前注册的用户信息:whoami

与文件链接的数 Unix文件目录的属性显示格式: 如:-rw-rw-rw- 1 steve users 138 Apr 5 19:34 readme

drwxrwxrwx 3 steve users 80 Apr 5 19:43 dir1 文件或目录名

文件的访问权限 文件的拥有者 4 用户所在的组

最后修改的日期和时间 实验二 进程同步与互斥

(3学时,综合)

一、实验目的:

掌握基本的同步与互斥算法,理解生产者消费者模型。

学习使用Windows 2000/XP中基本的同步对象,掌握相关API的使用方法。 了解Windows 2000/XP中多线程的并发执行机制,实现进程的同步与互斥。 二、实验原理 同步对象

同步对象是指Windows中用于实现同步与互斥的实体,包括信号量(Semaphore)、互斥量(Mutex)、临界区(Critical Section)和事件(Events)等。本实验中使用到信号量、互斥量和临界区三个同步对象。

同步对象的使用步骤:

创建/初始化同步对象。

请求同步对象,进入临界区(互斥量上锁)。 释放同步对象(互斥量解锁)。

这些对象在一个线程中创建,在其他线程中都可以使用,实现同步与互斥。 相关API的功能及使用

我们利用Windows SDK提供的API编程实现实验题目要求,而VC中包含有Windows SDK的所有工具和定义。要使用这些API,需要包含对这些函数进行说明的SDK头文件——最常见的是Windows.h(特殊的API调用还需要包含其他头文件)。

下面给出的是本实验使用到的API的功能和使用方法简单介绍。 (1) CreateThread

功能——创建一个在调用进程的地址空间中执行的线程 格式

HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParamiter,

DWORD dwCreationFlags, Lpdword lpThread );

参数说明

lpThreadAttributes——指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。

dwStackSize——定义原始堆栈大小。

lpStartAddress——指向使用LPTHRAED_START_ROUTINE类型定义的函数。

lpParamiter——定义一个给进程传递参数的指针。 dwCreationFlags——定义控制线程创建的附加标志。 lpThread——保存线程标志符(32位)

5

(2) CreateMutex

功能——创建一个命名或匿名的互斥量对象 格式

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,

BOOL bInitialOwner, LPCTSTR lpName);

参数说明

lpMutexAttributes——必须取值NULL。

bInitialOwner——指示当前线程是否马上拥有该互斥量(即马上加锁)。 lpName——互斥量名称。 (3) CreateSemaphore

功能——创建一个命名或匿名的信号量对象 格式

HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName );

参数说明

lpSemaphoreAttributes——必须取值NULL。

lInitialCount——信号量的初始值。该值大于0,但小于lMaximumCount指定的最大值。

lMaximumCount——信号量的最大值。 lpName——信号量名称。 (4) WaitForSingleObject

功能——使程序处于等待状态,直到信号量hHandle出现(即其值大于等于1)或超过规定的等待时间

格式

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);

参数说明

hHandle——信号量指针。

dwMilliseconds——等待的最长时间(INFINITE为无限等待)。 (5) ReleaseSemaphore

功能——对指定信号量加上一个指定大小的量。成功执行则返回非0值 格式

BOOL ReleaseSemaphore(HANDLE hSemaphore,

LONG lReleaseCount,

LPLONG lppreviousCount );

参数说明

hSemaphore——信号量指针。 lReleaseCount——信号量的增量。

lppreviousCount——保存信号量当前值。 (6) ReleaseMutex

功能——打开互斥锁,即把互斥量加1。成功调用则返回0

6

格式

BOOL ReleaseMutex(HANDLE hMutex);

参数说明

hMutex——互斥量指针。 (7) InitializeCriticalSection

功能——初始化临界区对象 格式

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

参数说明

lpCriticalSection——指向临界区对象的指针。 (8) EnterCriticalSection

功能——等待指定临界区对象的所有权 格式

VOID enterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

参数说明

lpCriticalSection——指向临界区对象的指针。 (9) LeaveCriticalSection

功能——释放指定临界区对象的所有权 格式

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

参数说明

lpCriticalSection——指向临界区对象的指针。 四、实验示例(方法、步骤与例程) 0. 问题模型:

有一批并发执行的生产者和消费者,它们的行为用同一个进程内的多个线程模拟。每个生产者生产的产品用自身的ID标识。一个消费者可能需要多个生产者的产品。生产者和消费者通过一定数量的缓冲区进行合作。缓冲区的数量由用户指定。生产者将标有自己ID的产品放到一个空闲缓冲区中。消费者如果发现自己需要的产品N出现在某个缓冲区就尝试消费N。一个产品N可以由多个消费者消费,最后一个消费N的消费者负责将N从缓冲区删除(即,N不是一个一次性的消费品)。 测试用例文件

测试用例文件用于描述各线程的有关信息,该文件内容及格式如下:

3

1 P 3 2 P 4 3 C 4 1 4 P 2

5 C 3 1 2 4#

说明:第一行给出的是缓冲区的数目;其余各行是各进程信息。 每行中的数据之间用Tab键分隔。 第一列(除第一行外):线程号。

第二列:P——生产者,C——消费者。

第三列:线程在生产和消费前的休眠时间,单位为秒。

7

第四及以后各列:消费的产品所对应的生产者线程号。 数据结构

(1) 用整型数组Buffer_Critical表示缓冲区。

(2) 用自定义结构ThreadInfo记录一条线程信息,多个线程对应一个ThreadInfo数组。

(3) 通过如下同步对象实现互斥:

设一个互斥量h-mutex,实现生产者在查询和保留缓冲区的下一个空位置时进行互斥。

设置h_Semaphore[MAX_THREAD_NUM]信号量数组表示相应产品已经生产,实现生产者与消费者之间的同步。同时,用表示空缓冲区数目的信号量empty_semephore指示是否存在空位置,实现类似的同步,以便开始下一个产品的生产。

设置临界区对象数组PC_Critical[MAX_BUFFER_NUM]实现每个缓冲区上消费者之间的互斥。 程序结构

为了方便,程序结构用如下的文字予以描述。 (1) 主函数

(2) 初始化缓冲区、消费请求队列及部分同步对象 (3) 提取线程信息

(4) 完成线程相关同步对象的初始化 (5) 创建线程,模拟生产者和消费者 (6) 等待所有线程结束 (7) 程序结束

(8) 消费者

(9) 有无消费请求?有,则继续(10);无,则转(16)

(10) 此请求可满足?可满足,转(11);否,则阻塞,再转(10) (11) 确定产品位置

(12) 此产品正被消费?是,则阻塞,再转(12);否,则转(13) (13) 进入临界区(请求同一产品的消费者之间互斥) (14) 消费产品,并判断是否应该释放产品所占缓冲区 (15) 退出临界区,转(9) (16) 结束消费者线程

(17) 生产者

(18) 存在空缓冲区?有,则继续(19);无,则阻塞,再转(18) (19) 另一生产者在写?否,则转(20);是,则阻塞,再转(19) (20) 进入临界区(请求同一产品的生产者之间互斥) (21) 在缓冲区中为本线程产品分配空间 (22) 退出临界区

(23) 写入产品到分配的缓冲区空间中 (24) 结束生产者线程

四、实验内容:

8

以生产者/消费者模型为依据,在Windows XP环境下创建一个控制台进程,在该进程中创建n个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。

五、实验步骤:

学习并理解生产者/消费者模型及其同步/互斥规则; 学习了解Windows同步对象及其特性; 熟悉实验环境,掌握相关API的使用方法;

设计程序,实现生产者/消费者进程(线程)的同步与互斥;

实验报告要求:

(1)在实验报告中给出实验过程中出现的问题及解决方法? (2)程序运行时是否在屏幕上出现不合词法的单词?为什么?

(3)根据程序验证并分析生产者、消费者模型,即,在每一次屏幕有输出的时刻,分析生产者的行为是否正确,消费者的行为是否正确,缓冲区的内容是否正确?

附:示例程序

//**************************R_WP1.CPP*******************************//

#include #include #include #include #include

//本程序允许的最大临界区数

#define MAX_BUFFER_NUM 10 //秒到微秒的乘法因子

#define INTE_PER_SEC 1000 //生产和消费进程总数

#define MAX_THREAD_NUM 64

//定义记录在测试文件中的线程参数数据结构 struct ThreadInfo {

int serial; char entity; double delay;

int thread_request[MAX_THREAD_NUM]; int n_request; };

9

//全局变量定义

//临界区对象的声明,用于管理缓冲区的互斥访问

CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM]; int Buffer_ Critical[MAX_BUFFER_NUM]; //缓冲区声明

HANDLE h_Thread[MAX_THREAD_NUM]; //存储线程句柄的数组 ThreadInfoThread_info[MAX_THREAD_NUM]; //线程信息数组 HANDLE empty_semephore; //信号量 HANDLE h_mutex; //互斥量

DWORD n_Thread=0; //实际线程数目

DWORD n_Buffer_or_Critical; HANDLE h_semephore[MAX_THREAD_NUM]; //生产、消费及辅助函数的声明 void Produce(void *p); void Consume(void *p); bool IfInOtherRequest(int); int FindProducePosition(); int FindBufferPosition(int);

int main(void) {

DWORD wait_for_all; ifstream inFile; //初始化缓冲区

for (int i=0;i

for (int j=0;j

for (int k=0;j

//初始化临界区对象

for (int i=0;i

//从文件中获得实际缓冲区数目 inFile>>n_Buffer_or_Critical; inFile.get();

printf(“输入文件是:\\n”); //回显获得的缓冲区数目

printf(“%d \\n”,(int)n_Buffer_or_Critical); //提取各线程信息到相应的数据结构中

10

//实际缓冲区/临界区数目 //生产者允许消费的信号量 while (inFile) {

inFile>>Thread_Info[n_Thread].serial; inFile>>Thread_Info[n_Thread].entity; inFile>>Thread_Info[n_Thread].delay; char c;

inFile.get(c);

while (c!=?\\n?&&!inFile.eof()) {

inFile>>

Thread_Info[n_Thread].thread_request[Thread_Info[n_Thread].n_request++]; inFile.get(c); }

n_Thread++; }

//回显获得的线程信息,便于确认正确性 for (j=0;j<(int)n_Thread;j++) {

int Temp_serial=Thread_Info[j].serial; char Temp_entity=Thread_Info[j].entity; double Temp_delay=Thread_Info[j].delay;

printf(“ \\n thread- %c %f”,Temp_serial,Temp_entity,Temp_delay); int Temp_request=Thread_Info[j].n_request; for (int k=0;k

printf(“ %d ”,Thread_In[j].thread_request[k]); cout<

printf(“\\n\\n”);

//创建模拟过程中必要的信号量

empty_semaphore=CreateSemaphore(NULL,n_Buffer_or_Critical, n_Buffer_or_Critical, “semephore_for_empty”);

h_mutex=CreateMutex(NULL,FALSE, “mutex_for_update”);

//用线程ID为产品读写时所使用的同步信号量命名 for (j=0;j<(int)n_Thread;j++) {

std::string lp=”semephore_for_produce”; int temp=j; while (temp) {

char c=(char)(temp); lp+=c; temp/=10;

11

}

h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lp.c_str()); }

//创建生产者和消费者线程 for (i=0;i<(int)n_Thread;i++) {

if (Thread_Info[i].entity==?P?)

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),&(Thread_Info[i]),0,NULL);

h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),&(Thread_Info[i]),0,NULL); }

//主程序等待各个线程的动作结束

wait_for_all=WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1); printf(“ \\n \\nALL Producer and Consumer have finished their work, \\n”); printf(“Press any key to quit!\\n”); return 0; }

//确认是否还有对同一产品的消费请求未执行 bool IfInOtherRequest(int req) {

for (int i=0;i

for (int j=0;j

//找出当前可以进行产品生产的空缓冲区位置 int FindProducePosition() {

int EmptyPosition;

for (int i=0;i

EmptyPosition=i;

//用下列特殊值表示本缓冲区正处于被写状态 Buffer_Critical[i]=-2; Break;

12

}

return EmptyPosition; }

//找出当前需要生产者生产的产品的位置 int FindBufferPosition(int ProPos) {

int TempPos;

for (int i=0;i

TempPos=i; Break; }

return TempPos; }

//生产者线程

void Produce(void *p) {//局部变量声明

DWORD wait_for_semaphore,wait_for_mutex,m_delay; int m_serial;

//获得本线程信息

m_serial=((ThreadInfo*)(p))->serial;

m_delay=(DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC); Sleep(m_delay); //开始请求生产

printf(“Producer - sends the produce require.\\n”,m_serial);

//确认有空缓冲区,同时把空位置数empty减1,用于生产/消费同步 wait_for_semaphore=WaitForSingleObject(empty_semaphore,-1); //互斥访问下一个用于生产的空临界区,实现写写互斥 wait_for_mutex= WaitForSingleObject(h_mutex,-1); int ProducePos=FindProducePosition(); ReleaseMutex(h_mutex);

//生产者在获得的空位置做标记,以下的写操作在生产者之间可以并发执行 //在核心生产步骤中,程序用生产者的ID作为产品编号供消费者识别

printf(“Producer - begin to produce at position -.\\n”,m_serial,ProducePos); Buffer_Critical[ProducePos]=m_serial;

printf(“Producer - finish producing :\\n”,m_serial);

printf(“Position[ - ]:= \\n”,ProducePos,Buffer_Critical[ProducePos]); //实现读写同步

ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL); }

13

//消费者线程

void Consume(void *p) {//局部变量声明

DWORD wait_for_semaphore,m_delay;

int m_serial,m_requestNum; //消费者序列号和请求的数目 int m_thread_request[MAX_THREAD_NUM]//本消费线程的请求队列 //提取本线程的信息

m_serial=((ThreadInfo*)(p))->serial;

m_delay=(DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC); m_requestNum=((ThreadInfo*))->n_request; for (int i=0;i

m_thread_request[i]=((ThreadInfo*)(p))->thread_request[i]; Sleep(m_delay);

//循环进行所需产品的生产 for (i=0;i

//若生产了,允许消费数目为-1;实现读写同步

wait_for_semaphore=WaitForSingleObject(h_semaphore[m_thread_request[i]],-1) //查询所需产品在缓冲区的号

int BufferPos=FindBufferPosition(m_thread_request[i]); //开始消费,读读互斥

//进入临界区消费,消费完毕通知其他消费者自己的请求已满足 //若产品消费完,应做相应处理,并给出提示

//相应处理是指把相应的缓冲区清空,并增加代表空缓冲区的信号量 EnterCriticalSection(&PC_Critical[BufferPos]); Printf(“Consumer- begin to cosume - product\\n”,m_serial,m_thread_request[i]); ((ThreadInfo*)(p))->thread_request[i]=-1; if (!IfInOtherRequest(m_thread_request[i])) {

Buffer_Critical[BufferPos]=-1; //标记缓冲区为空 printf(“Consumer- finish consuming -:\\n”, m_serial,m_thread_request[i]); printf(“position[-]:=\\n”,BufferPos,Buffer_Critical[BufferPos]); ReleaseSemaphore(empty_semaphore,1,NULL); } else {

printf(“Consumer - finish consuming product -\\n”, m_serial,m_thread_request[i]);

14

}

//离开临界区

LeaveCriticalSection(&PC_Critical[BufferPos]); } }

15

实验三 Windows虚拟存储器管理

(3学时)

一、实验目的:

1.理解内存分配原理,特别是页式虚拟内存分配方法。

2.了解Windows 2000/XP的内存管理机制,掌握页式虚拟存储技术。 3.掌握Windows 2000/XP中内存管理基本API的使用方法。 二、实验原理

1.Windows中的虚拟存储技术

http://www.yesky.com/67/1753067.shtml

2.页面状态 (1) 页面的种类

每个进程的虚拟地址空间中的页面状态可以分为三种:提交页面、保留页面和空闲页面。

提交页面:已经被分配了物理内存 保留页面:被该进程占用的 空闲页面:未被该进程占用的 (2) 页面操作

保留:该进程占用一个存储区域

提交:为某个保留的区域分配物理内存 回收: 释放: 加锁:

3.存储系统的统计指标

MEMORY_BASIC_INFORMATION structure

Contains information about a range of pages in the virtual address space of a process. The VirtualQuery and VirtualQueryEx functions use this structure.

Syntax

1. 2. 3. 4. 5. 6. 7. 8. 9. TION;

typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; PVOID AllocationBase; DWORD AllocationProtect; SIZE_T RegionSize; DWORD State; DWORD Protect; DWORD Type;

} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMA

Members

BaseAddress

A pointer to the base address of the region of pages.

AllocationBase

A pointer to the base address of a range of pages allocated by the VirtualAlloc function. The page pointed to by the BaseAddressmember is contained within this allocation range.

AllocationProtect

The memory protection option when the region was initially allocated. This member can be one of the memory protection constants or 0 if the caller does not have access.

RegionSize

The size of the region beginning at the base address in which all pages have identical attributes, in bytes.

State

The state of the pages in the region. This member can be one of the following values.

State Meaning

MEM_COMMIT Indicates committed pages for which physical storage has

been allocated, either in memory or in the paging file on disk. 0x1000

Indicates free pages not accessible to the calling process and

MEM_FREE available to be allocated. For free pages, the information in 0x10000 the AllocationBase, AllocationProtect, Protect,

and Type members is undefined.

Indicates reserved pages where a range of the process's virtual

MEM_RESERVE address space is reserved without any physical storage being 0x2000 allocated. For reserved pages, the information in

the Protect member is undefined.

Protect

The access protection of the pages in the region. This member is one of the values listed for the AllocationProtect member.

Type

The type of pages in the region. The following types are defined.

Type Meaning

MEM_IMAGE Indicates that the memory pages within the region are mapped

into the view of an image section. 0x1000000

MEM_MAPPED Indicates that the memory pages within the region are mapped into the view of a section. 0x40000

MEM_PRIVATE Indicates that the memory pages within the region are private (that is, not shared by other processes). 0x20000

Remarks

1

To enable a debugger to debug a target that is running on a different architecture (32-bit versus 64-bit), use one of the explicit forms of this structure.

1. 2. 3. 4. 5. 6. 7. 8. 9.

typedef struct _MEMORY_BASIC_INFORMATION32 { DWORD BaseAddress; DWORD AllocationBase; DWORD AllocationProtect; DWORD RegionSize; DWORD State; DWORD Protect; DWORD Type;

} MEMORY_BASIC_INFORMATION32, *PMEMORY_BASIC_INFOR

MATION32; 10. 11. typedef struct DECLSPEC_ALIGN(16) _MEMORY_BASIC_INFORMATION64 { 12. ULONGLONG BaseAddress; 13. ULONGLONG AllocationBase; 14. DWORD AllocationProtect; 15. DWORD __alignment1; 16. ULONGLONG RegionSize; 17. DWORD State; 18. DWORD Protect; 19. DWORD Type; 20. DWORD __alignment2; 21. } MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;

Requirements

Minimum supported client Windows 2000 Professional Minimum supported server Windows 2000 Server

Header

Winnt.h (include Windows.h)

See also

VirtualAlloc VirtualQuery VirtualQueryEx

MEMORYSTATUS structure

Contains information about the current state of both physical and virtual memory.

The GlobalMemoryStatus function stores information in a MEMORYSTATUS structure.

2

Syntax

1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

typedef struct _MEMORYSTATUS { DWORD dwLength;

DWORD dwMemoryLoad; SIZE_T dwTotalPhys; SIZE_T dwAvailPhys; SIZE_T dwTotalPageFile; SIZE_T dwAvailPageFile; SIZE_T dwTotalVirtual; SIZE_T dwAvailVirtual;

} MEMORYSTATUS, *LPMEMORYSTATUS;

Members

dwLength

The size of the MEMORYSTATUS data structure, in bytes. You do not need to set this member before calling the GlobalMemoryStatusfunction; the function sets it.

dwMemoryLoad

A number between 0 and 100 that specifies the approximate percentage of physical memory that is in use (0 indicates no memory use and 100 indicates full memory use).

dwTotalPhys

The amount of actual physical memory, in bytes.

dwAvailPhys

The amount of physical memory currently available, in bytes. This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. It is the sum of the size of the standby, free, and zero lists.

dwTotalPageFile

The current size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead.

dwAvailPageFile

The maximum amount of memory the current process can commit, in bytes. This value should be smaller than the system-wide available commit. To calculate this value,

call GetPerformanceInfo and subtract the value of CommitTotal from CommitLimit.

dwTotalVirtual

The size of the user-mode portion of the virtual address space of the calling process, in bytes. This value depends on the type of process, the type of processor, and the configuration of the operating system. For example, this value is approximately 2 GB for most 32-bit processes on an x86 processor and approximately 3 GB for 32-bit processes that are large address aware running on a system with 4 GT RAM Tuning enabled.

dwAvailVirtual

3

The amount of unreserved and uncommitted memory currently in the user-mode portion of the virtual address space of the calling process, in bytes.

Remarks

MEMORYSTATUS reflects the state of memory at the time of the call. It also reflects the size of the paging file at that time. The operating system can enlarge the paging file up to the maximum size set by the administrator.

On computers with more than 4 GB of memory, the MEMORYSTATUS structure can return incorrect information, reporting a value of –1 to indicate an overflow. If your application is at risk for this behavior, use the GlobalMemoryStatusEx function instead of theGlobalMemoryStatus function.

Examples

For an example, see the GlobalMemoryStatus function.

Requirements

Minimum supported client Windows 2000 Professional Minimum supported server Windows 2000 Server

Header

Winbase.h (include Windows.h)

See also

GlobalMemoryStatus GlobalMemoryStatusEx Memory Performance Information

MEMORYSTATUS结构的含义

GlobalMemoryStatus,Win32 API函数。

此函数用来获得当前可用的物理和虚拟内存信息,函数定义为: VOID GlobalMemoryStatus (

LPMEMORYSTATUS lpBuffer );

此函数无返回值,参数是一个指向名为MEMORYSTATUS的结构的指针。函数的返回信息会被存储在MEMORYSTATUS结构中。

此函数用来替代用来支持16位应用程序的GetFreeSpace函数。 应用程序应该在申请内存前调用此函数以防止影响到其他程序运行。 这个函数的返回值是动态的,并且可能返回相同的值。 关于MEMORYSTATUS结构: 机构定义:

typedef struct _MEMORYSTATUS { // mst DWORD dwLength; // sizeof(MEMORYSTATUS) DWORD dwMemoryLoad; // percent of memory in use

4

DWORD dwTotalPhys; // bytes of physical memory DWORD dwAvailPhys; // free physical memory bytes DWORD dwTotalPageFile; // bytes of paging file DWORD dwAvailPageFile; // free bytes of paging file DWORD dwTotalVirtual; // user bytes of address space DWORD dwAvailVirtual; // free user bytes } MEMORYSTATUS, *LPMEMORYSTATUS; 结构成员的含义: dwLength

MEMORYSTATUS结构的大小,在调GlobalMemoryStatus函数前用sizeof()函数求得,用来供函数检测结构的版本。 dwMemoryLoad

返回一个介于0~100之间的值,用来指示当前系统内存的使用率。 dwTotalPhys

返回总的物理内存大小,以字节(byte)为单位。 dwAvailPhys

返回可用的物理内存大小,以字节(byte)为单位。 dwTotalPageFile

显示可以存在页面文件中的字节数。注意这个数值并不表示在页面文件在磁盘上的真实物理大小。

dwAvailPageFile

返回可用的页面文件大小,以字节(byte)为单位。 dwTotalVirtual

返回调用进程的用户模式部分的全部可用虚拟地址空间,以字节(byte)为单位。 dwAvailVirtual

返回调用进程的用户模式部分的实际自由可用的虚拟地址空间,以字节(byte)为单位。

4.了解相关的API函数 (1) _beginthreadex函数

功能——创建新线程的C runtime函数,以执行指定的可执行模块 格式 参数说明

(2) sleep函数

功能——使当前线程休眠 格式 参数说明

(3) VirtualAlloc函数

功能——保留或提交某一范围的虚拟地址。 格式 参数说明

(4) VirtualFree函数

功能——解除已被提交的虚存,或释放被保留/提交的进程虚拟地址空间 格式 参数说明

(5) VirtualProtect函数

5

功能——改变虚拟内存页的保护方式 格式 参数说明

(6) VirtualLock和VirtualUnlock函数 功能——锁定或解锁虚拟内存页 格式 参数说明

(7) VirtualQuery函数

功能——查询进程的虚拟内存 格式 参数说明

(8) GlobalMemoryStatus函数

功能——获取程序存储空间的使用状况以及系统的使用情况信息 格式 参数说明 三、实验条件:

1. Windows操作系统; 2. VC++ 6.0 开发环境; 3. PC机 四、实验内容:

在程序中,用一个线程模拟各种虚存活动,如虚存的保留、提交等。

用一个监控线程实时监控系统当前的虚存操作,并在控制台输出虚存操作信息。要求监控线程与模拟虚存活动的线程保持同步,即模拟线程出现模拟活动,监控线程应即可监控到该活动的信息。

在用监控线程监控内存活动的同时,汇报整个存储系统的使用情况。 五、实验步骤:

2. 理解实验原理。

3. 基于示例程序分析虚拟内存的管理模型。

六、实验结论:

学习并理解Windows中的虚拟存储技术; 学习了解虚存页面的状态及操作;

熟悉实验环境,掌握相关API的使用方法; 设计程序,实现虚存活动的模拟及监控;

分析实验中出现的问题及解决方法,撰写并提交实验报告。

说明:在一个控制台程序中使用了_beginthreadex()函数,并包含了PROCESS.h头文件,但总是编译错误:告诉我函数没有定义? #include \

............

Compiling...

e:\\_te$t\\test\\main.cpp(2299) : error C2065: '_beginthreadex' : undeclared identifier

6

解决方法: 工程->设置->C/C++->Code Generation->Use run-time library->选 Debug Multithread(多线程),或 Multithread!因为:运行库必须用多线程的! 附1:示例程序 1.程序结构

(1) main函数

用_beginthreadex函数启动两个线程。 (2) simulator线程(模拟内存分配)

随机地进行各种虚存操作,包括虚存的保留与提交、虚存的注销、虚存的注销并释放虚存空间、改变虚拟内存页的保护、锁定虚拟内存页和虚存的保留。每个活动完成后,程序的全局变量Actnum被设置为一个特定的整数值。线程随即被阻塞直至Actnum被置回零。

(3) inspector线程(跟踪内存分配活动)

该线程通过Actnum的值获得上一个虚存动作的类型,并通过BASE_PTR的值获得该动作发生的虚存地址,并输出相关信息。最后,设置Actnum为零,通知模拟线程继续下一次动作,实现两线程的同步。

程序的主线程派生上述两个主要线程后,就处于等待状态。用户此时可以观察模拟和监控线程的活动,也可以键入任意键结束程序的运行。 2.数据结构

(1) 指示器Actnum

(2) 地址指针BASE_PTR 3.源程序

//******************VIRTUMEM.CPP****************** # include # include # include # include

unsigned _stdcall simulator(void *); unsigned _stdcall inspector(void *); LPVOID BASE_PTR; int Actnum=0;

//主函数,主要用于启动用于模拟虚存活动和进行监控的两个线程 int main(int argc, char* argv[ ]) {

unsigned ThreadID[2];

_beginthreadex(NULL,0,simulator,NULL,0,&ThreadID[0]); _beginthreadex(NULL,0,inspector,NULL,0,&ThreadID[1]); getchar( ); //输入回车即可终止程序 return 0; }

//模拟一系列的虚存活动,作为一个独立的线程运行 unsigned _stdcall simulator(void *)

7

{

DWORD OLDProtect; Int randnum;

Printf (“Now the simulator procedure has been started . \\ n”); // 产生一个随机数种子

srand ((unsigned) time (NULL)) ;

// 在一个死循环中,用随机数控制,不断进行虚存操作活动 while (1) {

sleep (500 ) ; // 控制整个模拟和监控的速度 while ( Actnum!=0) {

sleep (500 ) ; // 等待,直到监控线程捕捉到上一个模拟动作后再继续下一个动作 }

randnum=7&rand() ;

switch (randnum ) // 各个动作中的虚存指针均使用BASE_PTR ;它在过程中由虚存分配函数动态调整,如果某动作不成功,则不会被监控线程监控到

{

case 0: if

(BASE_PTR=VirtualAlloc(NULL,1024*32 ,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE )) {

Actnum=1; // 虚存的保留与提交 }

break; case 1:

if (VirtualFree (BASE_PTR,1024*32,MEM_DECOMMIT) ) {

Actnum=2; //虚存的注销 }

break; case 2:

if ( Virtualfree (BASE_PTR,0,MEM_RELEASE) ) {

Actnum=3; //虚存的注销并释放虚存空间 }

break; case 3: if

(VirtualProtect(BASE_PTR,1024*32,PAGE_READONLY,&OldProtect) ) {

8

Actnum=4; //改变虚拟内存页的保护 }

break; case 4:

if (VirtualLock(BASE_PTR,1024*12) ) {

Actnum=5; //锁定虚拟内存页 }

break; case 5:

if (BASE_PTR=VirtualAlloc(NULL,1024*32,MEM_RESERVE, PAGE_READWRITE)) {

Actnum=6; //虚存的保留 }

break; default: break;

} // end of ?switch? } // end of ?while? return 0; }

//通过一个全局变量来监视另一个模拟线程的模拟活动,并通过 //适当的信息查询函数,将存储的使用和活动情况打出报告 unsigned _stdcall inspector (void*) {

int QuOut=0; char paral [3000];

MEMORYSTATUS Vmeminfo; Char tempstr[100];

MEMORY_BASIC_INFORMATION inspectorinfol;

int structsize = sizeof (MEMORY_BASIC_INFORMATION);

Printf (“Hi , now inspector begin to work\\n”); //在一个死循环中不断通过一个全局变量(监视器),来监控模拟 //线程是否有新的动作,如果有,通过APT函数对相应虚存处 //(通过共用BASE——PTR实现)的信息进行检查,从而验证 //该动作对存储使用的影响 while (1) {

sleep (1000); if (Actnum!=0) {

9

// 通过全局变量(监视器)Actnum来获取上一个虚存动作的类型 //及相应构造监控信息的头部 switch (Actnum) {

case 1 :

memset(&inspectorinfol ,0, structsize); VirtualQuery ((LPVOID)BASE_PTR,&inspectorinfol , structsize);

strcpy(paral, “目前执行动作:虚存的保留与提交\\n”); break; case 2 :

memset(&inspectorinfol ,0, structsize);

VirtualQuery((LPVOID)BASE_PTR,&inspectorinfol , structsize); strcpy (paral, “目前执行动作:虚存的除配\\n”); break; case 3 :

memset(&inspectorinfol ,0, structsize); VirtualQuery ((LPVOID)BASE_PTR,&inspectorinfol , structsize);

strcpy (paral, “目前执行动作:虚存的除配并释放虚存空间\\n”); break; case 4 :

memset(&inspectorinfol ,0, structsize); VirtualQuery ((LPVOID)BASE_PTR,&inspectorinfol , structsize);

strcpy (paral, “目前执行动作:改变虚拟内存页的保护\\n”); break; case 5 :

memset(&inspectorinfol ,0, structsize);

VirtualQuery((LPVOID)BASE_PTR,&inspectorinfol , structsize); strcpy (paral, “目前执行动作:锁定虚拟内存页\\n”); break; case 6 :

memset(&inspectorinfol ,0, structsize);

VirtualQuery((LPVOID)BASE_PTR,&inspectorinfol , structsize); strcpy (paral, “目前执行动作:虚存的保留\\n”); break; default: break; }

//实时显示固定格式的相关材料;通过目前监控到的动作所发生 //的虚存地址,监控该活动对相应存储空间的影响

sprintf(tempstr,”开始地址:0x%x\\n”,inspectorinfol.BaseAddress);

10

strcat(paral,tempstr);

sprintf(tempstr,”区块大小(目前指针处向前同一属性的块):0x%x\\n” ,

inspectorinfol.Regionsize); strcat(paral, tempstr);

sprintf(tempstr,”目前状态:0x%x\\n”,inspectorinfol.State); strcat(paral, tempstr); sprintf(tempstr,”分配时访问保护x%x\\n”,inspectorinfol.AllocationProtect); strcat(paral, tempstr);

sprintf(tempstr,”当前访问保护:0x%x\\n”,inspectorinfol.Protect); strcat(paral, tempstr);

strcat(paral,”(状态:10000代表未分配;1000代表提交;2000代表保留; )\\n”);

strcat(paral,”(保护方式:0代表其他;1代表禁止访问;2代表只读;4代表读写 )\\n10代表可执”);

strcat(paral,”(行;20代表可读和执行;40代表可读写和执行);\\n*******************\\n);

//全局信息,报告目前系统和当前进程的存储使用总体情况 GlobalMemoryStatus(&Vmeminfo);

strcat(paral,”当前整体存储统计如下\\n);

sprintf(tempstr,”物理内存总数:%d (BYTES)\\n”,Vmeminfo.dwTotalPhys); strcat(paral,tempstr);

sprintf(tempstr,”可用物理内存数:%d (BYTES)\\n”,Vmeminfo.dwAvailPhys); strcat(paral,tempstr);

sprintf(tempstr,”页面文件总数:%d (BYTES)\\n”,Vmeminfo.dwTotalPageFile); strcat(paral,tempstr);

sprintf(tempstr,”可用页面文件数:%d (BYTES)\\n”,Vmeminfo.dwAvailPageFile); strcat(paral,tempstr);

sprintf(tempstr,”虚拟空间总数:%d (BYTES)\\n”,Vmeminfo.dwTotalVirtual); strcat(paral,tempstr);

sprintf (tempstr,”可用虚存空间数:%d (BYTES)\\n”,Vmeminfo.dwTotalVirtual); strcat(paral,tempstr);

printf(“% s”,paral); //显示报告内容 //(这里可以同时将报告内容记录进日志文件)

Actnum=0; //通知模拟线程可以进行下一个模拟动作 Sleep(500); //调节模拟和监控的总体速度

11

}//for if }//for while return 0; }

12

本文来源:https://www.bwwdw.com/article/av47.html

Top