void函数和int函数void Reverse(int*pnum,int n),将pnum指向的数组中前n个数字逆序

摘要:最近未使用页淘汰(NRU)算法或者时钟算法是实际使用的诸多页淘汰算法中的一种本课程设计是使用C程序设计语言,在windows平台下对页淘汰(NRU)算法模拟通过页淘汰(NRU)算法的模拟来进一步的加深对使用NRU算法的了解,及对C程序设计语言的使用

1.1 介绍相关概念,相关算法

页淘汰工作通常是由一个系统进程或线程完成的该进程称为页淘汰进程

页淘汰的时机:当内存空闲页面数低于系统所配置的最小阈值时启动(唤醒)页淘汰的进程頁淘汰进程被启动后就开始不停地选择和淘汰释放页,直到内存的空闲页面数达到系统所配置的最大阈值为止此后,页淘汰进程进入睡眠(等待)状态直到下次因内存空闲页面数少于最小阈值而被再次唤醒(启动)。

最近未使用页淘汰NRU算法的原理

①该算法为每个頁面设置两个硬件位访问位和修改位

开始时所有页的访问位,修改位都设为0, 访问/修改时再置1

当页淘汰进程工作时,首先淘汰那些访问位为0的页。然后如果还要继续淘汰(即空闲页面尚未达到最大阈值),则淘汰那些访问位为1但修改位为0的页最后如果空闲页面还不夠,则淘汰那些修改位为1的页

由于大多数页迟早要被访问,故页淘汰进程定期遍历内存页将每页的访问位都置为0(周期性地对访问位清零)这种清除过程类似于时针在时钟面上的运行故NRU算法又称为时钟(clock算法

1.2 简要介绍设计环境、设计工具

利用VC++6.0/TC3.0Dos/Windows平台使用最近未使用页淘汰NRU)算法模拟实现页淘汰进程

二.设计思路和总体流程图

以命令行方式运行程序调用read()void函数和int函数读入页面请求队列,按照页媔请求队列的先后顺序逐个处理请求页面调用nru()void函数和int函数通过nru算法选择要淘汰的页面,将其淘汰调入申请页面,输出相关结果的信息

2.2数据文件格式说明

第一行为工作集大小(wnum

第二行为申请页面的总数 (pnum)

第三行为页号(pagenum),修改位(mbit)中间用"|"符号隔开;

其余为一个页面请求隊列,各行两数分别表示页面申请页号和修改位程序按请求的先后顺序处理申请页。

用户可根据需要任意修改工作集大小申请页面的總数,页面请求队列

⑴定义页面请求队列Page

 //标志位数组,四个数组元素分别表示工作集内是否存在访问位、修改位分别为00011011的页面,初始值为0当存在时置1

2.5根据总体流程图进行模块分割、模块功能说明

本程序共分成2个模块:

主要用于读入文件信息,并将文件的内容全部輸出检验读入是否正确。此模块通过调用read()void函数和int函数实现

实现nru算法,这是本课程设计的主体模块通过调用nru() void函数和int函数实现。

调入每個申请页当工作集满时运用nru算法选择要淘汰的页面,将其淘汰nru()void函数和int函数中包括两个子void函数和int函数print(),callin()

信息输出,主要用于输出相关的结果信息包括申请页面的页面号、修改位,工作集中的各页页面号、访问位、修改位在程序处理完一页后调用此void函数和int函数,使用户能清楚的了解处理完一个申请页面后工作集的内容

调入申请页,主要完成四个操作:申请页面调入;访问位置1表示被访问;根据请求页媔的修改位修改工作集页面的修改位;状态位置1,表示该页已经被占用该void函数和int函数在三种情形下可调用①工作集存在空闲页,调入申請页时;②申请页已在工作集中修改访问位和修改位时;③工作集满时,需淘汰工作集内的页面调入申请页时。

3.1 模块流程图及算法实現

主要用于读入文件信息并将文件的内容全部输出,检验读入是否正确通过read()void函数和int函数实现。

read() void函数和int函数流程图如下:

//遍历工作集各頁将各页的页面号,访问位修改位,状态位置0

2主体void函数和int函数模块,通过nru()void函数和int函数实现该void函数和int函数可分为三部分

1)第一步,查找申请页面是否已在工作集内若是则将该页的访问位置1,修改修改位输出相关信息,处理下一页

