yaffs2源代码情景分析

yaffs2源代码分析(最新版) 曾经,有位叫斑点的大牛,写了一篇yaffs2源代码分析,全网都在转载,但是我研读这片好文的时候,真心不爽,因为这篇所使用的yaffs2源代码太老了,现在看很多都对不上啊,那就本人自告奋勇的把里面的源码更新一遍,整体重新学习一遍,奉献给大家! 阅读详情
作者: 斑点
Email: byeyear@hotmail.com

1.前言
略。

2.yaffs文件系统简介
按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。

3.本文内容组织
本文将模仿《linux内核源代码情景分析》一书,以情景分析的方式对yaffs2文件系统的源代码进行分析。首先将分析几组底层函数,如存储空间的分配和释放等;其次分析文件逻辑地址映射;然后是垃圾收集机制;接下来……Sorry,本人还没想好。:-)

4.说明
因为yaffs2貌似还在持续更新中,所以本文所列代码可能和读者手中的代码不完全一致。另外,本文读者应熟悉C语言,熟悉NAND Flash的基本概念(如block和page)。
Ok,步入正题。首先分析存储空间的分配。

5.NAND Flash存储空间分配和释放
我们知道,NAND Flash的基本擦除单位是Block,而基本写入单位是page。yaffs2在分配存储空间的时候是以page为单位的,不过在yaffs2中把基本存储单位称为chunk,和page是一样的大小,在大多数情况下和page是一个意思。在下文中我们使用chunk这个词,以保持和yaffs2的源代码一致。
我们先看存储空间的分配(在yaffs_guts.c中。这个文件也是yaffs2文件系统的核心部分):
static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve,
yaffs_BlockInfo **blockUsedPtr)
{
int retVal;
yaffs_BlockInfo *bi;

if (dev->allocationBlock < 0) {
/* Get next block to allocate off */
dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
dev->allocationPage = 0;
}
函数有三个参数,dev是yaffs_Device结构的指针,yaffs2用这个结构来记录一个NAND器件的属性(如block和page的大小)和系统运行过程中的一些统计值(如器件中可用chunk的总数),还用这个结构维护着一组NAND操作函数(如读、写、删除)的指针。整个结构体比较大,我们会按情景的不同分别分析。useReserve表示是否使用保留空间。yaffs2文件系统并不会将所有的存储空间全部用于存储文件系统数据,而要空出部分block用于垃圾收集时使用。一般情况下这个参数都是0,只有在垃圾收集时需要分配存储空间的情况下将该参数置1。yaffs_BlockInfo是描述block属性的结构,主要由一些统计变量组成,比如该block内还剩多少空闲page等。我们同样在具体情景中再分析这个结构中的字段含义。
函数首先判断dev->allocationBlock的值是否小于0。 yaffs_Device结构内的allocationBlock字段用于记录当前从中分配chunk(page)的那个block的序号。 当一个block内的所有page全部分配完毕时,就将这个字段置为-1,下次进入该函数时就会重新挑选空闲的block。这里我们假定需要重新挑选空闲block,因此进入yaffs_FindBlockForAllocation函数:

[yaffs_AllocateChunk() => yaffs_FindBlockForAllocation()]
static int yaffs_FindBlockForAllocation(yaffs_Device * dev)
{
int i;
yaffs_BlockInfo *bi;
if (dev->nErasedBlocks < 1) {
/* Hoosterman we've got a problem.
* Can't get space to gc
*/
T(YAFFS_TRACE_ERROR,
(TSTR("yaffs tragedy: no more eraased blocks" TENDSTR)));
return -1;
}
dev->nErasedBlocks记录着器件内所有可供分配的block的数量。如果该值小于1,那显然是有问题了。不但正常的分配请求无法完成,就连垃圾收集都办不到了。
for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
dev->allocationBlockFinder++;
if (dev->allocationBlockFinder < dev->internalStartBlock
|| dev->allocationBlockFinder > dev->internalEndBlock) {
dev->allocationBlockFinder = dev->internalStartBlock;
internalStartBlock和internalEndBlock分别是yaffs2使用的block的起始序号和结束序号。也就是说yaffs2文件系统不一定要占据整个Flash,可以只占用其中的一部分。
dev->allocationBlockFinder记录着上次分配的块的序号。如果已经分配到系统尾部,就从头重新开始搜索可用块。
bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder);
if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
dev->sequenceNumber++;
bi->sequenceNumber = dev->sequenceNumber;
dev->nErasedBlocks--;
T(YAFFS_TRACE_ALLOCATE,
(TSTR("Allocated block %d, seq %d, %d left" TENDSTR),
dev->allocationBlockFinder, dev->sequenceNumber,
dev->nErasedBlocks));
return dev->allocationBlockFinder;
}
yaffs_GetBlockInfo函数获取指向block信息结构的指针,该函数比较简单,就不详细介绍了。yaffs_BlockInfo结构中的blockState成员描述该block的状态,比如空,满,已损坏,当前分配中,等等。因为是要分配空闲块,所以块状态必须是 YAFFS_BLOCK_STATE_EMPTY,如果不是,就继续测试下一个block。找到以后将block状态修改为 YAFFS_BLOCK_STATE_ALLOCATING,表示当前正从该block中分配存储空间。正常情况下,系统中只会有一个block处于该状态。另外还要更新统计量ErasedBlocks和sequenceNumber。 这个sequenceNumber记录着各block被分配出去的先后顺序,以后在垃圾收集的时候会以此作为判断该block是否适合回收的依据。
现在让我们返回到函数 yaffs_AllocateChunk中。yaffs_CheckSpaceForAllocation()函数检查Flash上是否有足够的可用空间,通过检查后,就从当前供分配的block上切下一个chunk:
if (dev->allocationBlock >= 0) {
bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
dev->allocationPage;
bi->pagesInUse++;
yaffs_SetChunkBit(dev, dev->allocationBlock,
dev->allocationPage);
dev->allocationPage++;
dev->nFreeChunks--;
/* If the block is full set the state to full */
if (dev->allocationPage >= dev->nChunksPerBlock) {
bi->blockState = YAFFS_BLOCK_STATE_FULL;
dev->allocationBlock = -1;
}
if(blockUsedPtr)
*blockUsedPtr = bi;
return retVal;
}
dev->allocationPage记录着上次分配的chunk在block中的序号,每分配一次加1。从这里我们可以看出,系统在分配chunk的时候是从block的开头到结尾按序分配的,直到一个block内的所有chunk全部分配完毕为止。 retVal是该chunk在整个device内的总序号。PagesInUse记录着该block中已分配使用的page的数量。
系统在设备描述结构yaffs_Device 中维护着一张位图,该位图的每一位都代表着Flash上的一个chunk的状态。yaffs_SetChunkBit()将刚分配得到的chunk在位图中的对应位置1,表明该块已被使用。更新一些统计量后,就可以返回了。
上面就是我们分析的第一个函数,整个过程比较长,因为要顺带介绍一些重要的数据结构。接下来的分析相对就会简短一些,要不您看着累,俺打字更累:-)


