RT-Thread Virtual File System |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ранние встраиваемые системы на микроконтроллерах (MCU) обладали очень малыми ресурсами памяти, и обычно работали с простыми типами данных. Сохраняемые данные записывались непосредственно по указанному адресу в устройство хранения. Однако с расширением количества поддерживаемых функций понадобилось сохранять все больше и больше данных, форматы хранения данных усложнились, потребовалась поддержка стандартных файловых систем. Файловая система это набор абстрактных типов данных, поддерживающий хранение, иерархическую организицию и стандартизованный доступ к данным. Файловая система обеспечивает для пользователей высокоуровневый механизм доступа к данным, где единицей хранения данных является файл. Если файлов становится слишком много, то они могут быть распределены по категориям, для чего реализованы каталоги (директории, папки). В этом документе (перевод документации [1]), описывается архитектура виртуальной файловой системы RT-Thread (Virtual File System, VFS), её возможности и использование. Компонент DFS представляет в RT-Thread файловую систему устройства (DFS это сокращение от Device File System). Принятая система именования файлов аналогична файлам и папкам UNIX. Пример структуры директорий: Начало всех путей до файлов это корневая директория (root), обозначаемая символом /. Например файл f1.bin, находящийся в корневой директории, имеет путь /f1.bin, и файл f1.bin, находящийся в директории /data/2019, представлен полным именем /data/2019/f1.bin. Такая система именования используется в UNIX/Linux, что отличается от Windows, где в качестве разделителя имен используется символ \, и отдельные диски начинаются с буквы и двоеточия (C:, D: и т. д.). [Архитектура DFS] Основные функции компонента RT-Thread DFS: - Унифицированный POSIX-интерфейс операций над файлами и директориями для приложений: read, write, poll/select, и т. п. Иерархическая структура DFS показана на следующем рисунке. Основные слои здесь интерфейс POSIX, virtual file system (VFS) и слой абстракции устройства. [Слой интерфейса POSIX] POSIX означает Portable Operating System Interface of UNIX. POSIX определяет стандартный интерфейс операционной системы, предоставляемый для приложений. Это общий термин для набора стандартных API, определенных IEEE для программного обеспечения операционных систем UNIX. Стандарт POSIX предназначался для обеспечения переносимости ПО на уровне исходного кода. Другими словами, программа, написанная для POSIX-совместимой операционной системы, должна без особых трудностей компилироваться и запускаться на любой другой POSIX-системе (даже от другого производителя). RT-Thread поддерживает стандартный интерфейс POSIX, что упрощает портирование программ Linux/Unix на операционную систему RT-Thread. На операционных системах семейства UNIX обычные файлы, файлы устройств и сетевые дескрипторы файлов для программ являются одинаковыми. В операционной системе RT-Thread компонент DFS используется для поддержки аналогичного принципа доступа к файлам. Такая универсальность файловых дескрипторов позволяет использовать интерфейс poll/select для удобного доступа к данным из кода программ. Использование интерфейса poll/select обеспечивает блкировку и детектирование попыток одновременного доступа к устройствам ввода/вывода (таким как устройства чтения и записи, устройства высокоприоритетного вывода сообщенй об ошибках и т. д.) с поддержкой событий. Если устройство недоступно в течение заданного периода таймаута, то генерируется событие ошибки. Этот механизм помогает вызывающему API-функции коду находить устройства, находящиеся в состоянии готовности, что уменьшает сложность программирования. [Слой Virtual File System] Пользователи могут зарегистрировать для DFS несколько различных файловых систем: FatFS, RomFS, DevFS, и т. д. Ниже перечислены несколько таких типов систем: - FatFS поддерживает Microsoft FAT. Эта библиотека была разработана для небольших встраиваемых систем. Она написана на ANSI C, и обеспечивает хорошую переносимость на разное оборудование. FatFS чаще всего используется для файловой системы в RT-Thread. Device Abstraction Layer. Слой абстракции устройства предоставляет интерфейс к разным типам устройств хранения: SD Card, микросхемы SPI Flash и Nand Flash, так что они становятся доступными для использования в файловой системе. Например, файловая система FAT требует устройства хранения блочного типа, чтобы можно было сохранять данными по секторам (обычно 512 байт). Различные типы файловых систем реализованы независимо от драйвера хранилища (storage device driver). Благодаря этому файловая система может работать корректно после того, как для неё был определен драйвер хранилища. [Управление монтированием] Процесс инициализации файловой системы обычно распределен на следующие шаги: 1. Инициализация компонента DFS. Когда файловая система больше не используется, она может быть деинсталлирована. Инициализация компонента DFS. Dызов функции dfs_init() инициализирует соответствующие ресурсы, требуемые для DFS, и создает ключевые структуры данных, по которым DFS находит определенную файловую систему и способ манипуляции фалами на устройстве хранения. Эта функция будет вызвана автоматически, если включена система автоинициализации (что разрешено по умолчанию). int dfs_init(void); В случае успешной инициализации dfs_init вернет 1 (RT_TRUE), иначе, если инициализация уже имела место, будет возвращен 0 (RT_FALSE). Регистрация файловой системы. После того, как компонент DFS инициализирован, для него необходимо инициализировать определенный тип файловой системы, это делается вызовом функции dfs_register. int dfs_register(const struct dfs_filesystem_ops *ops); Параметр ops передает указатель на структуру, заполненную рабочими функциями файловой системы: struct dfs_filesystem_ops { char *name; // произвольный текст, описывающий файловую систему uint32_t flags; // флаги поддерживаемых операций /* Операции для файла */ const struct dfs_file_ops *fops; /* Монтирование и демонтирование файловой системы */ int (*mount) (struct dfs_filesystem *fs, unsigned long rwflag, const void *data); int (*unmount) (struct dfs_filesystem *fs); /* Создание файловой системы */ int (*mkfs) (rt_device_t devid); int (*statfs) (struct dfs_filesystem *fs, struct statfs *buf); int (*unlink) (struct dfs_filesystem *fs, const char *pathname); int (*stat) (struct dfs_filesystem *fs, const char *filename, struct stat *buf); int (*rename) (struct dfs_filesystem *fs, const char *oldpath, const char *newpath); }; В случае успеха dfs_register вернет 0, в случае ошибки регистрации вернет -1. Эта функция не требует вызова пользователем, она вызывается из функции инициализации различных файловых систем, например из функции elm_init() файловой системы elm-FAT. После того, как соответствующая файловая система разрешена, если автоматическая инициализация разрешена (что разрешено по умолчанию), функция файловой системы также будет вызвана автоматически. Диаграмма вызова функций для инициализации файловой системы elm-FAT: Регистрация устройства хранения (Storage Device) в как блочного (Block Device). Для файловой системы могут быть смонтированы только блочные устойства, поэтому на устройстве хранения должны быть созданы блочные устройства. Если устройством хранения является микросхема SPI Flash, то вы можете использовать компонент "Serial Flash Universal Driver Library SFUD", который поддерживает различные драйверы SPI Flash, создавая для монтирования абстракцию блочного устройства поверх микросхем SPI Flash. Процесс регистрации блочного устройства показан на следующей картинке: Форматирование файловой системы. После того, как блочное устройство зарегистрировано, вам на нем также надо создать файловую систему определенного типа. Для форматирования устройства хранения и создания на нем файловой системы вы можете использовать функцию dfs_mkfs(). int dfs_mkfs(const char *fs_name, const char *device_name);
Имя типа файловой системы (fs_name) может быть:
Для примера рассмотрим процесс форматирования блочного устройства elm-FAT: Вы также можете отформатировать устройство командой mkfs консоли FinSH. Ниже показан пример создания файловой системы на блочном устройстве с именем sd0, команда mkfs по умолчанию будет его форматировать. Параметр -t указывает имя типа файловой системы для elm-FAT. msh />mkfs sd0
sd0 is elm-FAT file system
msh />mkfs -t elm sd0
Монтирование файловой системы. В RT-Thread под монтированием подразумевается отображение устройства хранения на существующий путь в корневой файловой системе. Чтобы обратиться к файлу на устройстве хранения, мы должны смонтировать раздел, где находится файл, на существующий путь, и затем обращаться к файлу по полному имени, включающему этот путь. int dfs_mount(const char *device_name, const char *path, const char *filesystemtype, unsigned long rwflag, const void *data);
Если в системе только одно устройство хранения, то оно может быть напрямую смонтировано в корневой каталог /. Демонтирование файловой системы. Когда файловая система больше не нужна, она может быть размонтирована (unmount). int dfs_unmount(const char *specialfile);
[Работа с файлами] Операции над файлами обычно основываются на дескрипторе файла fd (file descriptor). Стандартные операции включают в себя открытие и закрытие файла (open, close), чтение и запись файла (read, write). Открытие и закрытие файла. Чтобы открыть существующий файл или создать новый, используется функция open(): int open(const char *file, int flags, ...);
Файл может быть открыт разными способами, и несколько вариантов его открытия могут быть указаны одновременно OR-комбинацией флагов в параметре flags. Например, если файл открыт в режиме O_WRONLY и O_CREAT, то когда файл с указанным именем не существует, он будет сначала создан (O_CREAT), и затем открыт в режиме только для записи (O_WRONLY). Методы открытия файла показаны в следующей таблице:
Если файл больше не нужен, то он может быть закрыт функцией close(). Тогда содержимое файла будет записано на физический носитель данных, и ресурсы, связанные с поддержкой файла, будут освобождены. int close(int fd);
Чтение и запись файла. Для чтения содержимого файла используется функция read(): int read(int fd, void *buf, size_t len);
По мере считывания len байт текущая позиция чтения в файле перемещается вперед на значение, которое было возвращено из функции read. Для записи данных в файл используется функция write(): int write(int fd, const void *buf, size_t len);
Функция запишет len байт по текущей позиции в файле, эта позиция перемещается вперед на значение, которое было возвращено из функции write. Переименование файла. Чтобы переименовать файл, используется функция rename(): int rename(const char *old, const char *new);
Функция rename меняет имя файла old на новое имя new, и если файл с именем new существует, то он будет перезаписан содержимым файла с именем old. Получение состояния файла. Чтобы узнать статус файла, используйте функцию stat(): int stat(const char *file, struct stat *buf);
Удаление файлов. Чтобы удалить файл в указанной директории, используйте функцию unlink(): int unlink(const char *pathname);
Синхронизация данных файла с хранилищем. Чтобы сбросить модифицированные данные файла на диск (устройство хранения), используется функция fsync(): int fsync(int fd);
Запрос системной информации, связанной с файловой системой. Для этого используется функция statfs(). int statfs(const char *path, struct statfs *buf);
Мониторинг статуса устройства ввода/вывода. Чтобы отслеживать события устройства хранения (I/O device), используйте функцию select(): int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
Функция select() используется для блокировки и детектирования событий на неблокируемых устройствах (non-blocking I/O devices), к таким событиям относятся чтение, запись, высокоприоритетный вывод ошибок, и т. д.). [Управление директориями] Для управления каталогами файлов используются следующие функции: Создание и удаление директорий. Чтобы создать директорию, используйте функцию mkdir(): int mkdir(const char *path, mode_t mode);
В параметр mode в текущей версии не используется, передайте в нем значение по умолчанию 0x777. Для удаления директории используйте функцию rmdir(): int rmdir(const char *pathname);
Открытие и закрытие директории. Чтобы открыть директорию, используйте функцию opendir(): DIR* opendir(const char* name);
Чтобы закрыть директорию, используйте функцию closedir(): int closedir(DIR* d);
Эта функция используется для закрытия директории, которая была предварительно открыта вызовом opendir(). Чтение директории. Чтобы прочитать элементы директории, используйте функцию readdir(): struct dirent* readdir(DIR *d);
При каждом вызове readdir итератор d автоматически перемещается на одну позицию. Получение текущей позиции чтения директории. Для этого используется функция telldir(): long telldir(DIR *d);
В возвращаемом значении записана текущая позиция потока (итератора) директории. Это значение представляет смещение от начала файла директории. Вы можете использовать это значение для последующего вызова seekdir(), чтобы сбросить итератор директории на определенную позицию. void seekdir(DIR *d, off_t offset);
Функция seekdir используется для установки позиции чтения директории d. Чтение директории функцией readdir() начнется с установленного смещения offset. Чтобы сбросить позицию чтения потока директории в начало, используйте функцию rewinddir(): void rewinddir(DIR *d);
[Опции конфигурации DFS] Опции конфигурации файловой системы настраиваются командой scons menuconfig. RT-Thread Components ---> После сохранения конфигурации menuconfig в файле rtconfig.h устанавливаются опции конфигурации DFS:
По умолчанию RT-Thread не включает поддержку относительных путей файлов, чтобы уменьшить расход памяти. В этом случае необходимо для использовать абсолютные пути для файлов и папок (поскольку не задана текущая директория для файловой системы). Если же необходимо использовать и текущую рабочую директорию, и относительную директорию, то вы можете разрешить функцию относительного пути в конфигурации DFS. Когда выбрана опция [*] Using mount table for file system is selected, будет разрешен макрос RT_USING_DFS_MNTTABLE, чтобы включить автоматическое монтирование. В коде приложения предоставляется таблица автоматического монтирования mount_table[]. В таблице нужно указать имя устройства, путь монтирования, тип файловой системы, флаг чтения и записи и указатель на приватные данные. После этого система выполнит итерацию по таблице mount_table, и автоматически смонтирует указанные устройства и файловые системы. Таблица mount_table должна заканчиваться маркером {0}. Ниже показан пример mount_table[], где в первом элементе таблицы mount_table[0] указаны 5 параметров для функции dfs_mount(). Задано монтировать файловую систему elm в путь / на устройстве хранения flash 0, rwflag 0, data 0. Элемент mount_table [1] содержит маркер окончания таблицы {0}. const struct dfs_mount_tbl mount_table[] = { {"flash0", "/", "elm", 0, 0}, {0} }; Опции конфигурации elm-FatFs. Файловая система Elm-FatFs может быть дополнительно сконфигурирована следующими опциями в menuconfig:
Длинное имя файла. По умолчанию у системы именования файлов FatFs есть следующие недостатки: - Длина имени файла (без суффикса) может иметь длину не более 8 символов, и длина суффикса (расширения) не может быть больше 3 символов. Если это ограничение превышено, то элементы имени будут обрезаны до 8 и 3 символов соответственно. Если необходима поддержка длинных имен, то нужно включить соответствующую опцию (support long filenames). Субменю настройки длинных имен:
Encoding Mode. Когда включена поддержка длинных имен файлов, можно установить режим кодирования (encoding mode) имени файла. RT-Thread/FatFs по умолчанию использует кодирование 437 (American English). Если нужно использовать китайскую кодировку (Chinese) для имен файлов, то установите 936 encoding (GBK encoding). 936 encoding требует библиотеки шрифта размером около 180 килобайт. Если в используете для имен файлов только символы English, то рекомендуется установить 437 encoding (American English), это позволит съэкономить 180KB памяти Flash. FatFs поддерживает следующие кодировки имен файлов: #define _CODE_PAGE 437
/* Эта опция задает кодовую страницу OEM, используемую на целевой системе.
/ Неправильная установка может привести к ошибке открытия файла.
/
/ 1 - ASCII (нет поддержки расширенных символов. Только для конфигурации без LFN)
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
*/
File System Sector Size. Указывает внутренний размер сектора FatFs, который должен быть больше или равен размеру сектора актуального драйвера аппаратуры хранилища. Например, если размер сектора микросхемы SPI Flash равен 4096 байтам, то макрос RT_DFS_ELM_MAX_SECTOR_SIZE должен быть установлен в значение 4096. Иначе, когда FatFs прочитает данные из драйвера, произойдет переполнение массива буфера сектора, что приведет к краху системы (новая версия выдаст предупреждающее сообщение при запуске системы). Обычно устройство Flash требует размера сектора 4096, и обычные карты TF и карты SD поддерживают размер сектора 512. Reentrant. FatFs полностью поддерживает многопоточную среду выполнения кода, обеспечивая безопасные операции чтения и записи. При чтении и записи FafFs в многопоточной среде для устранения проблем с одновременным доступом из разных потоков необходимо сконфигурировать макрос RT_DFS_ELM_REENTRANT, а также выбрать режим выделения памяти для длинных имен файлов RT_DFS_ELM_USE_LFN_2 или RT_DFS_ELM_USE_LFN_3 (в случае использования длинных имен). Если доступ к файлам происходит только из одного потока, то определение макроса реентерабельности RT_DFS_ELM_REENTRANT для экономии памяти можно удалить (закомментировать). Другие опции конфигурации. FatFs поддерживает множество опций, что делает эту библиотеку файловой системы очень гибкой в использовании. Опции конфигурации FatFs находятся в заголовочном файле: rt-thread/components/dfs/filesystems/elmfat/ffconf.h [Команды FinSH] После того, как файловая система успешно смонтирована, можно работать с файлами и директориями. Обычно в примере приложения RT-Thread консоль FinSH [4] предоставляет команды:
Используйте команду ls, чтобы посмотреть текущую директорию, результат будет примерно такой: msh />ls
Directory /:
dummy < DIR >
dummy.txt 17
DIR означает, что dummy это директория, а число 17 говорит о том, что размер файла dummy.txt равен 17 байтам. Для создания папки используйте команду mkdir: msh />mkdir rt-thread # создание папки rt-thread в текущем каталоге
msh />ls # просмотр информации
Directory /:
rt-thread < DIR >
dummy < DIR >
dummy.txt 17
Используйте команду echo, чтобы вывести строку в файл, находящийся в определенном каталоге. Для простоты файл запишем в текущий каталог /: msh />echo "hello rt-thread!!!" # вывод строки в stdout
hello rt-thread!!!
msh />echo "hello rt-thread!!!" hello.txt # вывод строки в файл hello.txt
msh />ls
Directory /:
rt-thread < DIR >
dummy < DIR >
dummy.txt 17
hello.txt 18
Для просмотра содержимого файла используйте команду cat: msh />cat hello.txt
hello rt-thread!!!
Для удаления файла или папки используйте команду rm: msh />ls # просмотр содержимого текущей директории
Directory /:
rt-thread < DIR >
hello.txt 18
msh />rm rt-thread # удаление папки rt-thread
msh />ls
Directory /:
hello.txt 18
msh />rm hello.txt # удаление папки hello.txt
msh />ls
Directory /:
[Чтение и запись файла] Как только файловая система заработала, можно попробовать её работу на чтение и запись данных файла, что показано в следующем примере. Здесь сначала создается файл text.txt с помощью функции open(), и в него записывается строка "RT-Thread Programmer!\n" функцией write(), после чего файл закрывается. Чтобы открыть файл и прочитать его, снова используется функция open(), и с помощью функции read содержимое файла считывается и печатается в stdout, после чего файл закрывается. #include < rtthread.h>
// Этот заголовочный файл нужно подключить для работы с файлами:
#include < dfs_posix.h>
static void readwrite_sample(void) { int fd, size; char s[] = "RT-Thread Programmer!", buffer[80]; rt_kprintf("Write string %s to test.txt.\n", s); /* Открыть файл /text.txt с флагами создания (O_CREAT),
в режиме записи (O_WRONLY). Если файл еще не существует,
то он будет создан. */ fd = open("/text.txt", O_WRONLY | O_CREAT); if (fd>= 0) { write(fd, s, sizeof(s)); close(fd); rt_kprintf("Write done.\n"); } /* Открыть файл /text.txt в режиме только для чтения. */ fd = open("/text.txt", O_RDONLY); if (fd>= 0) { size = read(fd, buffer, sizeof(buffer)); close(fd); rt_kprintf("Read from file test.txt : %s \n", buffer); if (size < 0) return ; } } /* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(readwrite_sample, readwrite sample);
[Пример изменения имени файла] В функции rename_sample() показан простой код, который вызовом rename() переименовывает файл text.txt в text1.txt. #include < rtthread.h>
#include < dfs_posix.h>
static void rename_sample(void) { rt_kprintf("%s => %s", "/text.txt", "/text1.txt"); if (rename("/text.txt", "/text1.txt") < 0) rt_kprintf("[error!]\n"); else rt_kprintf("[ok!]\n"); } /* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(rename_sample, rename sample);
Пример запуска команды rename_sample, которая переименует файл text.txt в text1.txt: msh />echo "hello" text.txt
msh />ls
Directory /:
text.txt 5
msh />rename_sample
/text.txt => /text1.txt [ok!]
msh />ls
Directory /:
text1.txt 5
[Получение состояния файла] Этот пример показывает, как получить информацию статуса файла text.txt. Это делается вызовом функции stat(), информация статуса записывается в структуру stat buf, после чего в stdout выводится информация о размере файла. #include < rtthread.h>
#include < dfs_posix.h>
static void stat_sample(void) { int ret; struct stat buf; ret = stat("/text.txt", &buf); if(ret == 0) rt_kprintf("text.txt file size = %d\n", buf.st_size); else rt_kprintf("text.txt file not found\n"); } /* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(stat_sample, show text.txt stat sample);
Запуск примера в консоли покажет следующий результат: msh />echo "hello" text.txt
msh />stat_sample
text.txt file size = 5
[Создание директории] Следующий пример создаст папку dir_test в корневой (root) директории /. #include < rtthread.h>
#include < dfs_posix.h>
static void mkdir_sample(void) { int ret; /* Вызов mkdir создаст каталог /dir_test: */ ret = mkdir("/dir_test", 0x777); if (ret < 0) { /* Ошибка создания директории */ rt_kprintf("dir error!\n"); } else { /* Директория создана успешно */ rt_kprintf("mkdir ok!\n"); } } /* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(mkdir_sample, mkdir sample);
Запуск примера в консоли покажет следующий результат: msh />mkdir_sample
mkdir ok!
msh />ls
Directory /:
dir_test < DIR>
[Чтение директории] Следующий пример кода показывает, как считывать список файлов и папок в указанном каталоге. С помощью вызова функции readdir() код получает доступ к списку содержимого в папке dir_test, и выводит эту информацию в консоль: #include < rtthread.h>
#include < dfs_posix.h>
static void readdir_sample(void) { DIR *dirp; struct dirent *d; /* Открыть директорию /dir_test */ dirp = opendir("/dir_test"); if (dirp == RT_NULL) { rt_kprintf("open directory error!\n"); } else { /* Чтение элементов в директории */ while ((d = readdir(dirp)) != RT_NULL) { rt_kprintf("found %s\n", d->d_name); } /* Закрыть директорию */
closedir(dirp);
}
}
/* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(readdir_sample, readdir sample);
Запуск примера в консоли покажет следующий результат: msh />ls
Directory /:
dir_test < DIR>
msh />cd dir_test # переход в папку dir_test
msh /dir_test>echo "hello" hello.txt # создание файла hello.txt
msh /dir_test>cd .. # переход в родительскую папку
msh />readdir_sample
found hello.txt
В этом примере мы сначала создадим файл hello.txt в каталоге dir_test, после чего вернемся обратно в корневую директорию, где находится сама папка dir_test. После этого мы запустим командой readdir_sample код нашего примера, он прочитает каталог dir_test и выведет в консоль его содержимое. [Навигация по списку директории] Этот код показывает, как программно указать текущий каталог. Функция команды telldir_sample() сначала откроет корневой каталог, затем напечатает его содержимое в консоль. Кроме того, используется функция telldir() для записи информации о третьем элементе директории. Перед чтением информации о корневой директории второй раз, вызов функции seekdir() используется для установки позиции чтения третьего элемента директории, которая была предварительно записана в переменную. После этого опять запускается итерация чтения директории. #include < rtthread.h>
#include < dfs_posix.h>
/* Предполагается, что операции с файлами выполняются одновременно
только в этом потоке */
static void telldir_sample(void) { DIR *dirp; int save3 = 0; int cur; int i = 0; struct dirent *dp; /* Открыть папку root */ rt_kprintf("the directory is:\n"); dirp = opendir("/"); for (dp = readdir(dirp); dp != RT_NULL; dp = readdir(dirp)) { /* Сохранение указателя на третий элемент списка директории / */ i++; if (i == 3) save3 = telldir(dirp); rt_kprintf("%s\n", dp->d_name); } /* Возвращаемся обратно по списку каталога на сохраненную позицию */
seekdir(dirp, save3);
/* Проверка: совпадает ли текущий указатель директории с сохраненным
третьим элементом директории */ cur = telldir(dirp); if (cur != save3) { rt_kprintf("seekdir (d, %ld); telldir (d) == %ld\n", save3, cur); } /* Начинаем итерацию по списку директории с третьего элемента */ rt_kprintf("the result of tell_seek_dir is:\n"); for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { rt_kprintf("%s\n", dp->d_name); } /* Закрыть директорию */
closedir(dirp);
}
/* Экспорт команды в консоль FinSH */
MSH_CMD_EXPORT(telldir_sample, telldir sample);
В этом примере мы должны сначала вручную создать 5 папок hello_1 .. hello_5 в корневой директории / с помощью команды mkdir. После этого запуск примера команды покажет следующий результат: msh />ls
Directory /:
hello_1 < DIR>
hello_2 < DIR>
hello_3 < DIR>
hello_4 < DIR>
hello_5 < DIR>
msh />telldir_sample
the directory is:
hello_1
hello_2
hello_3
hello_4
hello_5
the result of tell_seek_dir is:
hello_3
hello_4
hello_5
[VFS/DFS FAQ] Q01: Почему неправильно отображаются имена файлов или папок? Q02: Что делать, если происходит ошибка при попытке инициализации файловой системы? Q03: Почему не работает команда mkfs? Q04: Что делать, если файловая система устройства хранения не монтируется? - Проверьте, существует ли указываемый вами путь монтирования. Файловая система может быть смонтирована напрямую в корневой каталог, точка монтирования "/". Но если нужно смонтировать еще одно устройство, то необходимо указать другую точку (путь) монтирования, такую как например "/sdcard". Проверьте перед монтированием, существует ли путь "/sdcard". Если не существует, то нужно создать папку sdcard в корневом каталоге перед монтированием нового устройства в точку монтирования "/sdcard". Q05: Что делать, если SFUD не может определить Flash определенной модели? - Зарегистрировано ли устройство SPI. Q06: Почему тест производительности устройства хранения идет слишком долго? - Сравните данные эталонного теста, когда системный тик достигнет 1000, с временем, необходимым для этого теста. Если лаг времени слишком большой, то вероятно тест работает некорректно. Q07: На SPI Flash реализована файловая система elmfat, как сделать так, чтобы некоторые секторы не использовались для файловой системы? Q08: Что делать, если программа зависает на тесте файловой системы? Q09: Как пошагово разобраться с проблемой в работе файловой системы? - Сначала проверьте, успешно ли зарегистрировано и нормально ли работает устройство хранения (storage device). [Ссылки] 1. RT-Thread Virtual File System site:rt-thread.io. |