操作系统——内存管理

1、实验目的:1、掌握操作系统存储管理中,动态分区分配中首次适应算法进行内存分配的的实现原理;2、模拟编程实现内存分配;3,判断算法特性。

2、实验原理:

①、动态分配:

分配方式可分为四类:单一连续分配、固定分区分配、动态分区分配以及动态可重定位分区分配算法四种方式,其中动态分区分配算法就是此实验的实验对象。动态分区分配又称为可变分区分配,它是根据进程的实际需要,动态地为之分配内存空间。在实现动态分区分配时,将涉及到分区分配中所用的数据结构、分区分配算法和分区的分配与回收操作这样三方面的问题。动态分区分配又称为可变分区分配,它是根据进程的实际需要,动态地为之分配内存空间。在实现动态分区分配时,将涉及到分区分配中所用的数据结构、分区分配算法和分区的分配与回收操作这样三方面的问题。

②、首次适应算法:

空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

3、程序代码实现

#include <stdio.h>
#include <stdlib.h>

typedef int phys_addr_t;

#define NR_HOLES         128    /* 可登记空闲分区的表项总数 */
#define NR_PROCESS       10     /* 进程的总数 */

#define RUNNING          1 /* 进程处于执行、就绪等状态(不考虑阻塞)*/
#define NOTEXISIT        0 /* 进程控制块空闲 */

struct hole {
    phys_addr_t h_base;           /* 空闲分区起始地址 */
    phys_addr_t h_len;            /* 空闲分区大小 */
    struct hole* h_next;          /* 指向空闲分区链中下一个空闲分区 */
} hole[NR_HOLES];

struct hole* hole_head; /* 空闲分区链链首指针 */
struct hole* free_slots; /* 可用表项链链首:每个表项可用来登记一个空闲分区*/

struct pcb {
    phys_addr_t p_base;           /* 分配到的内存起始地址 */
    phys_addr_t p_len;            /* 分配到的内存大小 */
    int p_state;                  /* 进程状态 */
} pcb[NR_PROCESS];

void del_slot(struct hole* prev_ptr, struct hole* hp);
void merge(struct hole* hp);
phys_addr_t alloc_mem(phys_addr_t m_len);
void free_mem(phys_addr_t m_base, phys_addr_t m_len);
void mem_init(phys_addr_t base, phys_addr_t size);
void pcb_init();
void show();


/*=========================================================================*
 *  *                            alloc_mem                                    *
 *   *========================================================================*/
phys_addr_t alloc_mem(phys_addr_t m_len) {

    /* 采用首次适应算法分配一块连续的内存空间
 *      * 参数:
 *           *      m_len  :申请内存块的长度;
 *                * 返回值:
 *                     *       分配成功,返回分配到的内存块的起始地址;失败返回-1.
 *                          */

    struct hole* hp, * prev_ptr;
    phys_addr_t old_base;

    hp = hole_head;
    while (hp != NULL) {
        if (hp->h_len >= m_len) {
            /* 找到足够大的空闲分区,并使用该空闲分区. */
            old_base = hp->h_base;  /* 记录该空闲分区的起始地址 */
            hp->h_len -= m_len;    /* 从中分一块出去,剩余部分(新空闲区)大小 */
            hp->h_base += m_len;   /* 剩余部分(新空闲区)起始地址 */

            /* 如果空闲区只是被部分分配出去, 调整大小后返回. */
            if (hp->h_len != 0) return(old_base);

            /* 整个空闲区被分配出去,则从空闲分区链中删除所分配出去的节点,然后返回. */
            del_slot(prev_ptr, hp);
            return(old_base);
        }

        prev_ptr = hp;
        hp = hp->h_next;
    }
    return(-1);  /* 找不到合适的空闲分区,返回-1. */
}


/*========================================================================*
 *  *                           free_mem                                     *
 *   *========================================================================*/
