分页式存储管理

要求:

在第1部分实验基础上实现进程的分页式内存分配和地址转换过程,并进一步实现请求分页式存储分配和地址转换过程。页面置换算法至少应实现先进先出(FIFO)、最近最久未使用(LRU)等算法。

  1. 建立1个位示图,用来模拟内存的分配情况,位示图的位数与设定的物理块个数相同。程序启动时可利用一组随机0和1填充位示图,表示内存已被占用情况。

  2. 1、创建进程时输入进程大小,并根据程序中设定的物理块大小为进程分配物理块,同时建立页表。例如,在上图基础上,若要建立一个大小为5000字节的进程,则:

  • 计算出该进程共有“向上取整(5000/1024)=5”个页,需要占用5个内存块;
  • 建立空的页表,即长度为5的一维整数数组;
  • 从位示图中找出前5个“0”位在整个位示图中的位置号(即i字节j位为0,则该位的位置为8*i+j),并将这些号依次填入页表中,同时把前5个“0”改为“1”,以示对应内存块已经分配。
  1. 输入当前执行进程所要访问的逻辑地址,并将其转换成相应的物理地址。

  2. 进程退出时,根据其页表内容向位示图反向回填“0”。

  3. 扩充页表,将其变成支持请求和置换功能的二维页表(增加存在位等)。创建进程时可装入固定的前三页(或键盘输入初始装入页数,不同进程的装入个数可以不同),其余页装入到置换空间内(置换空间大小应为内存空间大小的1.5-2倍,对其还需建立独立的置换空间位示图)。

  4. 分别采用FIFO或LRU置换算法对地址转换过程中遇到的缺页现象进行页面置换,可将多次地址转换过程中所涉及到的页号视为进程的页面访问序列,从而计算置换次数和缺页率。

  5. 在完成第六步的基础上实现OPT的页面置换算法,并加以比较。

代码

import numpy as np
import math


class PCB(object):
    def __init__(self, name, length):
        self.name = name
        self.length = length
        self.start = 0
        self.page = 0
        self.pages_in_ram = 0
        self.page_list_FIFO = [[], []]  # 页表
        self.page_list_LRU = [[], []]
        self.ram_list_FIFO = []  # 在内存的进程的页表
        self.ram_list_LRU = []
        self.ram_FIFO = []
        self.ram_LRU = []
        self.visit = []  # 访问过的页
        self.total = 0
        self.missing_FIFO = 0
        self.missing_LRU = 0
        self.swap = []

    def show(self):
        print(self.ram_list_FIFO)


def create(p, ready, run):
    global name_list
    l = len(name_list)
    name_list[p.name] = 1
    if l == len(name_list):
        print('不能创建同名进程!')
        return
    p.page = math.ceil(p.length / (block_size * 1024))   # 向上取整得页数
    p.page_list_FIFO[0] = [0] * p.page
    p.page_list_FIFO[1] = [0] * p.page
    p.page_list_LRU[0] = [0] * p.page
    p.page_list_LRU[1] = [0] * p.page
    # print(p.page, p.page_list)
    pages = int(input('进程共有'+str(p.page)+'页, '+'输入装入内存的页数:'))
    p.pages_in_ram = pages
    global ram
    global switch
    c = 0
    if ram.shape[0] * ram.shape[1] - np.count_nonzero(ram) >= pages:  # 空闲空间比用户要求的页表大
        for i in range(ram.shape[0]):
            for j in range(ram.shape[1]):
                if ram[i][j] == 0:
                    ram[i][j] = 1
                    p.ram_FIFO.append(8 * i + j)
                    p.ram_LRU.append(8 * i + j)
                    c += 1  # 空出pages页给进程
                    if c >= pages:
                        break
            if c >= pages:
                break
        c = 0
        for i in range(switch.shape[0]):
            for j in range(switch.shape[1]):
                if switch[i][j] == 0:
                    p.page_list_LRU[0][c] = 8 * i + j  # 将进程存入置换空间
                    p.page_list_FIFO[0][c] = 8 * i + j
                    p.swap.append(8 * i + j)
                    switch[i][j] = 1
                    c += 1
                    if c >= p.page:
                        break
            if c >= p.page:
                break
        ready_to_run(ready, run)
        print(p.page_list_FIFO)
    else:

        print('剩余内容不足!剩余块数为:' + str(ram.shape[0] * ram.shape[1] - np.count_nonzero(ram)))
        print('...正在将剩余内存分配给该进程...')


def ready_to_run(ready, run):
    # print(type(run))
    if not run and ready:
        p = ready.pop(0)
        # print(type(p))
        run.append(p)
        # print(ready, run)


