在uC-OSII多个任务中使用FATFS的重入问题

FATFS文件系统,在多个任务都存在文件处理时,可能会因为任务调度导致在f_open函数执行过程中被其他任务抢断,导致文件系统中的 FATFS fs变量的值会与当前任务处理文件的初始值不匹配,导致异常发生。

 

f_open 打开文件操作

FRESULT f_open (

    FIL* fp,            /* Pointer to the blank file object */

    const TCHAR* path,    /* Pointer to the file name */

    BYTE mode            /* Access mode and file open mode flags */

)

 

注意:

1、使用的时候最好使用全路径打开,避免使用 f_chdir();改变当前目录,尤其在程序中多处使用f_chdir容易忘记当前文件路径

 

实际测试到如下问题

两个任务,任务A优先级高于任务B:

任务A:

if(test == 1)

        {

            PRINTF("6OS.\r\n");

 

            res = f_open(&fil2, "/HISTORY/CO/FIXPTfixpt20110105.msg", FA_READ | FA_WRITE);

 

            PRINTF("6OE.\r\n");

            if(res)

            {

                PRINTF(" f_open /HISTORY/CO/FIXPTfixpt20110105.msg failed. ret=%d\r\n",res);    

            }

            else

            {

                res = f_lseek(&fil2, f_size(&fil2));

                res = f_write(&fil2, "Hello, World66!\r\n", 17, &bw);

                PRINTF("6CS.\r\n");

 

                res = f_close(&fil2);

 

                PRINTF("6CE.\r\n");

            }

        

        }

       OSTimeDly(500);

 

任务B:

if(test == 1)

        {

            //测试FATFS 2个文件同时打开,有无错误

            PRINTF("8OS.\r\n");

 

            res = f_open(&fil, "/HISTORY/CO/FIXPTfixpt20110104.msg", FA_READ | FA_WRITE);

            

            PRINTF("8OE.\r\n");

            if(res)

            {

                PRINTF(" f_open /HISTORY/CO/FIXPTfixpt20110104.msg failed. ret=%d\r\n",res);    

            }

            else

            {

                res = f_lseek(&fil, f_size(&fil));

                res = f_write(&fil, "Hello, World88!\r\n", 17, &bw);

                PRINTF("8CS.\r\n");

 

                res = f_close(&fil);

 

                PRINTF("8CE.\r\n");

            }

        }

       OSTimeDly(1000);

 

以上两个任务在UcOS系统中运行,因为系统频率比较低25Mhz,导致f_open、f_close函数运行占用时间比较久,出现在任务B文件被打开过程中被任务A打断运行的情况

 

测试中函数运行流畅

 

f_open

    ...

    res = follow_path(&dj, path); 

            for (;;) {

            res = create_name(dp, &path);    /* Get a segment name of the path */   根据输入的文件全路径一级一级创建目录

            if (res != FR_OK) break;

            res = dir_find(dp);    //寻找存储在falsh上的文件目录,按扇区查找

                    res = dir_sdi(dp, 0);            /* Rewind directory object */先回滚文件目录到根目录,然后一个扇区一个扇区去匹配 create_name 获取的当前目录,匹配成功返回

 

            ...

            }

 

            for循环直到全部匹配完毕,任何一个目录匹配错误则f_open失败

 

 

测试中发现的问题如下:当上个文件打开成功后, FATFS fs变量中保存的当前扇区数据与其记录的扇区内容不对应,即当前真实flash中winsect扇区的内容与win[]不对应,原因是因为任务调度时,FATFS fs是全局变量,任务调度导致fs中的winsect与win[]内容匹配异常。最后导致无法匹配到目录而f_open失败。