作者:斑点
Email: byeyear@hotmail.com

看过chunk分配以后,我们再来chunk的释放。和chunk分配不同的是,chunk的释放在大多数情况下并不释放对应的物理介质,这是因为NAND虽然可以按page写,但只能按block擦除,所以物理介质的释放要留到垃圾收集或一个block上的所有page全部变成空闲的时候才进行。根据应用场合的不同,chunk的释放方式并不唯一,分别由yaffs_DeleteChunk函数和yaffs_SoftDeleteChunk函数完成。我们先看yaffs_DeleteChunk:

void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn)

chunkId就是要删除的chunk的序号,markNand参数用于yaffs一代的代码中,yaffs2不使用该参数。
参数lyn在调用该函数时置成当前行号(__LINE__),用于调试。
首先通过yaffs_GetBlockInfo获得chunk所在block的信息描述结构指针,然后就跑到else里面去了。if语句的判断条件中有一条!dev->isYaffs2,所以对于yaffs2而言是不会执行if分支的。在else分支里面只是递增一下统计计数就出来了,我们接着往下看。

if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
bi->blockState == YAFFS_BLOCK_STATE_FULL ||
bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
dev->nFreeChunks++;
yaffs_ClearChunkBit(dev, block, page);
bi->pagesInUse--;
if (bi->pagesInUse == 0 &&
!bi->hasShrinkHeader &&
bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
yaffs_BlockBecameDirty(dev, block);
}
} else {
/* T(("Bad news deleting chunk %d/n",chunkId)); */
}
首先要判断一下该block上是否确实存在着可释放的chunk。block不能为空,不能是坏块。 YAFFS_BLOCK_STATE_NEEDS_SCANNING表明正对该块进行垃圾回收,我们后面会分析; YAFFS_BLOCK_STATE_NEEDS_SCANNING在我手上的源代码中似乎没有用到。
通过判断以后,所做的工作和chunk分配函数类似,只是一个递增统计值,一个递减。递减统计值以后还要判断该block上的page是否已全部释放,如果已全部释放,并且不是当前分配块,就通过yaffs_BlockBecameDirty函数删除该block,只要能通过删除操作(不是坏块),该block就又可以用于分配了。
相比较来说,yaffs_SoftDeleteChunk所做的工作就简单多了。关键的代码只有两行:
static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk)
{
……
theBlock->softDeletions++;
dev->nFreeChunks++;
……
}
这里递增的是yaffs_blockInfo结构中的另一个统计量 softDeletions,而没有修改pagesInUse成员,也没有修改chunk状态位图。那么,这两个函数的应用场合有什么区别呢?
一般来说,yaffs_DeleteChunk用于文件内容的更新。比如我们要修改文件中的部分内容,这时候yaffs2会分配新的chunk,将更改后的内容写入新chunk中,原chunk的内容自然就没有用了,所以要将pageInUse减1,并修改位图;
yaffs_SoftDeleteChunk用于文件的删除。yaffs2在删除文件的时候只是删除该文件在内存中的一些描述结构,而被删除的文件所占用的chunk不会立即释放,也就是不会删除文件内容,在后续的文件系统操作中一般也不会把这些chunk分配出去,直到系统进行垃圾收集的时候才有选择地释放这些chunk。熟悉DOS的朋友可能还记得,DOS在删除的文件的时候也不会立即删除文件内容,只是将文件名的第一个字符修改为0xA5,事后还可以恢复文件内容。yaffs2在这点上是类似的。


