操作系统实验4
题目1:编写页面内存的LRU替换算法
在实验3基础上考虑,如果当前分配的内存或保存页面的数据项已经被用完,这时再有新的网页请求,需要对已在内存中的网页数据进行替换,本实验内容需要使用LRU算法来对内存中的网页数据进行替换
题目2:编写页面内存的LFU替换算法
实现LFU(最少访问频率的页面替换)算法来管理内存页面
实验报告要求:
- 实验报告封面如下页所示。
- 按照题目要求,完成相关实验题目。
2.1报告中要包含完成此题目所查阅的一些关键技术材料。例如内存结构的设计、分配管理、回收方法等内容。
2.2 报告中有实现的关键技术点源代码,源代码书写要有一定的规范,源代码中有相关的注释
2.3 作为扩展,在界面上能够定时给出当前内存的占用情况。因此根据上面的题目,可以适当地增加对其它方面的信息监控。
操作系统实验报告四
姓名:许恺
学号:2014011329
日期:12月7日
一.构思想法
题目1:编写页面内存的LRU替换算法
因为web4和3太像了,所以并没有什么难度,我只需要将替换的页面改一下就可以了,所以我加了一个属性,就是这个页面是第几个被申请的,这样既可以记录网页申请的流量,也可以将在内存中的数字最小的那个替换掉,也就是LRU算法所讲的最久未被用的那个,然后加上时间函数计算出每次用的时间进行分析报告就可以了。
题目2:编写页面内存的LFU替换算法
其实我想说我web3用的就是LFU替换算法,老师可以看我的web3报告,或者这份报告也可以,这份报告会给出对于一串申请序列,两种方法比较的时间分析。
二.源代码以及结果贴图和分析
题目1:编写页面内存的LRU替换算法
关键代码(有修改):
Webserver4.cpp: // webserver4.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <Winsock2.h> #include <windows.h> #include <string> #include <fstream> #include "PageMemo.h" PageMemo Mem; //初始化内存中的网页 SOCKET socketconn; static string dir = "D:\\xukai\\学习\\操作系统实验\\网页"; //文件路径 int num = 1; //请求网页次数 #include "Thread.h" #include "CMyTask.h" #pragma comment(lib, "ws2_32.lib") using namespace std; void main(int argc, _TCHAR* argv[]) { CMyTask taskObj; CThreadPool threadPool(10); double alltime=0;//运行总时间 LARGE_INTEGER large_interger; double dff; __int64 c1, c2; QueryPerformanceFrequency(&large_interger); dff = large_interger.QuadPart; QueryPerformanceCounter(&large_interger); //初始化WinSock库 WORD wVersionRequested; WSADATA wsaData; cout << "初始化库成功" << endl; wVersionRequested = MAKEWORD(2, 2); int wsaret = WSAStartup(wVersionRequested, &wsaData); if (wsaret) return; //创建SOCKET SOCKET socketSrv; socketSrv = socket(AF_INET, SOCK_STREAM, 0); if (socketSrv == INVALID_SOCKET) return; cout << "创建socket成功" << endl; SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(80); //绑定套接字 if (bind(socketSrv, (struct sockaddr*)&addrSrv, sizeof(SOCKADDR))) { //关闭连接 shutdown(socketSrv, 1); closesocket(socketSrv); WSACleanup(); return; } cout << "绑定套接字成功!" << endl; //等待客户端连接 SOCKADDR_IN addrCli; int len = sizeof(SOCKADDR); //监听端口 if (listen(socketSrv, 5) == SOCKET_ERROR) { printf("监听失败!\n"); } while (true) { socketconn = accept(socketSrv, (SOCKADDR*)&addrCli, &len); //接受连接 if (socketconn == SOCKET_ERROR) { printf("接受连接失败!\n"); return; } cout << "连接成功" << endl; //用QueryPerformanceCounter()来计时 微秒 c1 = large_interger.QuadPart; taskObj.SetData((void*)0); //将任务内容设到对象里 threadPool.AddTask(&taskObj); //将任务对象添加到线程池的任务队列 CThreadPool::Threadfunction(); QueryPerformanceCounter(&large_interger); c2 = large_interger.QuadPart; alltime = alltime + (c2 - c1) / dff; printf("本次用时%lf微秒\n", (c2 - c1) / dff); cout << "到现在为止共用时间:" << alltime << "微秒。" << endl; num++; } shutdown(socketSrv, 1); closesocket(socketSrv); //关闭连接 WSACleanup(); } PageMemo.h: #pragma once #include <string> #include <fstream> #include <iostream> using namespace std; /* 用来放网页的内存结构类 */ class PageMemo { public: /* ***内存结构构造函数,建立10个页面信息,从磁盘写入内存10个网页信息*** */ PageMemo() { //ifstream fp[10]; //用10个文件读的对象 //int i; //string which = ""; //for (i = 0; i < 10; i++) //{ // which = to_string(i); // file[i] = "D:\\xukai\\学习\\操作系统实验\\网页\\" + which + ".html "; // //cout << file[i] << endl; // fp[i].open(file[i], std::ios::binary); // //打开文件失败 // if (!fp[i].is_open()) // { // cout << "请求文件" << which + ".html" << "不存在" << endl; // } // else//打开文件成功并读取 // { // char buffer[1024]; // while (fp[i].good() && !fp[i].eof()) // { // fp[i].getline(buffer, 1024); // //cout << buffer << endl; // //将读取的内容追加入sendBuf中 // Page[i].append(buffer); // //cout << Page[i] << endl; // buffer[0] = '\0'; // } // } // fp[i].close(); //} } ~PageMemo() { } /* ****LRU算法的函数,找出前面最久未使用的页面替换 */ int LRU() { int M = lru[0], X = 0; for (int i = 1; i < 10; i++) { if (lru[i] < M) { X = i; M = lru[i]; } } return X; } /* ***求出被用过次数最少的内存中的页面,用的最普通的算法*** */ int Min() { int M = User_f[0], X = 0; for (int i = 1; i < 10; i++) { if (User_f[i] < M) { X = i; M = User_f[i]; } } return X; } /* ***在屏幕上打印当前页面的路径信息和使用次数*** */ void print() { cout << "现在要输出我当前的网页的内存信息:" << endl; for (int i = 0; i < 10; i++) { cout << " " << file[i] << " " << User_f[i]<<" "<<lru[i] << endl; } } //得到第i条路径的内容 string getfile(int i) { return file[i]; } //得到第i个页面的内容 string getPage(int i) { return Page[i]; } //得到第i个页面使用次数 int getUser_f(int i) { return User_f[i]; } //使用过一次这个网页了,使用次数+1 void plusone(int i) { User_f[i]++; } //修改file内容函数 void writefile(int i, string f) { file[i] = f; } //修改Page内容函数 void writePage(int i, string p) { Page[i] = p; } //修改User_f值的函数 void writeUser_f(int i, int n) { User_f[i] = n; } //当网页被使用,修改他的lru void writelru(int i, int n) { lru[i] = n; } private: string file[10]; //页面路径 string Page[10]; //页面信息 int User_f[10] = { 0 }; //使用次数 int lru[10] = { 0 }; //此网页第几个被使用,最小的就是最久没被用的 }; CMyTask.h: #pragma once #include "Thread.h" #include "windows.h" #include "PageMemo.h" class CMyTask : public CTask { public: CMyTask() {} inline int Run() { printf("Process startup!\n"); //init WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); DWORD pid = ::GetCurrentProcessId(); sockaddr_in sa; int add_len = sizeof(sa); if (socketconn != INVALID_SOCKET) { getpeername(socketconn, (struct sockaddr *)&sa, &add_len); //while (1) //{ //连接成功后与客户端进行会话 char recvBuff[10000]; string sendBuf; string locDir; ifstream fp; //接收请求 if (recv(socketconn, recvBuff, 10000, 0) == SOCKET_ERROR) { printf("%d\n", socketconn); printf("error!"); getchar(); return 0; } //读取http请求头 string recvBuffer = recvBuff; int posGet = recvBuffer.find("GET", 0); int posHttp = recvBuffer.find("HTTP", 0); //截取html文件路径 for (int pos = posGet + 4; pos < posHttp; pos++) { if (recvBuffer[pos] == '/') { locDir.push_back('\\'); continue; } locDir.push_back(recvBuffer[pos]); } locDir = dir + locDir; int i; //打开http请求文件进行读取 for (i = 0; i < 10; i++) //看看内存里有没有现成的 { //cout << locDir << endl; //cout << Mem.getfile(i) << endl; if (strcmp(locDir.c_str(), Mem.getfile(i).c_str()) == 0) //匹配字符串 { cout << "在内存中找到相应的页面信息啦!!!" << endl; Mem.plusone(i); //使用次数+1 //cout << Mem.getPage(i) << endl; sendBuf = Mem.getPage(i); break; } } if (i == 10) //内存中没有,从磁盘中调取 { cout << "555555,/(ㄒoㄒ)/~~还得从磁盘取~~" << endl; fp.open(locDir.c_str(), std::ios::binary); //打开文件失败 if (!fp.is_open()) { cout << "请求文件" << locDir.c_str() << "不存在" << endl; } else//打开文件成功并读取 { char buffer[1024]; while (fp.good() && !fp.eof()) { fp.getline(buffer, 1024); //将读取的内容追加入sendBuf中 sendBuf.append(buffer); buffer[0] = '\0'; } } fp.close(); //int x = Mem.Min(); //找到最不经常用的页 int x = Mem.LRU(); //找到LRU算法的结果 Mem.writefile(x, locDir); //把调用的页的路径也放内存里 Mem.writePage(x, sendBuf); //把调用的页放到内存里 Mem.writeUser_f(x, 1); //将使用次数置为一 Mem.writelru(x, num); } Mem.print(); //响应请求,将页面信息发送到客户端 //cout << sendBuf << endl; if (send(socketconn, sendBuf.c_str(), sendBuf.length(), 0) == SOCKET_ERROR) { return 0; } shutdown(socketconn, 1); //关闭连接 i = 0; //重置计数的i closesocket(socketconn); } else { printf("[%d]fail accept:%d\n", pid, ::WSAGetLastError()); } return 0; } }; 线程池(未修改): Thread.h: #pragma once #ifndef __THREAD_H #define __THREAD_H #include "PageMemo.h" #include <vector> #include <string> #include <pthread.h> #pragma comment(lib,"x86/pthreadVC2.lib") using namespace std; /** * 执行任务的类,设置任务数据并执行 */ class CTask { protected: string m_strTaskName; /** 任务的名称 */ void* m_ptrData; /** 要执行的任务的具体数据 */ public: CTask() {} CTask(string taskName) //任务类的重载:设置任务名,设置任务内容为空 { m_strTaskName = taskName; m_ptrData = NULL; } virtual int Run() = 0; /*启动任务的虚函数*/ void SetData(void* data); /** 设置任务数据 */ public: virtual ~CTask() {} //虚拟析构函数 }; /** * 线程池管理类的实现 */ class CThreadPool { private: static vector<CTask*> m_vecTaskList; /** 任务列表 */ static bool shutdown; /** 线程退出标志 */ int m_iThreadNum; /** 线程池中启动的线程数 */ pthread_t *pthread_id; static pthread_mutex_t m_pthreadMutex; /** 线程同步锁 */ static pthread_cond_t m_pthreadCond; /** 线程同步的条件变量 */ protected: static int MoveToIdle(pthread_t tid); /** 线程执行结束后,把自己放入到空闲线程中 */ static int MoveToBusy(pthread_t tid); /** 移入到忙碌线程中去 */ static void* ThreadFunc(void*); /** 新线程的线程回调函数 */ int Create(); /** 创建线程池中的线程 */ public: static void Threadfunction(); //在主函数中调用的任务函数 CThreadPool(int threadNum = 10); int AddTask(CTask *task); /** 把任务添加到任务队列中 */ int StopAll(); /** 使线程池中的线程退出 */ int getTaskSize(); /** 获取当前任务队列中的任务数 */ }; #endif Thread.cpp: #include "stdafx.h" #include "Thread.h" #include <iostream> void CTask::SetData(void * data) //设置任务的具体内容 { m_ptrData = data; } vector<CTask*> CThreadPool::m_vecTaskList; //任务列表 bool CThreadPool::shutdown = false; //设置关闭为0 pthread_mutex_t CThreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER; //设置变量值 pthread_cond_t CThreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER; /** * 线程池管理类构造函数 */ CThreadPool::CThreadPool(int threadNum) { this->m_iThreadNum = threadNum; //用参数设置线程数量 cout << "I will create " << threadNum << " threads\n" << endl; Create(); //调用创建线程的函数 } /** * 线程回调函数 */ void* CThreadPool::ThreadFunc(void*) { return (void*)1; } void CThreadPool::Threadfunction() { pthread_t tid = pthread_self(); printf("tid %lu run\n", tid); vector<CTask*>::iterator iter = m_vecTaskList.begin(); //添加迭代器从任务列表开头 /** * 取出一个任务并处理之 */ CTask* task = *iter; if (iter != m_vecTaskList.end()) { task = *iter; m_vecTaskList.erase(iter); } pthread_mutex_unlock(&m_pthreadMutex); task->Run(); /** 执行任务 */ printf("tid:%lu idle\n", tid); } int CThreadPool::MoveToIdle(pthread_t tid) { return 0; } int CThreadPool::MoveToBusy(pthread_t tid) { return 0; } /** * 往任务队列里边添加任务并发出线程同步信号 */ int CThreadPool::AddTask(CTask *task) { pthread_mutex_lock(&m_pthreadMutex); this->m_vecTaskList.push_back(task); pthread_mutex_unlock(&m_pthreadMutex); pthread_cond_signal(&m_pthreadCond); return 0; } /** * 创建线程 */ int CThreadPool::Create() { pthread_id = (pthread_t*)malloc(sizeof(pthread_t) * m_iThreadNum); for (int i = 0; i < m_iThreadNum; i++) { pthread_create(&pthread_id[i], NULL, ThreadFunc, NULL); } return 0; } /** * 停止所有线程 */ int CThreadPool::StopAll() { /** 避免重复调用 */ if (shutdown) { return -1; } printf("Now I will end all threads!!\n"); /** 唤醒所有等待线程,线程池要销毁了 */ shutdown = true; pthread_cond_broadcast(&m_pthreadCond); /** 阻塞等待线程退出,否则就成僵尸了 */ for (int i = 0; i < m_iThreadNum; i++) { pthread_join(pthread_id[i], NULL); } free(pthread_id); pthread_id = NULL; /** 销毁条件变量和互斥体 */ pthread_mutex_destroy(&m_pthreadMutex); pthread_cond_destroy(&m_pthreadCond); return 0; } /** * 获取当前队列中任务数 */ int CThreadPool::getTaskSize() { return m_vecTaskList.size(); }
结果贴图:(因为我有10个内存页面,当我输入,第0,1,2,3,4,5,6,7,8,9,2,23,5,54,4,456,3,2,4,4356,9个页面时的结果,LRU)
0,
。
。
。
9,
23,
54,
2,
456,
3,
4356,
3,
分析:我们发现从外存调入的大概会用到0.08微秒以上,而从内存中直接用的用时在0.07微秒左右或以下,可看出从内存中是快的,当然可能也跟文件的大小有关系,但是不影响大局。
LFU算法:
0,
。
。
9,
23,
54,
2,
456,
3,
4356,
3,
分析:LFU的算法时间就很明显了,从外存拿的用时0.1微秒多,而从内存拿的只用了0.04微秒左右,总时间用了1.41638微秒,而LRU算法用了1.17903微秒,在这个序列上LRU算法要比LFU算法要快一些,虽然只实验了一个序列,但是大部分序列LRU算法都是较好的一种算法。
三.查阅的文献
1.http://blog.csdn.net/morewindows/article/details/6854764 时间函数集锦
四.体会感想
其实没什么感想,因为和web3一样,而且有很多同学web3就把这个做了,恩,就这样,没什么难度,时间函数也是一查就有。没了。