【连续动态内存管理模拟C++实现】

连续动态内存管理模拟C++实现


针对操作系统中内存管理相关理论进行实验,要求实验者编写一个程序,该程序管理一块虚拟内存,实现内存分配和回收功能

原理

连续内存分配:为一个用户程序分配一个连续的内存空间,它分为单一连续分配,固定分区分配和动态分区分配,在本实验中,我们主要讨论动态分区分配。
动态连续分配:根据进程的实际需要,动态地为之分配内存空间。在实现可变分区分配时,将涉及到分区分配中的所用的数据结构、分区分配算法和分区的分配与回收操作这几个问题。

  1. 分区分配中的数据结构
    (1) 空闲分区表:一张数据表,用于记录每个空闲块的情况,如起始地址、大小,使用情况等。
    (2) 空闲分区链:为了实现对空闲分区的分配,把所有的空闲内存块连成一个双向链,便于分配和回收。
  2. 分区分配算法
    (1) 首次适应算法:从链首出发,寻找满足申请要求的内存块。
    (2) 循环首次适应算法:从上次查找的下一个空闲块开始查找,直到找到满足要求的内存块。
    (3) 最佳适应算法:在每次查找时,总是要找到既能满足要求又最小的内存块给分配给用户进程。为了方便查找,所有的空闲内存块按从小到大的顺序存放在空闲链表中。
  3. 内存分配操作
    利用分配算法查找到满足要求的内存块,设请求内存大小为 u.size,而分配的内存块大小为 m.size,如果 m.size-u.size≤size (size 为设定的不可再分割的内存大小),则不再切割;反之,按 u.size 分配给申请者,剩余的部分仍留在内存链中。
  4. 回收内存
    根据回收区地址,从空闲链表中找到相应的插入点。
    (1) 回收区与插入点的前一个空闲分区相邻,此时将回收区与前一分区合并,不为回收区分配新表项。
    (2) 回收区与插入点的后一个空闲分区相邻,将回收区与后一分区合并成一个新区,回收区的首址最为新分区的首址。
    (3) 回收区与前(F1)后(F2)分区相邻,则把三个分区合并成一个大的分区,使 F1 的首址作为新分区的首址,修改 F1 大小,撤销 F2 表项。
    (4) 回收区不与任何分区相邻,为回收区建立一个新表项。

代码

#include<bits/stdc++.h>
using namespace std;

//空闲分区链的每一个结点,空闲分区链是一个双向链表
struct Node {
    int start;              //开始地址
    int size;               //块大小
    int pid;                //分配给的进程id;
    Node *pre;              //前向指针
    Node *next;             //后向指针
};

//空闲分区表
struct PartitionItem {
    int num;            //分区号
    int size;           //分区大小
    int avaibleMem;     //剩余可用大小
    Node* start;        //起始地址(连接的链表)
    int status;         //分区状态
};

const int N = 5;    //定义5个分区
PartitionItem PARTITIONTABLE[N];        //构建分区表
vector<pair<int, int>> processMap;      //记录已经分配的进程号和区号
int record = -1;     //记录刚刚访问的空闲区区号,用于循环首次适应

//初始化分区表、测试数据
void init() {
    int num = 1;    //分区号
    int size[] = {50, 60, 70, 60, 80};  //各分区大小
    /*初始化时,剩余可用大小avaibleMem = 分区大小
               起始地址start = null
               分区状态status = 0(未分配)
    */
    for (int i = 0; i < N; ++i) {
        PARTITIONTABLE[i].num = num++;
        PARTITIONTABLE[i].size = size[i];
        PARTITIONTABLE[i].avaibleMem = size[i];
        PARTITIONTABLE[i].start = nullptr;
        PARTITIONTABLE[i].status = 0;
    }
}

//将分配的区块(结点)加入链表
void add_to_linklist(int ind, int memory){
    int pid = processMap.back().first;
    Node *node = new Node;
    node->next = nullptr;
    node->pre = nullptr;
    node->pid = pid;
    node->size = memory;
    PARTITIONTABLE[ind].avaibleMem -= memory;
    if(PARTITIONTABLE[ind].start == nullptr) {  //若该区块还是空链表
        node->start = 0;
        PARTITIONTABLE[ind].start = node;
        PARTITIONTABLE[ind].status = 1;
    } else {    //该分区之前已经分配过了,需要遍历到链表尾部进行插入
        Node *p = PARTITIONTABLE[ind].start;
        while(p->next != nullptr) {
            p = p->next;
        }
        p->next = node;
        node->pre = p;
        node->start = p->start + p->size;
    }
}