1.文件地址映射
上面说到,yaffs文件系统在更新文件数据的时候,会分配一块新的chunk,也就是说,同样的文件偏移地址,在该地址上的数据更新前和更新后,其对应的flash上的存储地址是不一样的。那么,如何根据文件内偏移地址确定flash存储地址呢?最容易想到的办法,就是在内存中维护一张映射表。由于flash基本存储单位是chunk,因此,只要将以chunk描述的文件偏移量作为表索引,将flash chunk序号作为表内容,就可以解决该问题了。但是这个方法有几个问题,首先就是在做seek操作的时候,要从表项0开始按序搜索,对于大文件会消耗很多时间;其次是在建立映射表的时候,无法预计文件大小的变化,于是就可能在后来的操作中频繁释放分配内存以改变表长,造成内存碎片。yaffs的解决方法是将这张大的映射表拆分成若干个等长的小表,并将这些小表组织成树的结构,方便管理。我们先看小表的定义:
union yaffs_Tnode_union {
union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
}
YAFFS_NTNODES_INTERNAL定义为(YAFFS_NTNODES_LEVEL0 / 2),而 YAFFS_NTNODES_LEVEL0定义为16,所以这实际上是一个长度为8的指针数组。不管是叶子节点还是非叶节点,都是这个结构。当节点为非叶节点时,数组中的每个元素都指向下一层子节点;当节点为叶子节点时,该数组拆分为16个16位长的短整数(也有例外,后面会说到),该短整数就是文件内容在flash上的存储位置(即chunk序号)。至于如何通过文件内偏移找到对应的flash存储位置,源代码所附文档(Development/yaffs/Documentation/yaffs-notes2.html)已经有说明,俺就不在此处饶舌了。下面看具体函数。
(to be continued)


作者:斑点
Email: byeyear@hotmail.com

为了行文方便,后文中将yaffs_Tnode这个指针数组称为“一组”Tnode,而将数组中的每个元素称为“一个”Tnode。树中的每个节点,都是“一组”Tnode。
先看映射树的节点的分配。
static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
{
yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);

if(tn)
memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);

return tn;
}
调用yaffs_GetTnodeRaw分配节点,然后将得到的节点初始化为零。
static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev)
{
yaffs_Tnode *tn = NULL;

/* If there are none left make more */
if (!dev->freeTnodes) {
yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES);
}
当前所有空闲节点组成一个链表,dev->freeTnodes是这个链表的表头。我们假定已经没有空闲节点可用,需通过yaffs_CreateTnodes创建一批新的节点。

static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes)
{
......
tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
newTnodes = YMALLOC(nTnodes * tnodeSize);
mem = (__u8 *)newTnodes;

上面说过,叶节点中一个Tnode的位宽默认为16位,也就是可以表示65536个chunk。对于时下的大容量flash,chunk的大小为2K,因此在默认情况下yaffs2所能寻址的最大flash空间就是128M。为了能将yaffs2用于大容量flash上,代码作者试图通过两种手段解决这个问题。第一种手段就是这里的dev->tnodeWidth,通过增加单个Tnode的位宽,就可以增加其所能表示的最大chunk Id;另一种手段是我们后面将看到的chunk group,通过将若干个chunk合成一组用同一个id来表示,也可以增加系统所能寻址的chunk范围。俺为了简单,分析的时候不考虑这两种情况,因此tnodeWidth取默认值16,也不考虑将多个chunk合成一组的情况,只在遇到跟这两种情况有关的代码时作简单说明。

在32位的系统中,指针的宽度为32位,而chunk id的宽度为16位,因此相同大小的Tnode组,可以用来表示N个非叶Tnode(作为指针使用),也可以用来表示N * 2个叶子Tnode(作为chunk id使用)。代码中分别用YAFFS_NTNODES_INTERNAL和 YAFFS_NTNODES_LEVEL0来表示。前者取值为8,后者取值为16。从这里我们也可以看出若将yaffs2用于64位系统需要作哪些修改。针对上一段叙述的问题,俺以为在内存不紧张的情况下,不如将叶节点Tnode和非叶节点Tnode都设为一个指针的长度。

分配得到所需的内存后,就将这些空闲空间组成Tnode链表:
for(i = 0; i < nTnodes -1; i++) {
curr = (yaffs_Tnode *) &mem[i * tnodeSize];
next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize];
curr->internal[0] = next;
}
每组Tnode的第一个元素作为指针指向下一组Tnode。完成链表构造后,还要递增统计量,并将新得到的Tnodes挂入一个全局管理链表yaffs_TnodeList:
dev->nFreeTnodes += nTnodes;
dev->nTnodesCreated += nTnodes;
tnl = YMALLOC(sizeof(yaffs_TnodeList));
if (!tnl) {
T(YAFFS_TRACE_ERROR,
(TSTR
("yaffs: Could not add tnodes to management list" TENDSTR)));
} else {
tnl->tnodes = newTnodes;
tnl->next = dev->allocatedTnodeList;
dev->allocatedTnodeList = tnl;
}

回到yaffs_GetTnodeRaw,创建了若干组新的Tnode以后,从中切下所需的Tnode,并修改空闲链表表头指针:

if (dev->freeTnodes) {
tn = dev->freeTnodes;
dev->freeTnodes = dev->freeTnodes->internal[0];
dev->nFreeTnodes--;
}

至此,分配工作就完成了。相比较来说,释放Tnodes的工作就简单多了,简单的链表和统计值操作:
static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn)
{
if (tn) {
tn->internal[0] = dev->freeTnodes;
dev->freeTnodes = tn;
dev->nFreeTnodes++;
}
}



作者:斑点
Email: byeyear@hotmail.com

看过Tnode的分配和释放,我们再来看看这些Tnode是如何使用的。在后文中,我们把以chunk为单位的文件内偏移称作逻辑chunk id,文件内容在flash上的实际存储位置称作物理chunk id。先看一个比较简单的函数。

void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val)
这个函数将某个Tnode设置为指定的值。tn是指向一组Tnode的指针;pos是所要设置的那个Tnode在该组Tnode中的索引;val就是所要设置的值,也就是物理chunk id。函数名中的Level0指映射树的叶节点。函数开头几行如下:

pos &= YAFFS_TNODES_LEVEL0_MASK;
val >>= dev->chunkGroupBits;
bitInMap = pos * dev->tnodeWidth;
wordInMap = bitInMap /32;
bitInWord = bitInMap & (32 -1);
mask = dev->tnodeMask << bitInWord;