def block(ready, blo, run):
    if run:
        pro = run.pop()
        blo.append(pro)
        ready_to_run(ready, run)
    else:
        print('没有可以阻塞的进程!')


def times_over(run, ready):
    if run:
        pro = run.pop()
        ready.append(pro)
        ready_to_run(ready, run)
    else:
        print('没有正在运行的进程!')


def wake_up(blo, ready, run):  # block[0]->ready
    if blo:
        pro = blo.pop(0)
        ready.append(pro)
        ready_to_run(ready, run)
    else:
        print('没有可唤醒的进程!')


def address_switch(log, p):
    global block_size
    page_num = log // (block_size * 1024)  # 访问的页号
    if page_num < len(p.page_list_FIFO[0]):  # log // (block_size * 1024)为页号
        p.total += 1
        # if p.page_list[1][page_num]:  # 如果在内存就转换地址
        if len(p.ram_list_FIFO) < p.pages_in_ram:  # 如果内存没满,ram列表不为空
            if page_num not in p.ram_list_FIFO:  # 不在内存
                p.missing_FIFO += 1
                p.ram_list_FIFO.append(page_num)
                p.page_list_FIFO[0][page_num] = p.ram_FIFO.pop()  # 将属于进程的内存分一块给访问的页
                p.page_list_FIFO[1][page_num] = 1
            if page_num in p.ram_list_LRU:  # 如果在内存,更新位置
                # print('!!!!')
                i = p.ram_list_LRU.index(page_num)
                p.ram_list_LRU.pop(i)
                p.ram_list_LRU.append(page_num)
            if page_num not in p.ram_list_LRU:
                p.missing_LRU += 1
                p.ram_list_LRU.append(page_num)
                p.page_list_LRU[0][page_num] = p.ram_LRU.pop()
                p.page_list_LRU[1][page_num] = 1

        else:
            if page_num not in p.ram_list_FIFO:
                p.missing_FIFO += 1
                first = p.ram_list_FIFO.pop(0)  # 先进的页号
                p.ram_list_FIFO.append(page_num)
                # 交换出去的和进来的页数在内存和置换空间的位置
                p.page_list_FIFO[0][page_num], p.page_list_FIFO[0][first] = p.page_list_FIFO[0][first], \
                                                                            p.page_list_FIFO[0][page_num]
                p.page_list_FIFO[1][page_num] = 1
                p.page_list_FIFO[1][first] = 0
            if page_num in p.ram_list_LRU:  # 如果当前页数在内存里
                i = p.ram_list_LRU.index(page_num)
                p.ram_list_LRU.pop(i)
                p.ram_list_LRU.append(page_num)
            else:
                p.missing_LRU += 1
                before = p.ram_list_LRU.pop(0)
                # print('before:', before)
                p.ram_list_LRU.append(page_num)
                # 交换出去的和进来的页数在内存和置换空间的位置
                p.page_list_LRU[0][page_num], p.page_list_LRU[0][before] = p.page_list_LRU[0][before], \
                                                                            p.page_list_LRU[0][page_num]
                p.page_list_LRU[1][page_num] = 1
                p.page_list_LRU[1][before] = 0

        p.visit.append(page_num)
        page_FIFO = p.page_list_FIFO[0][page_num]
        page_add_FIFO = log % (block_size * 1024)
        page_LRU = p.page_list_LRU[0][page_num]
        page_add_LRU = log % (block_size * 1024)
        # print(page_FIFO, page_add_FIFO)
        print('FIFO:', p.page_list_FIFO)
        print('FIFO:', p.ram_list_FIFO)
        print('FIFO下的物理地址:' + str(hex(page_FIFO * block_size * 1024 + page_add_FIFO)))
        print('FIFO缺页率:' + str(p.missing_FIFO/p.total))
        print('LRU:', p.page_list_LRU)
        print('LRU:', p.ram_list_LRU)
        print('LRU下的物理地址:' + str(hex(page_LRU * block_size * 1024 + page_add_LRU)))
        print('LRU缺页率:' + str(p.missing_LRU / p.total))
        print('访问列表:', p.visit)
        # print('OPT缺页率:', opt(p))
    else:
        print('地址越界!')


def opt(p):
    farthest = {}
    for i in range(p.page):
        farthest[i] = 0
    ram_list = p.visit[:p.pages_in_ram]
    missing = p.pages_in_ram
    for i in range(p.pages_in_ram, len(p.visit)):
        # print(ram_list)
        for k in range(p.page):
            try:
                farthest[k] = p.visit.index(k, i+1, len(p.visit)-1)
            except ValueError:
                farthest[k] = 1000
        now = p.visit[i]
        # print(now)
        # print(farthest)
        if now not in ram_list:
            # print('缺页!')
            missing += 1
            far = max(ram_list, key=lambda x: farthest[x])  # 找到最远的页数
            ind = ram_list.index(far)
            ram_list[ind] = now  # 将最远的页数替换成现在访问的页数
    return missing / p.total