typedef struct {

    BYTE    fs_type;        /* Filesystem type (0:N/A) */

    BYTE    pdrv;            /* Physical drive number */

    BYTE    n_fats;            /* Number of FATs (1 or 2) */

    BYTE    wflag;            /* win[] flag (b0:dirty) */

    BYTE    fsi_flag;        /* FSINFO flags (b7:disabled, b0:dirty) */

    WORD    id;                /* Volume mount ID */

    WORD    n_rootdir;        /* Number of root directory entries (FAT12/16) */

    WORD    csize;            /* Cluster size [sectors] */

#if FF_MAX_SS != FF_MIN_SS

    WORD    ssize;            /* Sector size (512, 1024, 2048 or 4096) */

#endif

#if FF_USE_LFN

    WCHAR*    lfnbuf;            /* LFN working buffer */

#endif

#if FF_FS_EXFAT

    BYTE*    dirbuf;            /* Directory entry block scratchpad buffer for exFAT */

#endif

#if FF_FS_REENTRANT

    FF_SYNC_t    sobj;        /* Identifier of sync object */

#endif

#if !FF_FS_READONLY

    DWORD    last_clst;        /* Last allocated cluster */

    DWORD    free_clst;        /* Number of free clusters */

#endif

#if FF_FS_RPATH

    DWORD    cdir;            /* Current directory start cluster (0:root) */

#if FF_FS_EXFAT

    DWORD    cdc_scl;        /* Containing directory start cluster (invalid when cdir is 0) */

    DWORD    cdc_size;        /* b31-b8:Size of containing directory, b7-b0: Chain status */

    DWORD    cdc_ofs;        /* Offset in the containing directory (invalid when cdir is 0) */

#endif

#endif

    DWORD    n_fatent;        /* Number of FAT entries (number of clusters + 2) */

    DWORD    fsize;            /* Size of an FAT [sectors] */

    DWORD    volbase;        /* Volume base sector */

    DWORD    fatbase;        /* FAT base sector */

    DWORD    dirbase;        /* Root directory base sector/cluster */

    DWORD    database;        /* Data base sector */

    DWORD    winsect;        /* Current sector appearing in the win[] */

    BYTE    win[FF_MAX_SS];    /* Disk access window for Directory, FAT (and file data at tiny cfg) */

} FATFS;

 

 

 

针对以上问题解决方法

 

一、避免任务中同时打开多个文件,为f_open、f_close 设置互斥信号,该方式虽然导致系统性能下降,但可保证文件系统稳定可靠运行。

 

如:

FRESULT f_open (

    FIL* fp,            /* Pointer to the blank file object */

    const TCHAR* path,    /* Pointer to the file name */

    BYTE mode            /* Access mode and file open mode flags */

)