上面说过,一组Tnode中的8个指针在叶节点这一层转换成16个16位宽的chunk Id,因此需要4位二进制码对其进行索引,这就是 YAFFS_TNODES_LEVEL0_MASK的值。我们还说过这个16位值就是chunk在flash上的序号,当flash容量比较大,chunk数量多时,16位可能无法给flash上的所有chunk编号,这种情况下可以增加chunk id的位宽,具体位宽的值记录在 dev->tnodeWidth中。yaffs2允许使用非字节对齐的tnodeWidth,因此可能出现某个chunk id跨32位边界存储的情况。所以在下面的代码中,需要分边界前和边界后两部分处理:

map[wordInMap] &= ~mask ;    //清零
map[wordInMap] |= (mask & (val << bitInWord));     //置位

if(dev->tnodeWidth > (32-bitInWord)) {     //当chunk序号跨越32位边界
bitInWord = (32 - bitInWord);     //计算出已读取部分
wordInMap++;     //读取位置+1
mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord);     //未读取部分
map[wordInMap] &= ~mask;     //清零
map[wordInMap] |= (mask & (val >> bitInWord));     //置位
}

if语句判断当前chunk序号是否跨越当前32位边界。整个代码初看起来比较难理解,其实只要将 dev->tnodeWidth以16或32代入,就很好懂了。还有一个类似的函数yaffs_GetChunkGroupBase,返回由tn和pos确定的一组chunk的起始序号,就不详细分析了。

现在我们假设有这样一个情景:已知文件偏移地址,要找到flash上对应的存储地址,该怎么做呢?这项工作的主体是由函数yaffs_FindLevel0Tnode完成的。

