前序
本篇分析f_write()函数,fatfs文件系统对应的不知道文件的读写,也对应了文件的其他的操作,也有文件夹的操作,函数分析确实是一个非常耗时耗精力的事情,此函数分析完之后,就结束函数分析。
分析假设
(1)假设一个磁盘就一个分区。
(2)只分析FAT32文件系统相关的代码。
(3)函数的大部分分析,都写入代码注释中。
(4)重要的注释都回加入很多星号以及数学标号。例如,
/****************** 1.把字符存入lfn的buffer中 *******************/
(5)在f_open()分析时,发现太多的代码,占用了不少的位置,从此篇文章开始,删除错误判定和不重要的代码,减少文章的长度。
f_write函数分析
下面f_write()函数的源码:
FRESULT f_write (
FIL* fp, /* Pointer to the file object */
const void* buff, /* Pointer to the data to be written */
UINT btw, /* Number of bytes to write */
UINT* bw /* Pointer to number of bytes written */
)
{
FRESULT res;
FATFS *fs;
DWORD clst, sect;
UINT wcnt, cc, csect;
const BYTE *wbuff = (const BYTE*)buff;
for ( ; btw;
wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {
if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */
/****** 1.如果当前簇已经写完了,那么就要再找出一个空闲的簇 ********/
if (csect == 0) { /* On the cluster boundary? */
if (fp->fptr == 0) { /* On the top of the file? */
clst = fp->obj.sclust; /* Follow from the origin */
if (clst == 0) { /* If no cluster is allocated, */
clst = create_chain(&fp->obj, 0); /* create a new cluster chain */
}
} else { /* On the middle or end of the file */
if (fp->cltbl) {
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
} else
{
clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
}
}
if (clst == 0) break; /* Could not allocate a new cluster (disk full) */
if (clst == 1) ABORT(fs, FR_INT_ERR);
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
fp->clust = clst; /* Update current cluster */
if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
}
if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= (BYTE)~FA_DIRTY;
}
sect = clust2sect(fs, fp->clust); /* Get current sector */
if (!sect) ABORT(fs, FR_INT_ERR);
sect += csect;
cc = btw / SS(fs); /* When remaining bytes >= sector size, */
/******** 2.先把整数倍扇区的内容,直接通过用户buffer直接写入磁盘,然后更新 ********/
if (cc) { /* Write maximum contiguous sectors directly */
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect;
}
if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
fp->flag &= (BYTE)~FA_DIRTY;
}
wcnt = SS(fs) * cc; /* Number of bytes transferred */
continue;
}
if (fp->sect != sect && /* Fill sector cache with file data */
fp->fptr < fp->obj.objsize &&
disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {
ABORT(fs, FR_DISK_ERR);
}
/******* 3.更新fp的当前扇区的值fp->sect *********/
fp->sect = sect;
}
wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */
/****** 4.把不足1个扇区的内容写入到fp->buf,然后标记fp->flag“脏”,这样在有机会的时候就会把此扇区写入到磁盘 **********/
mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
fp->flag |= FA_DIRTY;
}
fp->flag |= FA_MODIFIED; /* Set file change flag */
LEAVE_FF(fs, FR_OK);
}
f_wirte()和否_read()的过程大致相同:
(1)先把整扇区的内容,直接通过用户的buffer直接写入磁盘。
(2)再把不足整扇区的内容,写入fp对应的buf。
(3)最后调整fp->sect的值。
核心思想总结
先把整扇区的内容直接写入到磁盘,把剩余部分写入fp的buf,在之后合适的时机就会写入到对应的扇区,同时更新fp->sect的值。
总之,写文件描述符fb的sect也是指向当前扇区,buf里面存储的是当前sect的内容。