{

    FRESULT res;

    DIR dj;

    FATFS *fs;

#if !FF_FS_READONLY

    DWORD dw, cl, bcs, clst, sc;

    FSIZE_t ofs;

#endif

    DEF_NAMBUF

 

    uint8_t err = 0;

    if (!fp) return FR_INVALID_OBJECT;

    

     OSMutexPend(fsopen_mutex, MUTEX_FFTIMEOUT_MS, &err);

        if(err != OS_ERR_NONE)

            printf("OSMutexPend fsopen_mutex err = %d.\r\n", err);    

    /* Get logical drive number */

    mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;

    res = find_volume(&path, &fs, mode);

    if (res == FR_OK) {

        dj.obj.fs = fs;

        INIT_NAMBUF(fs);

//        printf("lfnb=%08x\r\n",(fs->lfnbuf));

        res = follow_path(&dj, path);    /* Follow the file path */

#if !FF_FS_READONLY    /* Read/Write configuration */

        if (res == FR_OK) {

            if (dj.fn[NSFLAG] & NS_NONAME) {    /* Origin directory itself? */

                res = FR_INVALID_NAME;

            }

#if FF_FS_LOCK != 0

            else {

                res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);        /* Check if the file can be used */

            }

#endif

        }

        /* Create or Open a file */

        if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {

            if (res != FR_OK) {                    /* No file, create new */

                if (res == FR_NO_FILE) {        /* There is no file to open, create a new entry */

#if FF_FS_LOCK != 0

                    res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;

#else

                    res = dir_register(&dj);

#endif

                }

                mode |= FA_CREATE_ALWAYS;        /* File is created */

            }

            else {                                /* Any object with the same name is already existing */

                if (dj.obj.attr & (AM_RDO | AM_DIR)) {    /* Cannot overwrite it (R/O or DIR) */

                    res = FR_DENIED;

                } else {

                    if (mode & FA_CREATE_NEW) res = FR_EXIST;    /* Cannot create as new file */

                }

            }

            if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {    /* Truncate the file if overwrite mode */

#if FF_FS_EXFAT

                if (fs->fs_type == FS_EXFAT) {

                    /* Get current allocation info */

                    fp->obj.fs = fs;

                    init_alloc_info(fs, &fp->obj);

                    /* Set directory entry block initial state */

                    mem_set(fs->dirbuf + 2, 0, 30);        /* Clear 85 entry except for NumSec */

                    mem_set(fs->dirbuf + 38, 0, 26);    /* Clear C0 entry except for NumName and NameHash */

                    fs->dirbuf[XDIR_Attr] = AM_ARC;

                    st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME());

                    fs->dirbuf[XDIR_GenFlags] = 1;

                    res = store_xdir(&dj);

                    if (res == FR_OK && fp->obj.sclust != 0) {    /* Remove the cluster chain if exist */

                        res = remove_chain(&fp->obj, fp->obj.sclust, 0);

                        fs->last_clst = fp->obj.sclust - 1;        /* Reuse the cluster hole */

                    }

                } else

#endif

                {

                    /* Set directory entry initial state */

                    cl = ld_clust(fs, dj.dir);            /* Get current cluster chain */

                    st_dword(dj.dir + DIR_CrtTime, GET_FATTIME());    /* Set created time */

                    dj.dir[DIR_Attr] = AM_ARC;            /* Reset attribute */

                    st_clust(fs, dj.dir, 0);            /* Reset file allocation info */

                    st_dword(dj.dir + DIR_FileSize, 0);

                    fs->wflag = 1;

                    if (cl != 0) {                        /* Remove the cluster chain if exist */

                        dw = fs->winsect;

                        res = remove_chain(&dj.obj, cl, 0);

                        if (res == FR_OK) {

                            res = move_window(fs, dw);

                            fs->last_clst = cl - 1;        /* Reuse the cluster hole */

                        }

                    }

                }

            }

        }

        else {    /* Open an existing file */

            if (res == FR_OK) {                    /* Is the object exsiting? */

                if (dj.obj.attr & AM_DIR) {        /* File open against a directory */

                    res = FR_NO_FILE;

                } else {

                    if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */

                        res = FR_DENIED;

                    }

                }

            }

        }

        if (res == FR_OK) {

            if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED;    /* Set file change flag if created or overwritten */

            fp->dir_sect = fs->winsect;            /* Pointer to the directory entry */

            fp->dir_ptr = dj.dir;

#if FF_FS_LOCK != 0

            fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);    /* Lock the file for this session */

            if (fp->obj.lockid == 0) res = FR_INT_ERR;

#endif

        }

#else        /* R/O configuration */

        if (res == FR_OK) {

            if (dj.fn[NSFLAG] & NS_NONAME) {    /* Is it origin directory itself? */

                res = FR_INVALID_NAME;

            } else {

                if (dj.obj.attr & AM_DIR) {        /* Is it a directory? */

                    res = FR_NO_FILE;

                }

            }

        }

#endif

 

        if (res == FR_OK) {

#if FF_FS_EXFAT

            if (fs->fs_type == FS_EXFAT) {

                fp->obj.c_scl = dj.obj.sclust;                            /* Get containing directory info */

                fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;

                fp->obj.c_ofs = dj.blk_ofs;

                init_alloc_info(fs, &fp->obj);

            } else

#endif

            {

                fp->obj.sclust = ld_clust(fs, dj.dir);                    /* Get object allocation info */

                fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);

            }