static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev,
yaffs_FileStructure * fStruct,
__u32 chunkId)
{

yaffs_Tnode *tn = fStruct->top;
__u32 i;
int requiredTallness;
int level = fStruct->topLevel;
函数参数中,fStruct是指向文件描述结构的指针,该结构保存着文件大小、映射树层高、映射树顶层节点指针等信息。chunkId是逻辑chunk id。
fStruct->top是映射树顶层节点指针,fStruct->topLevel是映射树层高。注意:当只有一层时,层高为0。
/* First check we're tall enough (ie enough topLevel) */
i = chunkId >> YAFFS_TNODES_LEVEL0_BITS;
requiredTallness = 0;
while (i) {
i >>= YAFFS_TNODES_INTERNAL_BITS;
requiredTallness++;
}
if (requiredTallness > fStruct->topLevel) {
/* Not tall enough, so we can't find it, return NULL. */
return NULL;
}
在看这段代码之前,我们先用一个例子来回顾一下映射树的组成。假定我们有一个大小为128K的文件,flash的page大小为2K,那么我们就需要64个page(或者说chunk)来存储该文件。一组Tnode的size是8个指针,或者16个16位整数,所以我们需要64 / 16 = 4组Tnode来存储物理chunk序号。这4组Tnode就是映射树的叶节点,也就是Level0节点。由于这4组Tnode在内存中不一定连续,所以我们需要另外一组Tnode,将其作为指针数组使用,这个指针数组的前4个元素分别指向4组Level0节点,而fStruct->top就指向这组作为指针数组使用的Tnode。随着文件长度的增大,所需的叶节点越多,非叶节点也越多,树也就越长越高。
回过头来看代码,首先是检查函数参数chunkId是否超过文件长度。作为非叶节点使用的Tnode每组有8个指针,需要3位二进制码对其进行索引,因此树每长高一层,逻辑chunkId就多出3位。相反,每3位非零chunkId就代表一层非叶节点。while循环根据这个原则计算参数chunkId所对应的树高。如果树高超过了文件结构中保存的树高,那就说明该逻辑chunkId已经超出文件长度了。通过文件长度检查之后,同样根据上面的原则,就可以找到逻辑chunkId对应的物理chunkId了。具体的操作通过一个while循环完成:

/* Traverse down to level 0 */
while (level > 0 && tn) {
tn = tn->
internal[(chunkId >>
( YAFFS_TNODES_LEVEL0_BITS +
(level - 1) *
YAFFS_TNODES_INTERNAL_BITS)
) &
YAFFS_TNODES_INTERNAL_MASK];
level--;

}

return tn;

将返回值和逻辑chunk id作为参数调用yaffs_GetChunkGroupBase,就可以得到物理chunk id了。

注:
1. 欢迎转载,转载时请注明作者
2. 未经作者同意,不得用于商业目的
3. 俺近日犯头痛,更新会慢一点



作者:斑点
Email: byeyear@hotmail.com

下面我们看另一个情景,看看当文件长度增加的时候,映射树是如何扩展的。主要函数为
static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev,
yaffs_FileStructure * fStruct,
__u32 chunkId,
yaffs_Tnode *passedTn)
函数的前几行和yaffs_FindLevel0Tnode一样,对函数参数作一些检查。通过检查之后,首先看原映射树是否有足够的高度,如果高度不够,就先将其“拔高”:
if (requiredTallness > fStruct->topLevel) {
/* Not tall enough,gotta make the tree taller */
for (i = fStruct->topLevel; i < requiredTallness; i++) {
tn = yaffs_GetTnode(dev);
if (tn) {
tn->internal[0] = fStruct->top;
fStruct->top = tn;
} else {
T(YAFFS_TRACE_ERROR,
(TSTR("yaffs: no more tnodes" TENDSTR)));
}
}
fStruct->topLevel = requiredTallness;
}
for循环完成增加新层的功能。新增的每一层都只有一个节点(即一组Tnode),fStruct->top始终指向最新分配的节点。将映射树扩展到所需的高度之后,再根据需要将其“增肥”,扩展其“宽度”:
l = fStruct->topLevel;
tn = fStruct->top;
if(l > 0) {
while (l > 0 && tn) {
x = (chunkId >>
( YAFFS_TNODES_LEVEL0_BITS +
(l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
YAFFS_TNODES_INTERNAL_MASK;
if((l>1) && !tn->internal[x]){
/* Add missing non-level-zero tnode */
tn->internal[x] = yaffs_GetTnode(dev);
} else if(l == 1) {
/* Looking from level 1 at level 0 */
if (passedTn) {
/* If we already have one, then release it.*/
if(tn->internal[x])
yaffs_FreeTnode(dev,tn->internal[x]);
tn->internal[x] = passedTn;
} else if(!tn->internal[x]) {
/* Don't have one, none passed in */
tn->internal[x] = yaffs_GetTnode(dev);
}
}
tn = tn->internal[x];
l--;
}
}
上面“拔高”的时候是从下往上“盖楼”,这里“增肥”的时候是从上往下“扩展”。
tn->internal[x]为空表示下层节点尚未创建,需要通过yaffs_GetTnode分配之,就是“增肥”了。如果函数参数passedTn有效,就用该组Tnode代替level0上原先的那组Tnode;否则按需分配新的Tnode组。所以这里的函数名似乎应该取作 yaffs_AddOrFindOrReplaceLevel0Tnode更加恰当。不过这个新名字也太长了些……

树的创建、搜索和扩展说完了,下面该说什么?……对了,收缩和删除。不过看过创建搜索扩展之后,收缩和删除已经没什么味道了。主要函数有:
yaffs_DeleteWorker()
yaffs_SoftDeleteWorker()
yaffs_PruneWorker()
前两者用于删除,第三个用于收缩。都是从level0开始,以递归的方式从叶节点向上删,并释放被删除Tnode所对应的物理chunk。递归,伟大的递归啊……俺不想把这篇文章做成递归算法教程,除了递归这三个函数也就不剩啥了,所以一概从略。唯一要说的就是yaffs_DeleteWorker和yaffs_SoftDeleteWorker的区别,这两个函数非常类似,只是在释放物理chunk的时候分别调用yaffs_DeleteChunk和yaffs_SoftDeleteChunk。其中函数yaffs_DeleteWorker在yaffs2中似乎是不用的,而yaffs_SoftDeleteWorker主要用于在删除文件时资源的释放。

p.s. 这个星期犯头疼,没力气写东西,所以短一些;希望下星期能好起来。接下来的几篇打算分析文件对象管理,大家捧个场 :-)



作者:斑点
Emai:byeyear@hotmail.com

7.文件系统对象
在yaffs2中,不管是文件还是目录或者是链接,在内存都用一个结构体yaffs_ObjectStruct来描述。我们先简要介绍一下这个结构体中的几个关键字段,然后再来看代码。在后文中提到“文件”或“文件对象”,若不加特别说明,都指广义的“文件”,既可以是文件,也可以是目录。
__u8 deleted:1; /* This should only apply to unlinked files. */
__u8 softDeleted:1; /* it has also been soft deleted */
__u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/
这三个字段用于描述该文件对象在删除过程中所处的阶段。在删除文件时,首先要将文件从原目录移至一个特殊的系统目录/unlinked,以此拒绝应用程序对该文件的访问,此时将unlinked置1;然后判断该文件长度是否为0,如果为0,该文件就可以直接删除,此时将deleted置1;如果不为0,就将deleted和softDelted都置1,表明该文件数据所占据的chunk还没有释放,要留待后继处理。
struct yaffs_ObjectStruct *parent;
看名字就知道,该指针指向上层目录。
int chunkId;
每个文件在flash上都有一个文件头,存储着该文件的大小、所有者、创建修改时间等信息。chunkId就是该文件头在flash上的chunk序号。
__u32 objectId; /* the object id value */
每一个文件系统对象都被赋予一个唯一的编号,作为对象标识,也用于将该对象挂入一个散列表,加快对象的搜索速度。
yaffs_ObjectType variantType;
yaffs_ObjectVariant variant;
前者表示该对象的类型,是目录、普通文件还是链接文件。后者是一个联合体,根据对象类型的不同有不同的解释。
其余的成员变量,我们在后面结合函数一起分析。

下面我们来看相关的函数。先看一个简单的:
static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number,
__u32 mode)
所谓Fake Directory,就是仅存在于内存中,用于管理目的的目录对象,比如我们上面提到的unlinked目录。这种类型的目录有一些特别的地方,如禁止改名、禁止删除等。由于对象仅存在于内存中,因此不涉及对硬件的操作,所以函数体很简单。首先通过yaffs_CreateNewObject获得一个新对象,然后对其中的一些字段初始化。先把字段初始化看一下,顺便再介绍一些字段:
renameAllowed表示是否允许改名,对于fake对象为0;
unlinkAllowed表示是否允许删除,对于fake对象同样为0;
yst_mode就是linux中的访问权限位;
chunkId是对象头所在chunk,由于fake对象不占flash存储空间,所以置0。
回过头来看yaffs_CreateNewObject:
[yaffs_CreateFakeDirectory --> yaffs_CreateNewObject]
yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
yaffs_ObjectType type)
{

yaffs_Object *theObject;

if (number < 0) {
number = yaffs_CreateNewObjectNumber(dev);
}

theObject = yaffs_AllocateEmptyObject(dev);
前面说过,每个yaffs_Object都有一个唯一的序列号,这个序号既可以在创建对象的时候由上层函数指定,也可以由系统分配。如果number < 0,那就表示由系统分配。序列号分配函数是 yaffs_CreateNewObjectNumber。我们就不深入到这个函数内部了,只说明一下该函数做了些什么:
系统为了方便根据对象id找到对象本身,将每个对象都通过指针hashLink挂入了一个散列表,散列函数是number % 256,所以这个散列表有256个表项。yaffs_CreateNewObjectNumber函数每次搜索10个表项,从中选取挂接链表长度最短的那一项,再根据表索引试图计算出一个和该索引上挂接对象的id号不重复的id。

分配到了id号和空闲对象后,再根据对象类型的不同作不同的处理。我们主要关心两种情况,就是对象类型分别为文件和目录的时候:
case YAFFS_OBJECT_TYPE_FILE:
theObject->variant.fileVariant.fileSize = 0;
theObject->variant.fileVariant.scannedFileSize = 0;
theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */
theObject->variant.fileVariant.topLevel = 0;
theObject->variant.fileVariant.top = yaffs_GetTnode(dev);
break;
case YAFFS_OBJECT_TYPE_DIRECTORY:
INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
break;
fileSize很好理解;topLevel就是映射树层高,新建的文件层高为0。还要预先分配一组Tnode供该对象使用。scannedFileSize和shrinkSize用于yaffs2初始化时的flash扫描阶段,这里先跳过。如果该对象是目录,那么所做的工作只是初始化子对象(就是该目录下的文件或子目录)双向链表指针,前后指针都指向链表头自身。

看过Fake对象创建,我们再看看普通对象的创建。按对象类型的不同,有四个函数分别用于创建普通文件、目录、设备文件、符号链接和硬链接,它们分别是:
yaffs_MknodFile;
yaffs_MknodDirectory;
yaffs_MknodSpecial;
yaffs_MknodSymLink;
yaffs_Link
这四个函数最终都调用yaffs_MknodObject来完成创建对象的工作,只是调用参数不一样。
static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
yaffs_Object * parent,
const YCHAR * name,
__u32 mode,
__u32 uid,
__u32 gid,
yaffs_Object * equivalentObject,
const YCHAR * aliasString, __u32 rdev)
函数参数中,前面几个都很好理解,分别是对象类型,上级目录对象,文件名,访问权限,文件所属user id和group id; equivalentObject是创建硬链接时的原始文件对象;aliasString是symLink名称;rdev是设备文件的设备号。
函数首先检查在父目录中是否已存在同名文件,然后同样调用yaffs_CreateNewObject创建新对象。参数-1表示由系统自行选择对象id。
if (in) {
in->chunkId = -1;
in->valid = 1;
in->variantType = type;
in->yst_mode = mode;
in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
in->yst_rdev = rdev;
in->yst_uid = uid;
in->yst_gid = gid;
in->nDataChunks = 0;
yaffs_SetObjectName(in, name);
in->dirty = 1;
yaffs_AddObjectToDirectory(parent, in);
in->myDev = parent->myDev;
这里列出的代码省略了和wince相关的条件编译部分。chunkId是对象头所在chunk,现在还没有将对象写入flash,所以置为-1;该新对象暂时还没有数据,所以nDataChunks是0。in->dirty = 1表示该新对象信息还没有写入flash。然后通过yaffs_AddObjectToDirectory将新对象挂入父对象的子对象链表。接下来根据对象类型作不同处理:
switch (type) {
case YAFFS_OBJECT_TYPE_SYMLINK:
in->variant.symLinkVariant.alias =
yaffs_CloneString(aliasString);
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
in->variant.hardLinkVariant.equivalentObject =
equivalentObject;
in->variant.hardLinkVariant.equivalentObjectId =
equivalentObject->objectId;
list_add(&in->hardLinks, &equivalentObject->hardLinks);
break;
case YAFFS_OBJECT_TYPE_FILE:
case YAFFS_OBJECT_TYPE_DIRECTORY:
case YAFFS_OBJECT_TYPE_SPECIAL:
case YAFFS_OBJECT_TYPE_UNKNOWN:
/* do nothing */
break;
}
对于最常用的文件对象和目录对象不做任何处理;如果是hardlink,就将新对象挂入原对象的 hardLinks链表。从这里我们可以看出,yaffs2在内存中是以链表的形式处理hardlink的。在将hardlink存储到flash上的时候,则是通过objectId将两者关联起来。Hardlink本身占用一个chunk存储对象头。
最后,通过yaffs_UpdateObjectHeader将新对象头写入flash。




作者:斑点
Email: byeyear@hotmail.com

8. Yaffs2的垃圾收集机制
yaffs2的垃圾收集过程实际上包括两个方面:
·一是对那些不再使用的page作物理上的删除。我们在前面介绍chunk释放函数的时候曾经看到,yaffs2在删除chunk的时候仅仅是修改内存中的统计量,而真正的删除工作要留到垃圾收集的时候做。
·二是处理坏块。在对flash进行写操作的时候,我们也许要使用过一个block上的若干page之后才发现这是一个坏块,此时该块上已经有部分有用数据了,在垃圾收集的时候要对这种情况进行处理。
flash在使用过一段时间之后,满足以上两种情况的block也许不止一个,那么,yaffs2按照什么样的原则挑选合适的块进行回收呢?我们看下面的函数:
static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev,
yaffs_BlockInfo * bi)
这个函数用来判定给定的块bi是否可以回收。
if (!dev->isYaffs2)
return 1; /* disqualification only applies to yaffs2. */
if (!bi->hasShrinkHeader)
return 1; /* can gc */
我们主要关心yaffs2。首先介绍一下什么是 hasShrinkHeader。
还是要提到yaffs2的“软”删除机制。假定我们现在需要减小一个文件的长度,比如从128K缩减到64K,在执行close()系统调用之后,yaffs2会将新的大小写入文件头,而这个文件头是会立即写入flash的,但是由于yaffs2使用软删除机制,原先那后面64K数据仍然残留在flash上,也就是说,出现了文件头和文件内容不一致的情况。此时就将文件头所在block的描述信息中的一个字段hasShrinkHeader置1,表明在垃圾回收时需要特别的处理。如果hasShrinkHeader=0,那么该块是不需要特别的处理,是可以回收的;但是如果hasShrinkHeader=1,那就需要注意了:如果我们所做的不仅仅是文件尺寸的收缩,而是文件的删除,并且在物理删除文件内容之前通过垃圾收集机制将文件头删掉了,那么残留的文件内容就成了“没娘要的孩子”,难以处理了。 所以,我们必须先处理文件的残留内容,然后处理文件头。下面我们来看看yaffs2是如何实现处理这个目标的:
/* Find the oldest dirty sequence number if we don't know it and save it
* so we don't have to keep recomputing it.
*/
if (!dev->oldestDirtySequence) {
seq = dev->sequenceNumber;
for (i = dev->internalStartBlock; i <= dev->internalEndBlock;
i++) {
b = yaffs_GetBlockInfo(dev, i);
if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
(b->pagesInUse - b->softDeletions) <
dev->nChunksPerBlock && b->sequenceNumber < seq) {
seq = b->sequenceNumber;
}
}
dev->oldestDirtySequence = seq;
}