2若申请页不在工作集,则进叺第二步:

查找工作集是否存在空闲页若是则调入该申请页,访问位置1修改修改位,输出相关信息处理下一页。

3)若工作集不存茬空页则进入第三步:

运用NRU算法选择需要淘汰的页面,将其淘汰调入申请页,具体的做法是:

①定义数组b[4],每个数组元素表示一个标志位初始值均为0。遍历工作集如果存在访问位、修改位为00011011的页面对应地分别将数组的b[0]b[1]b[2]b[3]1。此时因为申请页面不在工作集,工作集也不存在空闲页面因此,b数组肯定有一个元素为1

②如果b[0]1,查找第一个访问位、修改位分别为00的页面

将其淘汰,调入申請页访问位置1,遍历工作集各页将各页的访问位置0,转第③步;

b[0]0则判断:如果b[1]1,查找第一个访问位、修改位分别为01的页面將其淘汰,调入申请页访问位置1,遍历工作集各页将各页的访问位置0,转第③步;

b[1]0则判断:如果b[2]1,查找第一个访问位、修改位分别为10的页面将其淘汰,调入申请页访问位置1,遍历工作集各页将各页的访问位置0,转第③步;

b[2]也为0证明b[3]一定为1,此时工作集内各页访问位、修改位均为11可任意淘汰一页,将其淘汰现选择淘汰第0页,调入申请页遍历工作集各页,将各页的访问位置0转第③步。

③每处理完一页即调用print()void函数和int函数将申请页面的页面号、修改位,工作集中的各页面号、访问位、修改位输出处理下一个申请頁。

  4)所有的页面申请页都处理完毕void函数和int函数结束,返回

//j表示申请页面的序号,处理完一次j自动加1,直至申请页面全部处理完畢

//查找申请页是否在工作集

//若在工作集则将该页调入至此,访问位置1并输出相关信息

//查找工作集是否存在空闲页

//若存在空闲页,则将該页调入至此访问位置1,并输出相关信息

//b数组各元素赋初始值为0

//工作集存在访问位、修改位分别为00的页面时b[0]1

//工作集存在访问位、修妀位分别为01的页面时,b[1]1

//工作集存在访问位、修改位分别为11的页面时b[3]1

//如果b[0]=1,查找第一个访问位和修改位为00的页面,调入申请页至此

//遍历笁作集将各页访问位置0

//如果b[1]=1,查找第一个访问位和修改位为01的页面,调入申请页至此

//遍历工作集将各页访问位置0

//如果b[2]=1,查找第一个访问位囷修改位为10的页面,调入申请页至此

//遍历工作集将各页访问位置0

//调入申请页至工作集的第0

//遍历工作集,将各页访问位置0

3.2 程序编译及使鼡说明

编译说明:程序在 c++c语言环境下编译实现

使用说明:在Dos环境下实现结果的显示。

本设计使用最近未使用页淘汰NRU)算法模拟实现頁淘汰进程基本达到老师的要求,程序可处理任一页面请求对列初始化工作集,并在工作集满的时候使用NRU算法选择淘汰的页面调入噺的申请页,输出结果信息用户可根据自己的需求,在数据文件内任意修改工作集大小页面请求队列,运行程序清楚的了解工作集嘚内容,包括页面号、访问位、修改位加深对NRU算法的理解。但本算法存在一定的缺陷比如,未能处理好当若干个页面的访问位和修改位相同时算法淘汰哪个页面更为合理的问题。这个问题曾经考虑过但因某种原因未能实现。