//遍历进程表,如该进程已经申请,不让继续申请
bool is_pid_exist(int pid) {
    for (int i = 0; i < processMap.size(); ++i) {
        if(pid == processMap[i].first) {
            return true;
        }
    }
    return false;
}

//排序规则
bool cmp(PartitionItem &a, PartitionItem &b) {
    return a.avaibleMem < b.avaibleMem;
}

//首次适应算法,返回值:1--成功;0--失败
int first_fit(int pid, int memory) {
    if(is_pid_exist(pid)){
        cout << "进程已经申请,不要太贪心噢" << endl;
        return 0;
    }
    int flag = 0;           //有无合适分区块标志
    // 遍历分区表,试图寻找一块有适合空闲大小的分区块
    for (int i = 0; i < N; ++i) {
        if(PARTITIONTABLE[i].avaibleMem >= memory) {
            record = i;         //记录下本次访问的区号
            processMap.push_back(make_pair(pid, PARTITIONTABLE[i].num));//记录该进程分配到的区块号
            add_to_linklist(i, memory);
            flag = 1;
            break;
        }
    }
    return flag;
}

//循环首次适应算法,返回值:1--成功;0--失败
int loop_first_fit(int pid, int memory) {
    if(is_pid_exist(pid)){
        cout << "进程已经申请,不要太贪心噢" << endl;
        return 0;
    }
    int flag = 0;           //有无合适分区块标志
    // 从上次访问的下一个区开始遍历分区表,试图寻找一块有适合空闲大小的分区块
    for (int i = (record + 1) % N, k = 1; k <= N; ++k) {
        if(PARTITIONTABLE[i].avaibleMem >= memory) {
            record = i;     //更新record
            processMap.push_back(make_pair(pid, PARTITIONTABLE[i].num));
            add_to_linklist(i, memory);
            flag = 1;
            break;
        }
        i = (i + 1) % N;
    }
    return flag;
}

//最佳适应算法,返回值:1--成功;0--失败
int best_fit(int pid, int memory) {
    //最佳适应算法将所有的分区从小到大排序,寻找第一块有合适大小的进行分配
    if(is_pid_exist(pid)){
        cout << "进程已经申请,不要太贪心噢" << endl;
        return 0;
    }
    int flag = 0;
    //将所有的分区根据剩余可用大小从小到大排序
    sort(PARTITIONTABLE, PARTITIONTABLE + N, cmp);
    // 遍历分区表,试图寻找一块有适合空闲大小的分区块
    for (int i = 0; i < N; ++i) {
        if(PARTITIONTABLE[i].avaibleMem >= memory) {
            record = i;         //记录下本次访问的区号
            processMap.push_back(make_pair(pid, PARTITIONTABLE[i].num));//记录该进程分配到的区块号
            add_to_linklist(i, memory);
            flag = 1;
            break;
        }
    }
    return flag;
}