/* Can't do gc of this block if there are any blocks older than this one that have
* discarded pages.
*/
return (bi->sequenceNumber <= dev->oldestDirtySequence);
在分析这段代码之前,我们再来回顾一下yaffs2的chunk分配过程和特点。如前文所述,yaffs2在分配chunk的时候遵循两个原则:一是在block内部严格从低地址的chunk向高地址的chunk按次序分配,二是一定要将一个block内的page全部分配完毕后才另行选择block进行分配。而且在分配的时候每挑选一个block就会递增一个序号。这样我们从block的序号就可以推断出该block的分配顺序。
除此之外,yaffs2会在应用程序作clsoe()系统调用的时候将新的文件头写入flash。因此,我们可以作出这样的结论: 文件头所在block的序号,一定大于等于文件内容所在block的序号。这样,如果一个block信息结构内的hasShrinkHeader字段为1,并且该block的序号在系统中最小,我们就可以认为该block上的所有文件头对应的文件已经没有残余信息留在flash上了——这些残余信息如果存在,它们所在block的序号一定更小。有了这个结论,上面的代码就不难理解了,所以就不作解释了。
这个函数返回之后,我们就知道函数参数所指向的block是否可以回收了。但是,一个flash上可以回收的block大多数情况下不止一个,如何挑选出最合适的一个呢?——且听下回分解:-)