def quit(ready, run):
    p = run.pop()
    global name_list
    del name_list[p.name]
    for k in range(len(p.page_list_FIFO[0])):
        i = p.page_list_FIFO[0][k] // 8
        j = p.page_list_FIFO[0][k] % 8
        if p.page_list_FIFO[1][k] == 1:
            global ram
            ram[i][j] = 0  # 退出时将在内存的页在位示图置零
        else:
            global switch
            switch[i][j] = 0  # 不再内存的页在置换空间中置零
        i = p.page_list_LRU[0][k] // 8
        j = p.page_list_LRU[0][k] % 8
        if p.page_list_LRU[1][k] == 1:
            ram[i][j] = 0  # 退出时将在内存的页在位示图置零
        else:
            switch[i][j] = 0  # 不再内存的页在置换空间中置零
    for k in range(len(p.swap)):  # 统一归还置换空间的位置
        i = p.swap[k] // 8
        j = p.swap[k] % 8
        switch[i][j] = 0
    ready_to_run(ready, run)


if __name__ == '__main__':
    run = []
    ready = []
    blocked = []
    while True:
        try:
            ram_size = int(input("输入内存(K):"))
            block_size = int(input("块的大小(K):"))
            break
        except ValueError:
            print('输入错误!请重新输入!')
            continue
    ram = np.random.randint(0, 2, (ram_size // (block_size * 8), 8), dtype=np.int8)  # 位示图
    switch = np.random.randint(0, 2, size=[(ram_size * 2) // (block_size * 8), 8], dtype=np.int8)
    name_list = {}
    print("0.输出进程")
    print("1.创建进程")
    print("2.进入阻塞状态")
    print("3.时间片完")
    print("4.唤醒")
    print("5.结束进程")
    print("6.显示位示图")
    print("7.地址转换")
    print('8.OPT缺页率')
    print("9.帮助")
    print("10.退出")
    while True:
        try:
            a = int(input())
        except ValueError:
            print('输入错误!请重新输入!')
            continue
        if a == 0:
            pass
        if a == 1:
            pcb_name, pcb_size = input("输入进程的名字及内存(字节):").strip().split()
            pcb_size = int(pcb_size)
            # print(ram)
            pcb = PCB(pcb_name, pcb_size)
            ready.append(pcb)
            create(pcb, ready, run)
        if a == 2:
            block(ready, blocked, run)
        if a == 3:
            times_over(run, ready)
        if a == 4:
            wake_up(blocked, ready, run)
        if a == 5:
            quit(ready, run)
        if a == 6:
            print('内存位示图:\n', ram)
            print('置换空间位示图:\n', switch)
        if a == 7:
            pcb = run[0]
            log = int('0x' + input("输入逻辑地址:"), 16)
            address_switch(log, pcb)
        if a == 8:
            pcb = run[0]
            print(opt(pcb))
        if a == 9:
            print("0.输出进程")
            print("1.创建进程")
            print("2.进入阻塞状态")
            print("3.时间片完")
            print("4.唤醒")
            print("5.结束进程")
            print("6.显示位示图")
            print("7.地址转换")
            print('8.OPT缺页率')
            print("9.帮助")
            print("10.退出")
        if a == 10:
            exit(0)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容

  • 操作系统概论 操作系统的概念 操作系统是指控制和管理计算机的软硬件资源,并合理的组织调度计算机的工作和资源的分配,...
    野狗子嗷嗷嗷阅读 11,917评论 3 34
  • 操作系统对内存的管理 没有内存抽象的年代 在早些的操作系统中,并没有引入内存抽象的概念。程序直接访问和操作的都是物...
    Mr槑阅读 16,686评论 3 24
  • 作者,军哥 来源,军哥说说 2016年6月7日, 这一天,是今年高考的第一天。天公作美,前一晚北京下了一场暴雨,...
    张晓宇枫阅读 910评论 6 10
  • 2017.8.10 星期四 晴转雨 亲子日记(107) 今天要下班了又来了一场雨,可谓是风雨交加。 回家儿子把这几...
    于泽妈妈阅读 141评论 0 1
  • 调用deleteRowsAtIndexPaths之前先调用remove数据源数组方法。
    梁大大大大大壮_阅读 201评论 0 0