что такое прямой доступ к памяти и как он реализуется

Прямой доступ к памяти

Обзор передачи данных с прямым доступом к памяти

До введения подробностей программирования, давайте рассмотрим, как выполняются передачи DMA, для упрощения обсуждения учитывая только входные передачи.

Передача данных может быть вызвана двумя способами: либо программа запрашивает данные (через такую функцию, как read ) или оборудование асинхронно помещают данные в систему. В первом случае вовлечённые этапы могут быть подытожены следующим образом:

2. Оборудование записывает данные в буфер DMA и вызывает прерывание, когда это выполнено.

3. Обработчик прерывания получает входные данные, подтверждает прерывание и пробуждает процесс, который теперь имеет возможность прочитать данные.

Второй случай наступает, когда DMA используется асинхронно. Так происходит, например, с устройством сбора данных, которое выполняет выдаёт данные, даже если никто их не читает. В этом случае драйвер должен содержать буфер, чтобы последующий вызов read вернул все накопленные данные в пространство пользователя. Этапы, связанные с такого рода передачей, немного отличаются:

1. Оборудование вызывает прерывание, чтобы объявить, что поступили новые данные.

2. Обработчик прерывания выделяет буфер и сообщает оборудованию, куда передать данные.

3. Периферийное устройство записывает данные в буфер и по завершении вызывает другое прерывание.

4. Обработчик отправляет новые данные, будит любой соответствующий процесс, и заботится о ведении хозяйства.

Вариант асинхронного подхода часто виден с сетевыми картами. Эти карты часто ожидают увидеть круговой буфер (часто называемый DMA-шным буфером), находящийся в памяти, используемой совместно с процессором; каждый входящий пакет помещается в следующий доступный буфер в круге и сигнализируется прерыванием. Затем драйвер передаёт сетевые пакеты остальной части ядра и помещает новый буфер DMA в круг.

Обработка шаги во всех этих случаях подчеркнуть, что эффективная обработка DMA опирается на прерывание отчетности. Хотя можно реализовать DMA с опрашивающим драйвером, это не будет иметь смысла, потому что опрашивающий драйвер сведёт на нет преимущества производительности, которые предлагает DMA вместо более простого ввода/вывода, управляемого процессором. (* Конечно, из всего есть исключения; смотрите раздел «Уменьшение числа прерываний» в Главе 17 для демонстрации того, как высокопроизводительные сетевые драйверы лучше реализуются с использованием опроса.)

Выделение DMA буфера

Этот раздел описывает выделение DMA буферов на низком уровне; в ближайшее время мы введём высокоуровневый интерфейс, но ещё неплохо понимать материал, представленный здесь.

Основным вопросом, который остро стоит с DMA буферами является тот, что когда они больше, чем одна страница, они должны занимать смежные страницы в физической памяти, так как устройство передаёт данные с помощью системной шины ISA или PCI, обе из которых имеют физические адреса. Интересно отметить, что это ограничение не распространяется на SBus (смотрите раздел «SBus» в Главе 12), которая использует на периферийной шине виртуальные адреса. Некоторые архитектуры могут также использовать виртуальные адреса на шине PCI, но переносимый драйвер не может рассчитывать на такую возможность.

Большинство устройств на современных шинах могут обрабатывать 32-х разрядные адреса, это означает, что для них прекрасно работает обычное выделение памяти. Однако, некоторые PCI устройства, не следуют полному стандарту PCI и не могут работать с 32-х разрядными адресами. И конечно, устройства ISA ограничены только 24-х разрядными адресами.

Самостоятельное выделение

dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);

Однако, если вы заходите так далеко, что выделяете большой буфер DMA, стоит допустить мысли об альтернативах. Если ваше устройство может выполнять ввод/вывод с разборкой/сборкой, вы можете выделить ваш буфер небольшими кусочками и пусть устройство сделает всё остальное. Ввод/вывод с разборкой/сборкой также может быть использован при выполнении прямого ввода/вывода в пользовательском пространстве, что может быть лучшим решением, когда требуется действительно огромный буфер.

Шинные адреса

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

unsigned long virt_to_bus(volatile void *address);

void *bus_to_virt(unsigned long address);

Эти функции выполняют простые преобразования между логическими адресами ядра и шинными адресами. Они не будут работать в любой ситуации, где устройство управления памятью ввода/вывода должно быть запрограммировано или где должны быть использованы возвратные буферы. Правильным способом выполнения этого преобразования является использование универсального уровня DMA, так что мы теперь перейдём к этой теме.

Универсальный уровень DMA