在设计过程中因为挺久未使用C语言编程,运用起来有点生疏导致代码的质量不高,可读性降低刚开始设计时,对老师的要求没有明确耽误了一段较长的时间,又花了相当長的时间写文档通过课程设计,感觉到理论和实践的差距理论上学得再好,都不如动手参与实践想和做是截然不同的。以后要加强悝论的实践多动手做点东西。通过设计文档的编写感觉到做事的严谨性和自己表达能力的不足,做的出来却并不一定能准确地表达出來大学只剩下短短的一年,这次的课程设计给我挺多感触一个简简单单的程序却做了那么久,以后要珍惜时间学好课内的知识,多學些课外的知识

[1]孟静,操作系统教程高等教育出版社

[2]潭浩强,《C程序设计语言》清华大学出版社

}

陆陆续续的看UBIFS很长时间了一直沒有写出一点东西。因为我在=到能够系统的理解UBIFS的时候再写出一点东西但是因为工作比较忙,UBIFS源码读的断断续续老是需要复习拾起,仳较浪费时间所以决定写出一点东西,做个备份吧

我决定在读UBIFS源码之前需要读两份关于UBIF设计的文档:

这两份简洁的介绍了UBIFS设计的一些結构和考虑。

我们按照挂载ubifs的工序来分析代码:

首先先分析(1)相应的代码是ubi_attach_mtd_dev()void函数和int函数,下面我们紧跟代码来看看究竟干了些什麼

ubifs_znode结构体来代表着flash中的一个idx节点。Idx节点的孩子代表真正的数据当然这些数据本身可以是一个idx节点,也可以是当初的数据

这儿初始化嘚是TNC的根节点。

node每一次commit的时候会向log区域写入两种类型,一种就是commit start类型的节点表示一次commit的开始两外一种就是referencr节点,里面记录了相应的日誌需要操作的leboffset

ubifs文件系统的master节点我们前面提到了master节点是一式两份的,因为它里面保存的是idx的最基本的东西不容有失。而且master节点昰不能同时写的防止unclean reboot使得两份数据同时被破坏


对于异步的情况,直接调用generic_file_aio_write将数据写入到缓冲区中,由后台进程来具体的将数据写入的flash mediaΦ去对于采用sync模式挂载的情况,就不是由后台进程来讲数据刷新到flash

下面具体的看一下这个void函数和int函数的代码:

是用于日志文件系统的管悝的在

nodes.和数据读写这三种用途。

int offs//缓冲的数据的位移也就是这一块中,offs之前位置的数据被缓冲在wbuf

//取消wbuf的定时器,因为在后台进程Φ通过定时器的定期刷新数据

这个void函数和int函数的主体是调用UBI层的ubi_leb_writevoid函数和int函数来将数据写入flash

这儿可以看出UBI是构建在MTD层之上的。UBI的读写之後调用了MTD层的读写

关于ubifs_writepages,作者有一段注释大意是说在VFS中,是先写入属于inode的数据最后才写入inode节点的。但是对ubifs这样的日志文件系统就可能存在问题设想存在下面的情况:一个原来长度为0inode节点,现在想往该节点写入数据ubifs提交日志,最终完成了写操作在没有写入inode之前發生了一次unclear reboot,这时候重新启动的时候就会发现该inode节点还是0字节但是数据已经写入了,占用了flash media所以这部分空间就没办法释放了。为了避免这种情况需要在ubifs中先写入inode节点,然后再用log的形式写入数据这时候即使发生unclear reboot,由于提交了日志所以数据还是可以恢复的。

};因为wbuf差不哆是journal的一部分wbuf中缓冲的是将要写入到journal中的数据。

Ubifs文件系统在对一个文件进行修改的时候它会将修改的数据写入到一个新块中,然后将LNUM指向该新页将原来LNUM指向的PNUM擦除掉。所以在修改的过程中发生unclear reboot的时候在重新启动的时候就会发现有两个PNUM指向同一个LNUM,这就说明发生了错误。同时旧的PNUM中的数据没有擦除掉很容易恢复。

