MicroPython:Основы/Язык MicroPython и его реализация/Работа с файловыми системами: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Нет описания правки
 
(не показаны 3 промежуточные версии 1 участника)
Строка 3: Строка 3:
{{Myagkij-редактор}}
{{Myagkij-редактор}}


=Работа с файловыми системами<ref>[http://docs.micropython.org/en/latest/reference/filesystem.html docs.micropython.org - Working with filesystems]</ref>=


В этой статье рассказывается о том, как [[MicroPython]] предоставляет доступ к файловой системе, смонтированной на устройство, позволяя использовать при работе с постоянной энергонезависимой памятью стандартные [[Python]]’овские файловые методы чтения/записи.


<syntaxhighlight lang="python" enclose="div">
[[MicroPython]] автоматически задает настройки по умолчанию и определяет главную файловую систему, поэтому это руководство будет полезно в первую очередь, если вам нужно переопределить разделы файловой системы, изменить ее тип или воспользоваться блочными устройствами с вашими настройками.
 
Файловая система обычно монтируется на внутреннюю [[flash-память]] устройства, но она также может располагаться на внешней [[flash]]-памяти, в [[RAM]]-памяти или на блочном устройстве.
 
На некоторых портах (например, на [[STM32-порт]]е) доступ к файловой системе можно получить на [[ПК]] через [[USB-накопитель]]. С помощью инструмента [http://docs.micropython.org/en/latest/reference/pyboard.py.html#pyboard-py pyboard.py] [[ПК-хост]] может получить доступ к файловой системе на всех портах.
 
'''Примечание:''' Это касается, как правило, ''«голых»'' устройств вроде [[STM32]] или [[ESP32]]. На портах с операционной системой (например, на [[Unix-порт]]е) за файловую систему отвечает [[ОС]] хоста.
 
== VFS ==
 
В MicroPython используется [[VFS]] (от англ. ''«virtual file system»'', что можно перевести как ''«виртуальная файловая система»'') на манер той, что используется в [[Unix]]. Все смонтированные файловые системы комбинируются в одну виртуальную файловую систему с корневой директорией ''«/»''. Файловые системы монтируются в директории этой структуры, и при запуске рабочей директорией становится та, куда смонтирована главная файловая система.
 
На [[STM32]]/[[Pyboard]] внутренняя [[flash-память]] монтируется в ''«/flash»'', а опциональная [[SD-карта]] – в ''«/sd»''. На [[ESP8266]]/[[ESP32]] главная файловая система монтируется в ''«/»''.
 
== Блочные устройства ==
 
Блочное устройство – это экземпляр класса, в котором реализован протокол [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev uos.AbstractBlockDev].
 
=== Встроенные блочные устройства ===
 
В некоторых портах есть встроенные блочные устройства для доступа к их основной [[flash-памяти]].
 
При включении [[MicroPython]] попытается определить файловую систему на основной [[flash-памяти]], а также автоматически настроить и смонтировать ее. Если файловой системы найдено не будет, [[MicroPython]] попытается создать [[FAT]]-систему на всей [[flash-памяти]]. У используемого вами порта также может быть механизм сброса основной [[flash-памяти]] к заводским настройкам – обычно это выполняется нажатием на определенные кнопки при включении.
 
==== STM32/Pyboard ====
 
На этих платформах доступ к внутренней [[flash-памяти]] осуществляется с помощью класса [http://docs.micropython.org/en/latest/library/pyb.Flash.html#pyb-flash pyb.Flash]. На некоторых платах с большой внешней памятью (вроде Pyboard D) именно она будет использоваться вместо внутренней [[flash-памяти]]. При этом обязательно нужно указать именованный аргумент (kwarg) start – например, '''pyb.Flash(start=0)'''.
 
'''Примечание:''' При конструировании без аргументов – например, '''pyb.Flash()''' – в этом классе будет реализован только простой блочной интерфейс и отражено виртуальное устройство, показываемое [[USB-накопителю]] (т.е. на старте в нем будет виртуальная таблица разделов). Это сделано в целях обратной совместимости.
 
==== ESP8266 ====
 
Внутренняя [[flash-память]] отображается в виде объекта блочного устройства, создаваемого на запуске в модуле [[flashbdev]]. По умолчанию этот объект добавляется как глобальная переменная, поэтому, как правило, доступ к нему можно получить просто через [[bdev]]. Это реализует расширенный интерфейс.
 
==== ESP32 ====
 
Класс [http://docs.micropython.org/en/latest/library/esp32.html#esp32.Partition esp32.Partition] реализует блочное устройство для разделов, заданных для платы. Как и в случае с [[ESP8266]], в нем есть глобальная переменная [[bdev]], в которой задано базовое исходное разделение. Это реализует расширенный интерфейс.
 
=== Настраиваемые блочные устройства ===
 
Класс ниже реализует простое блочное устройство, которое хранит свои данные в [[RAM-памяти]] при помощи [[bytearray]]:
 
<syntaxhighlight lang="python">
class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)
 
    def readblocks(self, block_num, buf):
        for i in range(len(buf)):
            buf[i] = self.data[block_num * self.block_size + i]
 
    def writeblocks(self, block_num, buf):
        for i in range(len(buf)):
            self.data[block_num * self.block_size + i] = buf[i]
 
    def ioctl(self, op, arg):
        if op == 4: # считываем количество блоков
            return len(self.data) // self.block_size
        if op == 5: # считываем размер блока
            return self.block_size
</syntaxhighlight>
 
Воспользоваться им можно следующим образом:
 
<syntaxhighlight lang="python">
import os
 
bdev = RAMBlockDev(512, 50)
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/ramdisk')
</syntaxhighlight>
 
Ниже показан пример блочного устройства, поддерживающего простой и расширенный интерфейс – т.е. одновременно сигнатуры и поведение обоих форм методов [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev.readblocks uos.AbstractBlockDev.readblocks()] и [http://docs.micropython.org/en/latest/library/uos.html#uos.AbstractBlockDev.writeblocks uos.AbstractBlockDev.writeblocks()]:
 
<syntaxhighlight lang="python">
class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)
 
    def readblocks(self, block_num, buf, offset=0):
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            buf[i] = self.data[addr + i]
 
    def writeblocks(self, block_num, buf, offset=None):
        if offset is None:
            # сначала стираем, потом записываем:
            for i in range(len(buf) // self.block_size):
                self.ioctl(6, block_num + i)
            offset = 0
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            self.data[addr + i] = buf[i]
 
    def ioctl(self, op, arg):
        if op == 4: # счетчик блоков
            return len(self.data) // self.block_size
        if op == 5: # размер блока
            return self.block_size
        if op == 6: # стирание блока
            return 0
</syntaxhighlight>
 
Поскольку это блочное устройство поддерживает расширенный интерфейс, его можно использовать с [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs2 littlefs]:
 
<syntaxhighlight lang="python">
import os
 
bdev = RAMBlockDev(512, 50)
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/ramdisk')
</syntaxhighlight>
 
Когда файловая система будет смонтирована, ее (независимо от типа) можно использовать так, как ее обычно используют в [[Python-код]]е. Например:
 
<syntaxhighlight lang="python">
with open('/ramdisk/hello.txt', 'w') as f:
    f.write('Hello world')
print(open('/ramdisk/hello.txt').read())
</syntaxhighlight>
 
== Файловые системы ==
 
MicroPython-портами поддерживаются файловые системы [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsFat FAT], [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs1 littlefs v1] и [http://docs.micropython.org/en/latest/library/uos.html#uos.VfsLfs2 littlefs v2].
 
В таблице ниже показано, какие файловые системы включены в прошивку по умолчанию в разных портах/платах (они, впрочем, могут быть опционально включены в пользовательскую прошивку):
 
{| class="wikitable"
|-
! Плата !! FAT !! littlefs v1 !! littlefs v2
|-
| pyboard 1.0, 1.1, D || Да || Нет || Да
|-
| Другие STM32 || Да || Нет || Нет
|-
| ESP8266 (1M) || Нет || Нет || Да
|-
| ESP8266 (2M+) || Да || Нет || Да
|-
| ESP32 || Да || Нет || Да
|}
 
=== FAT ===
 
Главное преимущество файловой системы типа [[FAT]] в том, что доступ к ней можно получить с помощью [[USB-накопителя]] (на тех платах, где это поддерживается – например, на [[STM32]]) безо всяких дополнительных драйверов для [[ПК-хост]]а.
 
Однако [[FAT]] не терпима к перебоям питания во время записи – это может привести к повреждению файловой системы. Поэтому в приложениях, не требующих использования [[USB-накопителя]], рекомендуем использовать файловую систему типа ''littlefs''.
 
Чтобы отформатировать всю [[flash-память]] при помощи [[FAT]], выполните следующее:
 
<syntaxhighlight lang="python">
# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/')
 
# STM32:
import os, pyb
os.umount('/flash')
os.VfsFat.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')
</syntaxhighlight>
 
=== Littlefs ===
 
[https://github.com/ARMmbed/littlefs Littlefs] – это файловая система, разработанная специально устройств, полагающихся на [[flash-память]]. Она гораздо более устойчива к повреждениям.
 
'''Примечание:''' По сообщениям некоторых пользователей, ''littlefs v1'' и ''littlefs v2'' в некоторых ситуациях все же дают сбой. Более подробно читайте [https://github.com/ARMmbed/littlefs/issues/347 тут] и [https://github.com/ARMmbed/littlefs/issues/295 тут].
 
'''Примечание:''' Доступ к файловой системе ''littlefs'' можно получить и при помощи [[USB-накопителя]] – для этого нужно воспользоваться [https://github.com/ARMmbed/littlefs-fuse/tree/master/littlefs драйвером «littlefs-fuse»]. Помните, чтобы изменить размер блока, вам нужно будет воспользоваться опцией '''-b=4096'''.
 
Чтобы отформатировать всю [[flash-память]] при помощи ''littlefs v2'', выполните следующее:
 
<syntaxhighlight lang="python">
# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/')
 
# STM32:
import os, pyb
os.umount('/flash')
os.VfsLfs2.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')
</syntaxhighlight>
 
=== Гибридная система (STM32) ===
 
Вы можете создавать блочные устройства, занимающие лишь часть [[flash-памяти]] – это делается с помощью именованных аргументов '''start''' и '''len''' в '''pyb.Flash'''.
 
К примеру, чтобы настроить первые ''256 Кб'' как [[FAT]] (и сделать их доступными через USB-накопитель), а всю остальную память – как ''littlefs'', выполните следующее:
 
<syntaxhighlight lang="python">
import os, pyb
os.umount('/flash')
p1 = pyb.Flash(start=0, len=256*1024)
p2 = pyb.Flash(start=256*1024)
os.VfsFat.mkfs(p1)
os.VfsLfs2.mkfs(p2)
os.mount(p1, '/flash')
os.mount(p2, '/data')
os.chdir('/flash')
</syntaxhighlight>
 
То есть благодаря этому вы можете настроить доступ к Python-файлам, настройкам и прочему редко изменяемому контенту через [[USB-накопитель]], а часто изменяемые данные приложения – разместить на ''littlefs'', которая более устойчива к перебоям питания и т.д.
 
Разбитие на разделы со смещением ''«0»'' будет смонтировано и определение типа файловой системы будут выполнены автоматически, но если вы хотите сделать раздел ''«/data»'', в ''boot.py'' можно добавить следующее:
 
<syntaxhighlight lang="python">
import os, pyb
p2 = pyb.Flash(start=256*1024)
os.mount(p2, '/data')
</syntaxhighlight>
 
=== Гибридная система (ESP32) ===
 
Если вы создаете свою прошивку для [[ESP32]], то собственное разбиение на разделы можно задать, отредактировав ''partitions.csv''.
 
При загрузке раздел под названием ''«vfs»'' по умолчанию будет смонтирован в ''«/»'', а дополнительные разделы можно создать в ''boot.py'':
 
<syntaxhighlight lang="python">
import esp32, os
p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
os.mount(p, '/foo')
</syntaxhighlight>


=См.также=
=См.также=

Текущая версия от 18:18, 14 мая 2023

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Работа с файловыми системами[1]

В этой статье рассказывается о том, как MicroPython предоставляет доступ к файловой системе, смонтированной на устройство, позволяя использовать при работе с постоянной энергонезависимой памятью стандартные Python’овские файловые методы чтения/записи.

MicroPython автоматически задает настройки по умолчанию и определяет главную файловую систему, поэтому это руководство будет полезно в первую очередь, если вам нужно переопределить разделы файловой системы, изменить ее тип или воспользоваться блочными устройствами с вашими настройками.

Файловая система обычно монтируется на внутреннюю flash-память устройства, но она также может располагаться на внешней flash-памяти, в RAM-памяти или на блочном устройстве.

На некоторых портах (например, на STM32-порте) доступ к файловой системе можно получить на ПК через USB-накопитель. С помощью инструмента pyboard.py ПК-хост может получить доступ к файловой системе на всех портах.

Примечание: Это касается, как правило, «голых» устройств вроде STM32 или ESP32. На портах с операционной системой (например, на Unix-порте) за файловую систему отвечает ОС хоста.

VFS

В MicroPython используется VFS (от англ. «virtual file system», что можно перевести как «виртуальная файловая система») на манер той, что используется в Unix. Все смонтированные файловые системы комбинируются в одну виртуальную файловую систему с корневой директорией «/». Файловые системы монтируются в директории этой структуры, и при запуске рабочей директорией становится та, куда смонтирована главная файловая система.

На STM32/Pyboard внутренняя flash-память монтируется в «/flash», а опциональная SD-карта – в «/sd». На ESP8266/ESP32 главная файловая система монтируется в «/».

Блочные устройства

Блочное устройство – это экземпляр класса, в котором реализован протокол uos.AbstractBlockDev.

Встроенные блочные устройства

В некоторых портах есть встроенные блочные устройства для доступа к их основной flash-памяти.

При включении MicroPython попытается определить файловую систему на основной flash-памяти, а также автоматически настроить и смонтировать ее. Если файловой системы найдено не будет, MicroPython попытается создать FAT-систему на всей flash-памяти. У используемого вами порта также может быть механизм сброса основной flash-памяти к заводским настройкам – обычно это выполняется нажатием на определенные кнопки при включении.

STM32/Pyboard

На этих платформах доступ к внутренней flash-памяти осуществляется с помощью класса pyb.Flash. На некоторых платах с большой внешней памятью (вроде Pyboard D) именно она будет использоваться вместо внутренней flash-памяти. При этом обязательно нужно указать именованный аргумент (kwarg) start – например, pyb.Flash(start=0).

Примечание: При конструировании без аргументов – например, pyb.Flash() – в этом классе будет реализован только простой блочной интерфейс и отражено виртуальное устройство, показываемое USB-накопителю (т.е. на старте в нем будет виртуальная таблица разделов). Это сделано в целях обратной совместимости.

ESP8266

Внутренняя flash-память отображается в виде объекта блочного устройства, создаваемого на запуске в модуле flashbdev. По умолчанию этот объект добавляется как глобальная переменная, поэтому, как правило, доступ к нему можно получить просто через bdev. Это реализует расширенный интерфейс.

ESP32

Класс esp32.Partition реализует блочное устройство для разделов, заданных для платы. Как и в случае с ESP8266, в нем есть глобальная переменная bdev, в которой задано базовое исходное разделение. Это реализует расширенный интерфейс.

Настраиваемые блочные устройства

Класс ниже реализует простое блочное устройство, которое хранит свои данные в RAM-памяти при помощи bytearray:

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf):
        for i in range(len(buf)):
            buf[i] = self.data[block_num * self.block_size + i]

    def writeblocks(self, block_num, buf):
        for i in range(len(buf)):
            self.data[block_num * self.block_size + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # считываем количество блоков
            return len(self.data) // self.block_size
        if op == 5: # считываем размер блока
            return self.block_size

Воспользоваться им можно следующим образом:

import os

bdev = RAMBlockDev(512, 50)
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/ramdisk')

Ниже показан пример блочного устройства, поддерживающего простой и расширенный интерфейс – т.е. одновременно сигнатуры и поведение обоих форм методов uos.AbstractBlockDev.readblocks() и uos.AbstractBlockDev.writeblocks():

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf, offset=0):
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            buf[i] = self.data[addr + i]

    def writeblocks(self, block_num, buf, offset=None):
        if offset is None:
            # сначала стираем, потом записываем:
            for i in range(len(buf) // self.block_size):
                self.ioctl(6, block_num + i)
            offset = 0
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            self.data[addr + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # счетчик блоков
            return len(self.data) // self.block_size
        if op == 5: # размер блока
            return self.block_size
        if op == 6: # стирание блока
            return 0

Поскольку это блочное устройство поддерживает расширенный интерфейс, его можно использовать с littlefs:

import os

bdev = RAMBlockDev(512, 50)
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/ramdisk')

Когда файловая система будет смонтирована, ее (независимо от типа) можно использовать так, как ее обычно используют в Python-коде. Например:

with open('/ramdisk/hello.txt', 'w') as f:
    f.write('Hello world')
print(open('/ramdisk/hello.txt').read())

Файловые системы

MicroPython-портами поддерживаются файловые системы FAT, littlefs v1 и littlefs v2.

В таблице ниже показано, какие файловые системы включены в прошивку по умолчанию в разных портах/платах (они, впрочем, могут быть опционально включены в пользовательскую прошивку):

Плата FAT littlefs v1 littlefs v2
pyboard 1.0, 1.1, D Да Нет Да
Другие STM32 Да Нет Нет
ESP8266 (1M) Нет Нет Да
ESP8266 (2M+) Да Нет Да
ESP32 Да Нет Да

FAT

Главное преимущество файловой системы типа FAT в том, что доступ к ней можно получить с помощью USB-накопителя (на тех платах, где это поддерживается – например, на STM32) безо всяких дополнительных драйверов для ПК-хоста.

Однако FAT не терпима к перебоям питания во время записи – это может привести к повреждению файловой системы. Поэтому в приложениях, не требующих использования USB-накопителя, рекомендуем использовать файловую систему типа littlefs.

Чтобы отформатировать всю flash-память при помощи FAT, выполните следующее:

# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsFat.mkfs(bdev)
os.mount(bdev, '/')

# STM32:
import os, pyb
os.umount('/flash')
os.VfsFat.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')

Littlefs

Littlefs – это файловая система, разработанная специально устройств, полагающихся на flash-память. Она гораздо более устойчива к повреждениям.

Примечание: По сообщениям некоторых пользователей, littlefs v1 и littlefs v2 в некоторых ситуациях все же дают сбой. Более подробно читайте тут и тут.

Примечание: Доступ к файловой системе littlefs можно получить и при помощи USB-накопителя – для этого нужно воспользоваться драйвером «littlefs-fuse». Помните, чтобы изменить размер блока, вам нужно будет воспользоваться опцией -b=4096.

Чтобы отформатировать всю flash-память при помощи littlefs v2, выполните следующее:

# ESP8266 и ESP32:
import os
os.umount('/')
os.VfsLfs2.mkfs(bdev)
os.mount(bdev, '/')

# STM32:
import os, pyb
os.umount('/flash')
os.VfsLfs2.mkfs(pyb.Flash(start=0))
os.mount(pyb.Flash(start=0), '/flash')
os.chdir('/flash')

Гибридная система (STM32)

Вы можете создавать блочные устройства, занимающие лишь часть flash-памяти – это делается с помощью именованных аргументов start и len в pyb.Flash.

К примеру, чтобы настроить первые 256 Кб как FAT (и сделать их доступными через USB-накопитель), а всю остальную память – как littlefs, выполните следующее:

import os, pyb
os.umount('/flash')
p1 = pyb.Flash(start=0, len=256*1024)
p2 = pyb.Flash(start=256*1024)
os.VfsFat.mkfs(p1)
os.VfsLfs2.mkfs(p2)
os.mount(p1, '/flash')
os.mount(p2, '/data')
os.chdir('/flash')

То есть благодаря этому вы можете настроить доступ к Python-файлам, настройкам и прочему редко изменяемому контенту через USB-накопитель, а часто изменяемые данные приложения – разместить на littlefs, которая более устойчива к перебоям питания и т.д.

Разбитие на разделы со смещением «0» будет смонтировано и определение типа файловой системы будут выполнены автоматически, но если вы хотите сделать раздел «/data», в boot.py можно добавить следующее:

import os, pyb
p2 = pyb.Flash(start=256*1024)
os.mount(p2, '/data')

Гибридная система (ESP32)

Если вы создаете свою прошивку для ESP32, то собственное разбиение на разделы можно задать, отредактировав partitions.csv.

При загрузке раздел под названием «vfs» по умолчанию будет смонтирован в «/», а дополнительные разделы можно создать в boot.py:

import esp32, os
p = esp32.Partition.find(esp32.Partition.TYPE_DATA, label='foo')
os.mount(p, '/foo')

См.также

Внешние ссылки