DMA операции, в конечном счёте, сводятся к выделению буфера и передачи вашему устройству шинных адресов. Тем не менее, задача написания переносимых драйверов, которые выполняют DMA безопасно и корректно на всех архитектурах значительно труднее, чем можно подумать. Разные системы имеют разные представления о том, как должно работать согласование кэша; если вы не решите этот вопрос правильно, ваш драйвер может повредить память. Некоторые системы имеют сложное шинное оборудование, которое может сделать задачу DMA легче или труднее. И не все системы могут выполнять DMA из всех частей памяти. К счастью, ядро предоставляет шинно- и архитектурно-независимый уровень DMA, который скрывает большинство из этих проблем от автора драйвера. Мы настоятельно рекомендуем вам использовать этот уровень для DMA операций в любой драйвере, который вы пишете.

Работа с проблемным оборудованием

Первый вопрос, на который необходимо ответить, прежде чем пытаться выполнять DMA, является ли данное устройство способным на такие операции на текущем оборудовании. Многие устройства ограничены в диапазоне памяти, который они могут адресовать, по ряду причин. По умолчанию ядро предполагает, что устройство может выполнять DMA на любой 32-х разрядный адрес. Если это не так, вы должны сообщить ядру об этом факте сделав вызов:

int dma_set_mask(struct device *dev, u64 mask);

mask должна указать биты, которые ваше устройство может адресовать; например, если оно ограничено 24-мя битами, вам бы передали mask как 0x0FFFFFF. Возвращаемое значение отлично от нуля, если с заданной маской DMA возможен; если dma_set_mask возвращает 0, вы не можете использовать DMA операции с этим устройством. Таким образом, код инициализации драйвера для устройства, ограниченного 24-х разрядными DMA операциями, может выглядеть так:

if (dma_set_mask (dev, 0xffffff))

card->use_dma = 0; /* Нам придётся жить без DMA */

printk (KERN_WARN, «mydev: DMA not supported\n»);

Опять же, если устройство поддерживает нормальные, 32-х разрядные операции DMA, вызывать dma_set_mask необходимости нет.

Отображения DMA

Заметим, что не все архитектуры имеют IOMMU; в частности, популярная платформа x86 не имеет поддержки IOMMU. Тем не менее, должным образом написанному драйверу нет необходимости знать о поддержке ввода/вывода оборудованием, на котором он выполняется.

Отображения DMA также должны обратиться к вопросу о согласовании кэша. Помните, что современные процессоры хранят копии наиболее часто используемых областей памяти в быстром, локальном кэше; без этого кэша приемлемая производительность невозможна. Если ваше устройство изменяет область основной памяти, крайне важно, чтобы любые процессорные кэши, охватывающие эту область, стали недействительными; в противном случае, процессор может работать с неправильным образом основной памяти и в результатом будет повреждение данных. Аналогично, когда ваше устройство использует DMA для чтения данных из основной памяти, любые изменения в этой памяти, находящиеся в кэшах процессора, должны быть прочитаны в первую очередь. Эти вопросы согласованности кэша могут создать бесконечные непонятные и труднообнаруживаемые ошибки, если программист не осторожен. Некоторые архитектуры управляют согласованием кэша аппаратно, но другие требуют поддержки от программного обеспечения. Универсальный уровень DMA делает многое, чтобы обеспечить, чтобы всё работало корректно на всех архитектурах, но, как мы увидим, надлежащее поведение требует соблюдения небольшого набора правил.

Код PCI различает два типа отображений DMA, в зависимости от ожидания того, как долго будет существовать буфер DMA:

Согласованные отображения DMA

Эти отображения обычно существуют во время жизни драйвера. Согласованный буфер должен быть одновременно доступным для процессора и периферии (другие типы отображения, как мы увидим позднее, могут быть доступны в любой момент времени только одному или другому). В результате согласованные отображения должны находиться в согласованной с кэшем памяти. Согласованные отображения могут быть дорогими в настройке и использовании.

Потоковые отображения DMA

Потоковые отображения, как правило, создаются на одну операцию. Как мы увидим, некоторые архитектуры позволяют значительную оптимизацию, когда используются потоковые отображения, но эти отображения также управляются строгим набором правил в том, как они могут быть доступны. Когда это возможно, разработчики ядра рекомендуют использовать потоковые отображения вместо согласованных отображений. Есть две причины для такой рекомендации. Первой является то, что в системах, которые поддерживают регистры отображения, каждое отображение DMA использует на шине один или более из них. Согласованные отображения, которые имеют долгую жизнь, могут монополизировать эти регистры в течение длительного времени, даже когда они не используются. Другая причина состоит в том, что на некотором оборудовании потоковые отображения могут быть оптимизированы способами, недоступными для согласованных отображений.