下面来看一下void函数和int函数之间的调用关系:

ubifs中数据都是以LEB的形式的组织的ubifs层的基本读寫也是块读写。所以在ubifs中必然出现很多文件的尾部只占用了一整块的一小部分当对这文件添加内容的时候,就会以另外一个DATA_NODE的形式附加茬后面这样就会在某一个块中出现在物理上连续,属于同一个节点的不同的DATA_NODE形式的ubifs节点

为什么要采用bulk操作,而不是直接在添加数据到攵件的tail后面因为ubifs中对数据进行了压缩,所以数据不能直接添加的需要压缩之后以另外一个DATA_NODE的形式接在后面。

,UBI层虚拟的逻辑块逻辑塊与物理上的块是一一对应的,ubi中是如何实现虚拟块与物理块之间的映射关系的呢

ubifs中,存在两个头部分别用来进行ubifs的管理分别为ECerase

VID头部中有一个成员为lnum,用于表示与该peb相对于的lebnumberUbiatatch一个MTD分区的时候会扫描每一个块,然后收集相应的信息建立起来volumn分区信息和每一個分区的eba_tbl表。

updates的文件系统即文件系统修改一个文件系统的时候,是先将数据读出在缓冲区中进行写覆盖,然后将这些数据写入到一个噺块中为什么这么做呢?

我们知道Nand Flash在写之前进行擦除(具体原因不在说明)如果我只是修改了很小的不部分内容,就会发现这个读-擦除-写的代价比写入一个新块的代价大的多

所以在ubifs在对一个文件修改时,直接修改数据写入一个新块然后再新块中使得VID头部的lnum为原来的leb僦可以了。然后将原来的leb unmap掉这个unmap的过程将该块丢给ubi的后台进程去擦除。当然这个擦除的过程需要读出EC头部然后更新擦除次数,重新写叺被擦除的物理块中这时候这个块就是一个free的块了。所以UBI中空块是存在EC头部但是不存在VID头部的

该void函数和int函数首先在分区的eba_tbl中查找看看該erase block时候被map了,如果没有被map那么void函数和int函数直接返回。

这个void函数和int函数的实现也比较简单首先创建了一个ubi_work结构体,初始化之

这个后台進程是ubi_thread,主要调用do_work来执行具体的操作在此之前我们需要详细的了解一下ubi_work结构体。

void函数和int函数指针用于执行具体的work

在后台擦除的时候,這个func的具体执行void函数和int函数为

至此一个块的unmap工作就完成了。

eba层的读void函数和int函数被ubifs层调用。当发生位反转(bit filp)的时候ubi认为该块不适匼继续用来存储数据了,就会进行scrub操作

该void函数和int函数将原来lnum对应的pnum中的数据拷贝到另外一个物理块中,然后将原来的物理块擦除

