fatfs文件系统详解之f_write函数分析

前序

本篇分析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的内容。

你可能感兴趣的:(文件系统)