Эти два типа отображения должны управляться разными способами; настало время взглянуть на подробности.

Создание согласованных отображений DMA

Драйвер может создать согласованное отображение вызовом dma_alloc_coherent :

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);

Когда буфер больше не нужен (обычно во время выгрузки модуля), он должен быть возвращён системе с помощью dma_free_coherent :

void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle);

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

Пулы DMA

Пул DMA представляет собой механизм выделения для небольших, согласованных отображений DMA. Отображения, полученные от dma_alloc_coherent могут иметь минимальный размер в одну страницу. Если вашему устройству необходимы небольшие области DMA, чем это, лучше всего использовать пул DMA. Пулы DMA также полезны в ситуациях, когда может возникнуть соблазн выполнять DMA для небольших областей, встроенных в более крупные структуры. Некоторые весьма непонятные ошибки драйверов были прослежены до проблем согласования кэша с полями структур, находящимися рядом с небольшими участками DMA. Чтобы избежать этой проблемы, вы всегда должны выделять области для операций DMA явно, в стороне от других, не-DMA структур данных.

Пул DMA должен быть создан до использования вызовом:

struct dma_pool *dma_pool_create(const char *name, struct device *dev,

size_t size, size_t align,

Когда вы завершили работу с пулом, он может быть освобождён с помощью:

void dma_pool_destroy(struct dma_pool *pool);

Вы должны вернуть все выделения в пул до его уничтожения.

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

void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);

void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);

Создание потоковых отображений DMA

Потоковые отображения по ряду причин имеют более сложный интерфейс, чем согласованный вариант. Эти отображения рассчитывают на работу с буфером, который уже был выделен драйвером, и, следовательно, иметь дело с адресами, которые они не выбирают. На некоторых архитектурах потоковые отображения могут также иметь несколько несоприкасающихся страниц и составные буферы «разборки/сборки». По всем этим причинам потоковые отображения имеют свой собственный набор функций отображения.

При создании потокового отображения вы должны сообщить ядру, в каком направлении движутся данные. Для этой цели были определены некоторые символы (с типом enum dma_data_direction ):

Этот символ предоставляется только как помощь при отладке. Попытки использовать буферы с этим «направлением» вызовут панику ядра.

Если у вас для передачи есть единственный буфер, отображайте его с помощью dma_map_single :

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size,

enum dma_data_direction direction);

Возвращаемым значением является шинный адрес, который вы можете передать в устройство или NULL, если что-то пойдёт не так.

После завершения передачи отображение должно быть удалено с помощью dma_unmap_single :

void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,

enum dma_data_direction direction);

Здесь, аргументы size и direction должны соответствовать тем, которые использовались для отображения буфера.

Для потокового отображения DMA применяются некоторые важные правила:

• Буфер должен быть использован только для передачи, которая соответствует значению заданного при отображении направления.

• После того, как буфер был отображён, он принадлежит устройству, а не процессору. До тех пор, пока буфер не отключён, драйвер не должен касаться его содержимого любым способом. Драйверу безопасно обращаться к содержимому буфера только после того, как была вызвана dma_unmap_single (с одним исключением, которое мы увидим в ближайшее время). Среди прочего, это правило предполагает, что буфер записи для устройство не может быть отображён, пока он не содержит все данные для записи.

• Буфер не должен быть отключён во время активности DMA, либо гарантирована серьёзная нестабильность системы.

Кстати, возвратные буферы являются одной из причин, почему важно правильно задавать направление. Возвратные буферы при DMA_BIDIRECTIONAL копируются до и после операции, что часто является ненужной тратой циклов процессора.

Иногда драйверу необходимо получить доступ к содержимому потокового буфера DMA без его отключения. Это делает возможным следующий вызов:

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr,

size_t size, enum dma_data_direction direction);

Эта функция должна быть вызвана перед тем, как процессор обращается к потоковому буферу DMA. После того, как вызов был сделан, процессор «владеет» буфером DMA и может работать с ним, как это требуется. Однако, перед доступом в буфер устройства, право собственности должно быть ему возвращено:

void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr,

size_t size, enum dma_data_direction direction);

Процессор, опять же, не должен обращаться к буферу DMA после того, как был сделан этот вызов.

Одностраничные потоковые отображения

dma_addr_t dma_map_page(struct device *dev, struct page *page,

unsigned long offset, size_t size,

enum dma_data_direction direction);