void free_mem(phys_addr_t m_base, phys_addr_t m_len) {
    /* 释放一块连续的内存空间.
 *      * 参数:
 *           *      m_base  :释放区的起始地址;
 *                *      m_len :释放区的长度
 *                     * 释放时要进行空闲分区的合并。
 *                          */

    struct hole* hp, * new_ptr, * prev_ptr;

    if (m_len == 0) return;
    if ((new_ptr = free_slots) == NULL) {
        printf("登记空闲分区的表项已耗尽!!!\n");
        exit(-1);
    }
    new_ptr->h_base = m_base;
    new_ptr->h_len = m_len;
    free_slots = new_ptr->h_next;
    hp = hole_head;

    /* 如果释放区的起始地址比空闲分区链首的小,或者目前系统中没有空闲分区,
 *      * 则将释放区插入空闲分区链的链首
 *           */
    if (hp == NULL || m_base <= hp->h_base) {
        new_ptr->h_next = hp;
        hole_head = new_ptr;
        merge(new_ptr);
        return;
    }

    /* 释放区不是插在链首. */
    while (hp != NULL && m_base > hp->h_base) {
        prev_ptr = hp;
        hp = hp->h_next;
    }

    /* 将释放区插在 'prev_ptr'的后面 */
    new_ptr->h_next = prev_ptr->h_next;
    prev_ptr->h_next = new_ptr;
    merge(prev_ptr);              /* 顺序是 'prev_ptr', 'new_ptr', 'hp' */
}


/*========================================================================*
 *  *                           del_slot                                     *
 *   *========================================================================*/
void del_slot(struct hole* prev_ptr, struct hole* hp) {
    /* 从空闲分区链中删除一个指定的节点,并将登记该空闲分区的表项插入可用表项链中
 *     * 参数:
 *         *      prev_ptr:指向空闲分区链表中欲删除节点前面的节点;
 *             *      hp      :指向空闲分区链表中要被删除的节点
 *                 */

    if (hp == hole_head)
        hole_head = hp->h_next;
    else
        prev_ptr->h_next = hp->h_next;

    hp->h_next = free_slots;
    free_slots = hp;
}


/*========================================================================*
 *  *                           merge                                        *
 *   *========================================================================*/
void merge(struct hole* hp) {
    /* 合并空闲分区
 *      * 参数:
 *           *      hp:指向可能需合并的空闲分区
 *               */

    struct hole* next_ptr;

    /* 如 'hp'指向最后一个空闲分区, 则无需合并;否则尝试着合并空闲分区,
 *          * 合并后,需释放登记被合并的后面空闲分区的登记表项。
 *               */
    if ((next_ptr = hp->h_next) == NULL) return;
    if (hp->h_base + hp->h_len == next_ptr->h_base) {
        hp->h_len += next_ptr->h_len;   /* 合并两个连续分区的大小 */
        del_slot(hp, next_ptr);
    }
    else {
        hp = next_ptr;
    }

    /* 如 'hp'指向最后一个空闲分区, 则无需合并;否则尝试着合并空闲分区,
 *      * 合并后,需释放登记被合并的后面空闲分区的登记表项。
 *           */
     /* 在分区的回收时,若回收区hp插入空洞链中间或链尾时,是通过merge(prev_ptr)进行分区的合并的,故合并的操作必须做两次 */
    if ((next_ptr = hp->h_next) == NULL) return;
    if (hp->h_base + hp->h_len == next_ptr->h_base) {
        hp->h_len += next_ptr->h_len;
        del_slot(hp, next_ptr);
    }
}


/*========================================================================*
 *  *                           mem_init                                     *
 *   *========================================================================*/
void mem_init(phys_addr_t base, phys_addr_t size) {
    /* 初始化空闲表项链和空闲分区链:
 *      *     空闲表项链是指可用来登记空闲分区的空表项组成的链表;
 *           *     空闲分区链中每个节点对应着内存中的一个空闲分区。
 *                * 参数:
 *                     *     base:初始用户空间的起始地址;
 *                          *     size:初始用户空间的长度。
 *                              */

    struct hole* hp;

    /* 将所有用来登记空闲分区的表项插入空闲表项链中. */
    for (hp = &hole[0]; hp < &hole[NR_HOLES]; hp++) hp->h_next = hp + 1;
    hole[NR_HOLES - 1].h_next = NULL;
    free_slots = &hole[0];

    /* 初始化空闲分区链. */
    hole_head = NULL;
    free_mem(base, size);
}

/*========================================================================*
 *  *                           pcb_init                                     *
 *   *========================================================================*/
void pcb_init() {
    /*
 *      * 初始化进程控制块,初始状态下,系统中没有任何用户进程存在。
 *           */

    int i;

    /* 将所有pcb的状态置为空闲状态 */
    for (i = 0; i < NR_PROCESS; i++) pcb[i].p_state = NOTEXISIT;
}