p.s. 最近厂里比较忙,可能更新会慢下来。私人企业,大家都知道的,时间没办法保证啊。

这阵子开始重读yaffs2,发现上次看的时候有很多地方理解上还是有问题的,写出来的东西也不严谨,最主要的是,文章不是很有条理。俺打算写完垃圾收集就暂停,待俺第二遍(也许还有第三遍,第四遍……)看结束的时候,整理一下思路,然后重新排标题,写分析。不知诸位是否有兴趣?
yaffs2分析 yaffs2 源码分析 yaffs2分析 yaffs2 源码分析 不错的文档 立即下载

相关推荐

yaffs文件源码

支持2.4的内核 很好的东西 比较少见 操作步骤请参考http://wangweixing128.blog.163.com/blog/static/2043481802012754630220/

如何确定当前yaffs2文件系统数据参数

对于YAFFS2文件系统来说,我们可以通过cat /proc/yaffs来获取它的第一手资料,当然最主要还是着手于代码。 [dvr]$ cat /proc/yaffs Multi-version YAFFS built:Aug 11 2014 12:20:16 Device 0 "File System" start_block.......... 0 //nand flash当...

weixin_42929891的博客 798

VSCode搭配Keil开发STM32:从环境配置到代码跳转全流程(避坑指南)

本文详细介绍了如何使用VSCode搭配Keil进行STM32开发,从环境配置到代码跳转的全流程指南。通过安装必备插件和正确配置项目文件,开发者可以在VSCode中享受现代编辑器的便捷功能,同时保留Keil的编译调试能力,提升嵌入式开发效率。

weixin_28832121的博客 253

Yaffs goes bad when flash is completelly full,yaffs文件系统在空间占满后,出现错误

yaffs2的文件系统,在文件空间占满后,如果依然不停的写文件,则会在一段时间后出现文件系统的错乱,文件名出现乱码的现象,日志可能出现yaffs tragedy: no more eraased blocks!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!。Charles Manning已经fix这个问题在http://www.

basin9901的专栏 1639

Pytorch版deeplabv3+环境配置训练自己的数据集

参考:https://blog.youkuaiyun.com/qq_36766560/article/details/110009622?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161594607816780266214828%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161594607816780266214828

Ring__Rain的博客 1万+

数据库和BI工程师面试题

 一.数据库 1.Oracle数据库,视图与表的区别?普通视图与物化视图的区别?物化视图的作用? 2.Oracle数据库,有哪几类索引,分别有什么特点? 3.Union与Union All的区别? 4.对游标的理解?游标的分类?使用方法? 5.如何查找和删除表中的重复数据?给出方法或SQL。 6.不借助第三方工具,怎么查看SQL的执行计划? 7.创建索引有哪些需要注意的要点?

web_go_run的专栏 1万+

yaffs2源代码分析(一)