void dma_unmap_page(struct device *dev, dma_addr_t dma_address,

size_t size, enum dma_data_direction direction);

Преобразования разборки/сборки

Многие устройства могут принимать список разборки (scatterlist) из указателей массивов и длин, и передавать их все за одну операцию DMA; например, сетевое «нулевое копирование» (“zero-copy”) становится проще, если пакеты могут быть собраны из множества кусков. Другой причиной отображать списки разборки как целое является использование преимуществ систем, которые имеют в шинном оборудовании регистры отображения. На таких системах с точки зрения устройства физически разрозненные страницы могут быть собраны в единый непрерывный массив. Этот метод работает только когда записи в списке разборки равны по длине размеру страницы (за исключением первого и последнего), но когда он работает, это может обернуться несколькими операциями в одном DMA и, соответственно, ускорить процесс.

Наконец, если должен быть использован возвратный буфер, имеет смысл объединять весь список в один буфер (поскольку он всё равно копируется).

unsigned int length;

unsigned int offset;

Длина этого буфера и его смещение на странице.

int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,

enum dma_data_direction direction)

Для каждого буфера для передачи в устройство во входном списке разборки dma_map_sg определяет собственный шинный адрес. В рамках этой задачи, она также объединяет буферы, которые находятся в памяти рядом друг с другом. Если система, на которой работает ваш драйвер, имеет блок управления памятью ввода/вывода, dma_map_sg также программирует регистры устройства отображения, с возможным результатом, что с точки зрения вашего устройства вы сможете передать единый непрерывный буфер. Однако, вы никогда не узнаете, как будет выглядеть результирующая передача, пока не сделаете вызов.

dma_addr_t sg_dma_address(struct scatterlist *sg);

Возвращает шинный (DMA) адрес из этой записи списка разборки.

unsigned int sg_dma_len(struct scatterlist *sg);

Возвращает длину этого буфера.

После завершения передачи, отображение разборки/сборки отключается с помощью вызова dma_unmap_sg :

void dma_unmap_sg(struct device *dev, struct scatterlist *list,

int nents, enum dma_data_direction direction);

Отображения разборки/сборки являются потоковыми отображениями DMA и к ним, как к одной из разновидностей, применяются те же правила доступа. Если необходимо получить доступ к отображённому списку разборки/сборки, сначала необходимо его синхронизировать:

void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,

int nents, enum dma_data_direction direction);

void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,

int nents, enum dma_data_direction direction);

Двухадресный цикл отображения PCI

Обычно, уровень поддержки DMA работает с 32-х разрядными шинными адресами, возможно, ограниченными маской DMA определённого устройства. Шина PCI, однако, также поддерживает 64-х разрядный режим адресации с циклом двойной адресации (double-address cycle, DAC). Универсальный уровень DMA не поддерживает этот режим по ряду причин, первая из которых в том, что это возможность специфична для PCI. Кроме того, во многих реализациях DAC в лучшем случае глючит и поскольку DAC медленнее, чем обычный, 32-х разрядный DMA, это может быть накладным расходом. Тем не менее, существуют приложения, где использование DAC может быть тем, что делать правильно; если вы имеете устройство, которое может работать с очень большими буферами, размещёнными в верхней памяти, вы можете рассмотреть вопрос о реализации поддержки DAC. Эта поддержка доступна только для шины PCI, так что должны быть использованы процедуры, предназначенные для PCI.

int pci_dac_set_dma_mask(struct pci_dev *pdev, u64 mask);

Вы можете использовать DAC адресацию только если эта функция возвращает 0.

Для DAC отображений используется специальный тип ( dma64_addr_t ). Чтобы установить одно из таких отображений, вызовите pci_dac_page_to_dma :

dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page,

unsigned long offset, int direction);

Отображения DAC не требуют внешних ресурсов, поэтому нет необходимости явно освобождать их после использования. Следует, однако, относиться к DAC отображениям как и другим потоковым отображениям и соблюдать правила, касающиеся владения буфером. Существует набор функций для синхронизации буферов DMA, которые аналогичны универсальному варианту:

void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev,

void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev,

Простой пример DMA для PCI

В качестве примера того, как могут быть использованы отображения DMA, приведём простой пример кодирования DMA для PCI устройства. Фактический вид операций DMA на шине PCI сильно зависит от управляемого устройства. Таким образом, этот пример не будет применим к какому-то реальному устройству; вместо этого, он является частью гипотетического драйвера, названного dad (DMA Acquisition Device, Устройство, получающее DMA). Драйвер для этого устройства может определить передающую функцию следующим образом:

int dad_transfer(struct dad_dev *dev, int write, void *buffer, size_t count)