/*========================================================================*
 *  *                           show                                  *
 *   *========================================================================*/
void show() {
    /*
 *      * 显示所有进程的详细情况以及空闲分区的详细情况。
 *           */

    int i;
    struct hole* hp;

    printf("==========================================\n");

    /* 显示所有进程的详细情况 */
    printf("进程号      起始地址  长度\n");

    for (i = 0; i < NR_PROCESS; i++) {
        if (pcb[i].p_state != NOTEXISIT) {
            printf("%4d   %8d  %8d\n", i, pcb[i].p_base, pcb[i].p_len);
        }
    }

    printf("==========================================\n");
    /* 显示所有空闲区的详细情况 */
    printf("空闲区序号  起始地址  长度\n");
    hp = hole_head;
    i = 1;
    while (hp != NULL) {
        printf("%4d   %8d  %8d\n", i, hp->h_base, hp->h_len);
        hp = hp->h_next;
        i++;
    }
    printf("==========================================\n");
}


/*========================================================================*
 *  *                          main                                          *
 *   *========================================================================*/
int main() {
    /* 测试程序:
 *      *     1.初始状态下,内存只有一个大的空闲分区,输入它的起始地址
 *           * 和长度,并对所有的数据结构进行初始化。
 *                *     2.分配和回收的模拟:
 *                     *     (1)输入"a n size"表示进程n申请长度为size的内存空间;
 *                          *     (2)输入"f n"表示进程n释放所其所占用的内存空间;
 *                               *     (3)输入"e"结束整个程序。
 *                                    */

    phys_addr_t base, size, psize;
    char op[10];
    int pno;

    /* 输入初始用户内存空间的起始地址和长度 */
    printf("请输入初始用户内存空间的起始地址:");
    scanf("%d", &base);
    printf("请输入初始用户内存空间的长度:");
    scanf("%d", &size);

    /* 进行内存的初始化工作 */
    mem_init(base, size);
    pcb_init();

    show();

    while (1) {
        scanf("%s", op);
        switch (op[0]) {

        case 'e':
            return 0;
        case 'a':
            scanf("%d%d", &pno, &psize);
            printf("\n动作:%c %4d %8d\n", op[0], pno, psize);
            if (pcb[pno].p_state == NOTEXISIT) {
                if ((pcb[pno].p_base = alloc_mem(psize)) != -1) {
                    pcb[pno].p_len = psize;
                    pcb[pno].p_state = RUNNING;
                }
                else {
                    printf("申请失败\n");
                }
            }
            else {
                printf("进程%d已经存在\n", pno);
            }
            break;
        case 'f':
            scanf("%d", &pno);
            printf("\n动作:%c %4d\n", op[0], pno);
            if (pcb[pno].p_state == NOTEXISIT) {
                printf("进程%d不存在", pno);
            }
            else {
                free_mem(pcb[pno].p_base, pcb[pno].p_len);
                pcb[pno].p_state = NOTEXISIT;
            }
            break;
        }
        show();
    }

}

4、程序流程图

5、结果实现

这一次实验是对内存的管理,用了首次适应算法来分配和回收内存空间,这个算法的优点是高址部分的大的空闲分区得到保留,为大作业的内存分配创造了条件。

实验数据分析:

 

①在程序开始运行时输入初始的用户空间的起始地址0和长度640K(空闲区);

②给作业1分配130K的内存,空闲区有一块剩余510K;

③给作业2分配60K的内存,空闲区有一块剩余450K;

④给作业3分配100K的内存,空闲区有一块剩余350K;

⑤释放作业2所占有的内存空间60K,空闲区有一块350K,还有一块60K;

⑥给作业4分配200K的内存,空闲区有一块150K,还有一块60K;

⑦释放作业3所占有的内存空间100K,与空闲区一块60K的合并为160K,还有一块150K;

⑧释放作业1所占有的内存空间130K,与空闲区一块160K的合并为290K,还有一块150K;

⑨给作业5分配140K的内存,空闲区剩余有两块150K;

⑩给作业6分配60K的内存,空闲区剩余一块90K和150K;

⑪给作业7分配50K的内存,空闲区剩余一块40K和150K;

⑫释放作业6所占有的内存空间60K,空闲区剩余一块40K、60K和150K;

⑬输入e结束整个程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值