yaffs2源代码分析(一)(转linuxforum 精华) 2007-09-22 20:24 3259人阅读 评论(0) 收藏 举报 yaffs2源代码情景分析 作者: 斑点 Email: byeyear@hotmail.com 1.前

咕唧咕唧shubo.lk的专栏 3681

如何制作微信小程序的在线购物商城应用

首先,你需要规划好商城的整体框架,确定好你的目标用户群体,思考他们的需求是什么。接下来,使用微信开发者工具创建一个新的小程序项目,输入你的AppID,选择好保存路径,点击“创建”,一个新的商城雏形就诞生了。你可以把这里当作自己的家,无论是工作之余的小憩,还是寻找灵感的驿站,我都希望你能在这里找到属于你的那份快乐和满足。数据是现代商业的灵魂,通过数据分析,你可以更准确地把握用户的行为模式,从而做出更明智的决策。

master_chenchen的博客 2428

yaffs2源代码分析

  yaffs2源代码分析[1](经典,值得一阅)作者: 斑点 Email: byeyear@hotmail.com 1、前言 略…… 2、yaffs文件系统简介 按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。 3、本文内容组织 本文将模仿《linux内核

xgbing 8619

ORACLE 12C RAC 启动报错,错误代码 ORA-27300,ORA-27301,ORA-27302

非一体机,12.2 无任何补丁,2套X86服务器,1节点集群异常(数据库正常,还有会话),2节点正常工作,负载不高,硬件、空间均未检查到异常,多路径存在掉线的情况,但通过Mulitpath -ll 看又是正常的,2根私有网络,其中一根不同,sysctl中配置的稀疏模式。5、再次加成crs alert ohas等集群日志,以及根据现象和错误再次到后台sr中查找可用信息,发现有一个类似问题,12.2 RAC 无任何补丁,报同样错误,解决方案为重启两套服务器。查询MOS文档以及后台文档,排除上面指出的问题。

weixin_42344665的博客 1465

OpenClaw人人养虾:接入即时通讯平台Zalo

通过 Zalo Official Account(OA,官方账号)API,可以将 OpenClaw 接入 Zalo,实现面向越南用户的自动化客服和互动。

王国平的博客 437

yaffs2 源码分析

yaffs2源代码情景分析 (小寿转载)<br /><br /> 1.前言 <br /> 略。 <br /><br /> 2.yaffs文件系统简介 <br /> 按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。 <br /><br /> 3.本文内容组织 <br /> 本文将模仿《linux内核源代码情景分析》一书,以情景分析的方式对yaffs2文件系统的源代码

yinxusen的专栏 4305

ADES1831和ADBMS6830

1、上电默认进入STANDBY模式,SPI发送0xFF也可以进入STANDBY模式2、看门狗定时器超时后,进入SLEEP状态3、配置寄存器中的REFON位使用WRCFGA命令将组A设置为1进入REFUP状态,此状态比待机状态更快地启动ADC转换。i<TOTAL_IC;i++)注意:读AFE数据时会随着AFE片数增加而增加,原因是唤醒时间和AFE片数密切相关,如果需要缩短时间,那么减少唤醒频次。

m0_61973119的博客 266

yaffs文件类型_YAFFS2文件系统分析(转)

http://blog.chinaunix.net/uid-25314474-id-343665.html1.前言略。2.yaffs 文件系统简介按理说这里应该出现一些诸如“yaffs 是一种适合于 NAND Flash 的文件系统 XXXXX”之类的字眼,不过考虑到网络上关于 yaffs/yaffs2 的介绍已经多如牛毛,所以同上,略。3.本文内容组织本文将模仿《linux 内核源代码情景分析》...

weixin_39972768的博客 671

pandas教程:2012 Federal Election Commission Database 2012联邦选举委员会数据库

里面包含了支持者的名字,职业和雇主,地址,赞助金额。我们可以已经能想到如何切割、整理数据,来提取赞助者的统计数据,或提取宣传活动的模式,这里我们展示一些不同的分析方法。可以看到有些职业是同一种工作类型,指代的是同一个东西。这两个连续操作是分别独立执行的,一个结束后,另一个才开始。上,这里我是把数据集压缩成了ZIP格式,解压后能正常导入)。我觉得也有必要好好了解一下,这个网站做了很好的讲解,),所以我们最好添加这样的信息。这样的情况,即把两个索引操作连在一起。,发现映射关系被正常执行了,所以这里的。

Cachel Wood的博客 349

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

YAFFS2文件系统分析 2011

http://blog.chinaunix.net/uid-25314474-id-343665.html       1.前言 略。 2.yaffs 文件系统简介 按理说这里应该出现一些诸如“yaffs 是一种适合于 NAND Flash 的文件系统 XXXXX”之类 的字眼,不过考虑到网络上关于 yaffs/yaffs2 的介绍已经多如牛毛,所以同上,略。 3.本文内容组

AS520213211314的专栏 882

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

YAFFS2文件系统分析(转)

http://blog.chinaunix.net/uid-25314474-id-343665.html 1.前言略。2.yaffs 文件系统简介按理说这里应该出现一些诸如“yaffs 是一种适合于 NAND Flash 的文件系统 XXXXX”之类的字眼,不过考虑到网络上关于 yaffs/yaffs2 的介绍已经多如牛毛,所以同上,略。3.本文内容组织本文将模仿《linux 内核源代码情景分...

weixin_33778778的博客 421

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

内核参考书籍及文章

Kernel源码 内核git库: http://git.kernel.org/?p=linux/kernel/git linus-git http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary Kernel书籍 “unde

云行雨施 品物流形 1447
上一篇: Linux下patch的制作和应用
下一篇: u-boot nand flash read/write cmd
张志龙
博客等级 码龄24年 412粉丝 131原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值