#if FF_USE_FASTSEEK

            fp->cltbl = 0;            /* Disable fast seek mode */

#endif

            fp->obj.fs = fs;         /* Validate the file object */

            fp->obj.id = fs->id;

            fp->flag = mode;        /* Set file access mode */

            fp->err = 0;            /* Clear error flag */

            fp->sect = 0;            /* Invalidate current data sector */

            fp->fptr = 0;            /* Set file pointer top of the file */

#if !FF_FS_READONLY

#if !FF_FS_TINY

            mem_set(fp->buf, 0, FF_MAX_SS);    /* Clear sector buffer */

#endif

            if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) {    /* Seek to end of file if FA_OPEN_APPEND is specified */

                fp->fptr = fp->obj.objsize;            /* Offset to seek */

                bcs = (DWORD)fs->csize * SS(fs);    /* Cluster size in byte */

                clst = fp->obj.sclust;                /* Follow the cluster chain */

                for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {

                    clst = get_fat(&fp->obj, clst);

                    if (clst <= 1) res = FR_INT_ERR;

                    if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;

                }

                fp->clust = clst;

                if (res == FR_OK && ofs % SS(fs)) {    /* Fill sector buffer if not on the sector boundary */

                    if ((sc = clst2sect(fs, clst)) == 0) {

                        res = FR_INT_ERR;

                    } else {

                        fp->sect = sc + (DWORD)(ofs / SS(fs));

#if !FF_FS_TINY

                        if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;

#endif

                    }

                }

            }

#endif

        }

//        printf("lfnb=%08x\r\n",(fs->lfnbuf));

        FREE_NAMBUF();

    }

 

    if (res != FR_OK) fp->obj.fs = 0;    /* Invalidate file object on error */

 

    err = OSMutexPost(fsopen_mutex);    

    if(err != OS_ERR_NONE)

        printf("OSMutexPost fsopen_mutex err = %d.\r\n", err);

    LEAVE_FF(fs, res);

}

 

 

FRESULT f_close (

    FIL* fp        /* Pointer to the file object to be closed */

)

{

    FRESULT res;

    FATFS *fs;

 

    uint8_t err = 0;

    OSMutexPend(fsopen_mutex, MUTEX_FFTIMEOUT_MS, &err);

    if(err != OS_ERR_NONE)

        printf("OSMutexPend fsopen_mutex err = %d.\r\n", err);

    

#if !FF_FS_READONLY

    res = f_sync(fp);                    /* Flush cached data */

    if (res == FR_OK)

#endif

    {

        res = validate(&fp->obj, &fs);    /* Lock volume */

        if (res == FR_OK) {

#if FF_FS_LOCK != 0

            res = dec_lock(fp->obj.lockid);        /* Decrement file open counter */

            if (res == FR_OK) fp->obj.fs = 0;    /* Invalidate file object */

#else

            fp->obj.fs = 0;    /* Invalidate file object */

#endif

#if FF_FS_REENTRANT

            unlock_fs(fs, FR_OK);        /* Unlock volume */

#endif

        }

    }

    

    err = OSMutexPost(fsopen_mutex);    

    if(err != OS_ERR_NONE)

        printf("OSMutexPost fsopen_mutex err = %d.\r\n", err);

    

    return res;

}

二、实现FATFS的可重入功能

#define FF_FS_REENTRANT    0
#define FF_FS_TIMEOUT    1000
#define FF_SYNC_t        HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/  module itself. Note that regardless of this option, file access to different
/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/  and f_fdisk() function, are always not re-entrant. Only file/directory access
/  to the same volume is under control of this function.
/
/   0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/   1: Enable re-entrancy. Also user provided synchronization handlers,
/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/      function, must be added to the project. Samples are available in
/      option/syscall.c.

/
/  The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/  The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/  SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/  included somewhere in the scope of ff.h. */

你可能感兴趣的:(在uC-OSII多个任务中使用FATFS的重入问题)