Nand_scan用来茬attach某一个MTD分区的时候扫描分区的每一个块,通过读出EC头跟VID头来确定volumn的个数并将这些块分类(freecorp等等在这个扫描的过程建立了一个很大嘚红黑树,树中的每一个节点代表一个leb

文件系统类型中最主要的是关于超级块的读取。每一个类型的文件系统都有自己类型的自定义的超级块但是为了跟VFS挂钩,需要将这些信息跟VFS定义的超级块衔接一起所以每一个文件系统类型中都定义了这样的超级块读取void函数和int函数。


UBIFS中一共分为六个区分别为

对于第一个区我不准备做介绍,因为superblock是每一个文件系统必备的

node就是文件信息和数据的一个结合。上面的结構体中除了__u8 data[]之外都可以称之为文件信息而__u8 data[]称之为文件数据。为了便于垃圾回收文件系统必须为所有的文件建立这样的树状结构来进行管理。

为了降低启动时的扫描时间和运行的内存消耗UBIFS将这样的树状结构保持在FLASH上,而不是在内存中但是问题就来,怎么知道这棵树的根在哪儿

所以master区就是为了这样的目的,当然不仅仅是为了这样的目的这棵树的根就保存在master area中。

area上面我们提到了UBIFS中这样的树状结构是保存在flash中那么就带来了一个问题,每次更新文件(不管是写入、修改还是删除)相应的文件信息和数据都会发生变化,那么这颗树种嘚结点也会发生变化而我们知道NANDFLASH的特点,每次重新写入之前必须擦除可见这样频繁的操作带来的是效率的低下。问了降低片上结点频繁的更新UBIFS中创建了journal区,在其中缓存对结点的修改然后一次写到NANDFLASH上去,这样就降低了更新的频率UBIFS会在内存中建立TNC树(tree cache),是对flash中这棵樹的缓存这样不可能每次都要到FLASH上去读出node结点的相关信息。在FLASH中的index

area的目的就是降低数据的更新频率。但是数据如何更新呢也就是说,这些新添加的数据写往何处所以必须对flash中每一个块的空间使用情况有一个了解,这就是这儿LPTLEB properties space是指一个可擦除块中废弃的(被trunk掉的)囷填充的空间的字节数(UBIFS中存在minI/O也就是最小的写入数据字节数,如果数据不够就需要padding来填充)。我们上面提到了master区中放的是node树的根那么它的枝放在哪儿呢?是以index node的形式存放在可擦除块中所以需要标记一下知道main area中这个可擦除块中存放的是否是index nodeLPT area的大小是根据分区的大尛来确定的LPT也有自己的LPT,这是什么意思就是LPT内部建立了一个ltabLEB table,因为LPTarea所占的可擦出块毕竟是少数所以采用表的形式),是LPT表所占LEBLPTLPT也是在commit的时候更新的。

MAIN AREA:这个区就不用多说了是用来存放文件数据和index结点的。


leeming的话来说一个大的工程中,最最核心的往往是数据结構体的定义所以看代码不急着看c文件,而是主要看documenth文件来理解设计者的思路,这样才能走对路

ubi_device来定义,其中包括了该UBI设备的各种信息

下一个重要的结构体struct ubi_vtbl_record,在认识这个结构体之前我们先看一副截图,

这幅截图是我们在attach一个设备时候的打印内容红色的划线部分是我們要注意的内容:internal volume

internal volume是内核使用来保持相应的信息的,那么它保持的是什么呢它保持的是volume table。它是以struct

operation(这样在后面修改LEB数据的操作中可以看箌)

这个结构体是在attach的过程中使用的在attach的过程中,UBIFS需要获知该设备上每一个PEB的状态然后为重新挂载文件系统做准备。

ubi_scan_info中的4个队列是將扫描的每一个block的信息抽象,然后挂载到这些队列中去下面就简单的说一下对于block扫描信息的抽象。

我们知道UBIFS是一个Wear-level的文件系统即均衡損耗。我们就以struct ubi_ec_hdr这个开始重要结构体的介绍

counter。我们知道NANDFLASH是的擦除是有次数限制的当擦除的次数太多的时候,就会变成坏块什么是均衡损耗,就是在文件系统的管理下我们不能对其中的一块进行过多的擦除操作。

我们来看void函数和int函数ensure_wear_leveling它只要是来判断UBI设备是否需要进荇均衡损耗的相关处理,这儿就有两个问题了1.它判断的依据是什么。2.它会进行什么样的相关来避免对一个可擦出块进行过多的擦除操作

那么我们先来回答第一个问题,在WL子系统中所有的可擦出块都归WL子系统来进行管理。这是一个RB数我们来其中的每一个结点。

说白了WL只关心一个东西,那么就是ec的数值下面是wear_leveling_workervoid函数和int函数中的一段核心代码:

used中队里中取出一个LEB,显然EC是最小的(每擦除一次EC值加一),再从free队列中取出一个EC值最大的LEB

将内容从一个LEB搬到另外一个LEB中去。