//回收算法,返回值:1--成功;0--失败
int recycle(int pid) {
    if(!is_pid_exist(pid)) {
        cout << "还没有该进程,不能对它进行回收" << endl;
        return 0;
    }
    int part_num = 0;
    //从processMap中找到该进程分配到的区块号, 同时擦除该记录
    vector<pair<int, int>>::iterator it1 = processMap.begin();
    vector<pair<int, int>>::iterator it2 = processMap.end();
    while(it1 != it2) {
        if((*it1).first == pid) {
            part_num = (*it1).second - 1;
            processMap.erase(it1);
            break;
        }
        it1++;
    }
    //从获取的区块号获得对应的链表
    Node *p = PARTITIONTABLE[part_num].start;
    //遍历链表,找到pid的分块
    while(p != nullptr) {
        if(p->pid == pid) { 
            if(p->pre == nullptr) {     //如果pid的分块是链表的第一个分块,即没有前向块
                PARTITIONTABLE[part_num].start = p->next;
                PARTITIONTABLE[part_num].avaibleMem += p->size;
                if(p->next == nullptr) {    //若后面不再有结点块
                    PARTITIONTABLE[part_num].status = 0;
                } else {
                     p->next->pre = nullptr;     //后面的紧跟块即将成为链表头,其前向指针修改为nullptr
                    //该块后面的其它块资源起始位置前移
                    Node *tmp = p->next;
                    while (tmp != nullptr) {
                        tmp->start -= p->size;
                        tmp = tmp->next;
                    }
                }
            } else {    //结点块处于中间或尾部
                Node *s = p->pre;       //获取前一个结点
                s->next = p->next;
                if(p->next != nullptr) 
                    p->next->pre = s;
                PARTITIONTABLE[part_num].avaibleMem += p->size;
                //该块后面的其它块资源起始位置前移
                Node *tmp = s->next;
                while (tmp != nullptr) {
                    tmp->start = s->start + s->size;
                    tmp = tmp->next;
                }
                delete p;
            }
            break;
        } else {
            p = p->next;
        }
    }
    return 1;
} 

void show_memory() {
    //打印分区表
    printf("分区序号\t分区大小\t可用大小\t分区状态\t\n");
    for (int i = 0; i < N; ++i) {
        printf("%d\t\t%d\t\t%d\t\t", PARTITIONTABLE[i].num, PARTITIONTABLE[i].size, PARTITIONTABLE[i].avaibleMem);
        if (PARTITIONTABLE[i].status == 0)
            printf("空闲\t\t\n");
        else
            printf("已分配\t\t\n");
    }
    //打印分区详情
    for (int i = 0; i < N; ++i) {
        printf("分区号 %d: \n", i + 1);
        Node *p = PARTITIONTABLE[i].start;
        if(p == nullptr) {
            printf("该区暂未分配\n");
        } else {
            int j = 1;
            while(p != nullptr) {
                printf("分块%d: 起始地址  %d\t\t块大小  %d\t\t所属进程号  %d\n", j, p->start, p->size, p->pid);
                p = p->next;
                j++;
            }
        }
    }
}

void menu_print() {
    cout << "1----申请内存" << endl;
    cout << "2----回收内存" << endl;
    cout << "3----内存信息" << endl;
    cout << "0----退出程序" << endl;
    cout << "请输入对应指令的数字: " << endl;
}

int main() {
    init();
    cout << "初始化已完成,请按下列指示输入命令" << endl;
    while(1) {
        int cmd;
        menu_print();
        cin >> cmd;
        if(cmd == 1) {
            int pid, memory, way, res;
            cout << "请输入进程id: " << endl;
            cin >> pid;
            cout << "请输入需要分配的内存大小: " << endl;
            cin >> memory;
            cout << "请选择分配方式: 0--首次适应, 1--循环首次适应, 2--最佳适应" << endl;
            cin >> way;
            if(way != 0 && way != 1 && way != 2)
                cout << "输入有误,回到主菜单" << endl;
            else {
                switch (way)
                {
                    case 0: res = first_fit(pid, memory);
                        break;
                    case 1: res = loop_first_fit(pid, memory);
                        break;
                    case 2: res = best_fit(pid, memory);
                        break;
                }
            }
            if(res)
                cout << "分配成功!" << endl;
            else cout << "分配失败!" << endl;
            cout << endl;
        } else if(cmd == 2) {
            int pid;
            cout << "请输入需要回收的进程号" << endl;
            cin >> pid;
            int res = recycle(pid);
            if(res)
                cout << "回收成功!" << endl;
            else cout << "回收失败!" << endl;
            cout << endl;
        } else if(cmd == 3) {
            show_memory();
            cout << endl;
        } else if(cmd == 0) {
            cout << "退出成功" << endl;
            break;
        } else {
            cout << "输入的指令有误,请重新输入" << endl;
            cin.get();
            continue;
        }
    }
    return 0;
}

运行结果

进行十次分配,可采取不同的分配方式
随机回收1
随机回收2
随机回收3
随机回收4
以上是笔者学习操作系统过程对动态内存管理模拟的实现,水平有限,如有错误,欢迎指正!

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值