前序

文件系统的最终目的是为了进行文件的管理,文件的管理就是读写、删除等操作,文件打开后,本篇继续分析读操作。

分析假设

(1)假设一个磁盘就一个分区。

(2)只分析FAT32文件系统相关的代码。

(3)函数的大部分分析,都写入代码注释中。

(4)重要的注释都回加入很多星号以及数学标号。例如,
/****************** 1.把字符存入lfn的buffer中 *******************/
(5)在f_open()分析时,发现太多的代码,占用了不少的位置,从此篇文章开始,删除错误判定和不重要的代码,减少文章的长度。

f_read函数分析

下面是f_read()函数的源码:

FRESULT f_read (
	FIL* fp, 	/* Pointer to the file object */
	void* buff,	/* Pointer to data buffer */
	UINT btr,	/* Number of bytes to read */
	UINT* br	/* Pointer to number of bytes read */
)
{
	FRESULT res;
	FATFS *fs;
	DWORD clst, sect;
	FSIZE_t remain;
	UINT rcnt, cc, csect;
	BYTE *rbuff = (BYTE*)buff;

	*br = 0;	/* Clear read byte counter */
    /********* 1.如果文件可读数据不足,调整要读取的数据字节数 **********/
    remain = fp->obj.objsize - fp->fptr;
	if (btr > remain) btr = (UINT)remain;		/* Truncate btr by remaining bytes */

	for ( ;  btr;								/* Repeat until all data read */
		rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
		if (fp->fptr % SS(fs) == 0) {			/* On the sector boundary? */
			csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));	/* Sector offset in the cluster */
			/* 够了一个正簇,需要找到文件的下一个簇的位置 */
			if (csect == 0) {					/* On the cluster boundary? */
				if (fp->fptr == 0) {			/* On the top of the file? */
					clst = fp->obj.sclust;		/* Follow cluster chain from the origin */
				} else {						/* Middle or end of the file */
					if (fp->cltbl) {
						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
					} else
					{
						clst = get_fat(&fp->obj, fp->clust);	/* Follow cluster chain on the FAT */
					}
				}
				fp->clust = clst;				/* Update current cluster */
			}
			sect = clust2sect(fs, fp->clust);	/* Get current sector */
			sect += csect;
			cc = btr / SS(fs);					/* When remaining bytes >= sector size, */
            /************ 2.如果要读出的数据大于1个扇区,就先把整数扇区直接读入到用户的缓冲区 ****************/
			if (cc) {							/* Read maximum contiguous sectors directly */
				if (csect + cc > fs->csize) {	/* Clip at cluster boundary */
					cc = fs->csize - csect;
				}
				if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);

				if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
					mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
				}
				rcnt = SS(fs) * cc;				/* Number of bytes transferred */
				continue;
			}
            /* fp缓冲区对应的sect值与要读的扇区不相同的话,先要把脏扇区回写到磁盘,再从磁盘把数据读入到fp对应的缓冲 */
			if (fp->sect != sect) {			/* Load data sector if not in cache */
				if (fp->flag & FA_DIRTY) {		/* Write-back dirty sector cache */
					if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
					fp->flag &= (BYTE)~FA_DIRTY;
				}
				if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK)	ABORT(fs, FR_DISK_ERR);	/* Fill sector cache */
			}
			fp->sect = sect;
		}
		rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);	/* Number of bytes left in the sector */
		if (rcnt > btr) rcnt = btr;					/* Clip it by btr if needed */
        
        /******* 3.把不足一个扇区的内容,通过当前扇区的缓冲区,直接读入到用户缓冲区 ********/
		mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt);	/* Extract partial sector */
	}

	LEAVE_FF(fs, FR_OK);
}

读文件相对来说简单一些:
(1)找出文件所在的簇的起始位置,这个簇在f_open()函数的时候已经被确认了。
(2)然后在把簇中的内容读取到用户的缓冲区。

核心思想总结

(1)在f_open()函数的时候,fp->obj.objsize中记录的文件的大小,fp->clust记录了文件的起始簇号。
(2)每次读文件的时候都会更改fp->fptr的值,如果fp->fptr的值够了一个簇,但是要读取的大小还不足,此时就要找到文件对应的下一个簇的位置,然后更新fp->clust。
在这里插入图片描述
(3)在文件描述符的fp对应的结构体中,sect指向当前的读扇区,buf[]缓冲当前的扇区内容。
在这里插入图片描述

Logo

鸿蒙生态一站式服务平台。

更多推荐