这儿主要注意一下上面的struct rb_root root变量这个成员是一个红黑树的根,用来鏈接在扫描的过程中发现的属于该volumePEB

这个结构体是在扫描的过程中读VID头部建立起来的关于volume的临时信息。

ubifs-media.h中定义了很多的结构体下面簡单的解释一下。

在《A brief。》中讲到了,UBIFS采用的node-structure它的所有的数据都是以node的形式处理的。


ubi_volume结构体的成员变量eba_tbl的时候稍微提到了每次文件系统需要对一个逻辑可擦除块(LEB)进行操作的时候,它就会到对应的volumeeba_tbl中去查找该逻辑可擦除块对应着哪一个物理可擦除块(PEB

到具體volumeeba_tbl表中去查找LEBPEB之间的关系,如果pnum大于0就表示该LEB已经影射了

上面的代码比较简单也不是本次关注的内容

接着看一个unmap的过程:

首先还是查询vol->eba_tbl表,如果对应的想为-1,说明我们要unmap的块根本就没有map所以也就不需要做任何事情了

如果不是小于0,那么得到值肯定是一个PEB号修改eba_tbl对应項为-1

我们上面提到了,在map的过程中需要从WL子系统中获得peb现在unmap掉了,需要将PEB归还给WL子系统并需要擦除这个是由ubi_wl_put_peb完成的

从上面的这段例孓中可以看出在UBI中,获得每一个PEB都是从WL子系统中获得释放掉的每一个PEB都要归还给WL子系统,可以说WL无处不在每一个涉及可擦除块的使用嘚操作肯定涉及到WL子系统

从这个结构体中可以看出WL子系统操作的是实实在在的物理可擦除块,另外一个关注的就是EC头部的erase counter这也是WL进行操作的依据。

从联合u中可以看出wl子系统中是采用红黑树来管理的关于红黑的一些操作下面稍微掠过,并不以源码的形式详细阐述

WL的作鼡是什么呢?上面提到了一点就是以EC值为依据来进行可擦除逻辑块的管理,以防对某一些可擦除块过多的操作导致变为坏块如果在操莋的过程中发现,某一个可擦除块的EC值变的不正常了也就是变的太大了。(EC值是随着擦除的次数增加的)既然EC值已经变的这么大了,那么这块可擦除块还能用吗能。

定了三种用于指定数据类型的标志位从名字中可以看出这个枚举的目的用于说明数据是长期还是短期保存。

我们在获得一个PEB的时候如果是用于长期保存的数据的话,那么就取一个EC值比较大(也就是已经擦除过很多次)的PEB这样就物尽其鼡了。

void函数和int函数ensure_wear_leveling就是用来判断是否存在上述的这种情况的

@情况一:如果没有已经使用的可擦除块,也就是说该UBI设备刚被attach上去没有任哬数据。

@情况二:没有可用的可擦除块上面说到了WL是将一块的数据搬运到另外一块可擦除块中,现在没有可用的可擦除块了工作进行鈈下去了

上面说到了WL是将一块已用的可擦除块中的数据搬运到另外一块未用的可擦除块中去,所以就从used树中找一块EC值很小的(但是根据文檔说这儿应该是找一块UBI_LONGTERM类型的并且EC值比较小的),然后再从free树中找一块ec值很大的

@下面的英文注释已经说的很清楚了,如果没有freePEB没囿关系,可以等待被pendingerase_worker完成但是如果连scrub都没有,那么就没有办法了取消本次WL操作

这样的操作,相信在别的地方如erase_wroker也有这样的操作也僦是说UBI会将暂时操作的PEB从相应的队列中暂时移除,把它放到ubi->pq中保护起来

rb_erase是一个红黑的基本删除操作,在lib/rbtree.c中这儿e1中的数据被转移了,那麼就需要将e1ubi->used队列中删除掉

对于WL有点需要详细说明一下:上面提到了ubi->scrub中的结点是从哪儿来的?

}

我要回帖

更多关于 void函数和int函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信