/* Отобразить буфер для DMA */

Источник

Прямой доступ к памяти.

в Компьютеры 01.10.2018 0 249 Просмотров

Прямой доступ к памяти, иногда называемый DMA, представляет собой способ передачи данных из памяти произвольного доступа в другую часть компьютера, не занимая центральный процессор. Эта возможность встроена в большинство современных компьютерных систем. Это позволяет компьютеру выполнять сразу несколько задач, что в конечном итоге ускоряет работу компьютера.
Так же, как владелец малого бизнеса имеет дело со всеми отчетами и информацией, входящей или выходящей из его офиса, центральный блок обработки данных компьютера должен обрабатывать все входные или выходные данные компьютера. До прямого доступа к памяти загрузка или выгрузка данных занимала всё внимание центрального процессора. Он мог выполнять только одну задачу за один раз.

что такое прямой доступ к памяти и как он реализуется. Смотреть фото что такое прямой доступ к памяти и как он реализуется. Смотреть картинку что такое прямой доступ к памяти и как он реализуется. Картинка про что такое прямой доступ к памяти и как он реализуется. Фото что такое прямой доступ к памяти и как он реализуется

Компьютер с прямым доступом к памяти похож на владельца малого бизнеса с несколькими помощниками. Вместо того, чтобы иметь дело со всей информацией напрямую, центральный процессор может делегировать задачу контроллеру прямого доступа к памяти. Это устройство, встроено в материнскую плату, которое осуществляет прямой доступ к операциям в памяти. Центральный процессор может выполнять другие задачи когда происходит загрузка или выгрузка данных. Когда делегированная задача завершена, контроллер DMA сообщает об этом центральному блоку обработки данных.

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

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

Недостатком DMA является то, что он может вызвать потерю согласованности кеша. В основном это означает, что данные перемещаются всё время и могут храниться в нескольких временных местах. Проблема заключается в том, что когда компьютер запрашивает доступ к информации, он может не получить доступ к самой последней информации. Производители компьютеров могут справиться с этим с помощью специального оборудования или путём программирования операционной системы для защиты от потери согласованности кеша.

Источник

Структура микропроцессорной системы

Прямой доступ к памяти

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

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

Схема включения КПДП в состав микропроцессорной системы представлена на рис. 8.4.

что такое прямой доступ к памяти и как он реализуется. Смотреть фото что такое прямой доступ к памяти и как он реализуется. Смотреть картинку что такое прямой доступ к памяти и как он реализуется. Картинка про что такое прямой доступ к памяти и как он реализуется. Фото что такое прямой доступ к памяти и как он реализуется

Перед началом работы контроллер ПДП необходимо инициализировать: занести начальный адрес области ОП, с которой производится обмен, и длину передаваемого массива данных. В дальнейшем по сигналу запроса прямого доступа контроллер фактически выполняет все те действия, которые обеспечивал микропроцессор при программно управляемой передаче.

Последовательность действий КПДП при запросе на прямой доступ к памяти со стороны внешнего устройства следующая:

Прямой доступ к памяти позволяет осуществлять обмен данными между внешним устройством и оперативной памятью параллельно с выполнением процессором программы.

Структура КПДП представлена на рис. 8.5.

что такое прямой доступ к памяти и как он реализуется. Смотреть фото что такое прямой доступ к памяти и как он реализуется. Смотреть картинку что такое прямой доступ к памяти и как он реализуется. Картинка про что такое прямой доступ к памяти и как он реализуется. Фото что такое прямой доступ к памяти и как он реализуется

В состав каждого канала входят следующие регистры:

Значения в регистрах BAR и WCR устанавливаются при инициализации и в ходе циклов ПДП не меняются. В регистры CAR и CWR в начале выполнения ПДП заносятся значения из регистров BAR и WCR соответственно. При выполнении ПДП эти регистры изменяются.

Управляющие регистры, общие для всего контроллера:

что такое прямой доступ к памяти и как он реализуется. Смотреть фото что такое прямой доступ к памяти и как он реализуется. Смотреть картинку что такое прямой доступ к памяти и как он реализуется. Картинка про что такое прямой доступ к памяти и как он реализуется. Фото что такое прямой доступ к памяти и как он реализуется

что такое прямой доступ к памяти и как он реализуется. Смотреть фото что такое прямой доступ к памяти и как он реализуется. Смотреть картинку что такое прямой доступ к памяти и как он реализуется. Картинка про что такое прямой доступ к памяти и как он реализуется. Фото что такое прямой доступ к памяти и как он реализуется

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *