NVMe Namespaces: возможности и подводные камни

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


Уверен, многие слышали про NVM Express, или просто NVMe. Изначально для меня это были просто быстрые диски. Потом я осознал, что это интерфейс для подключения этих дисков. Затем стал понимать NVMe как протокол для передачи данных по PCIe-шине. И не просто протокол, а протокол, разработанный специально для твердотельных накопителей!

Мало-помалу я пришел к пониманию, что это целая спецификация. И понеслось… Сколько интересных особенностей использования, оказывается, существует у NVMe. Сколько всего придумано… Было даже немного обидно, что мимо меня прошел такой пласт информации.

Так что же такое NVMe? Рассмотрим подробнее.

Началось все с SATA-интерфейса. На самом деле до него существовала масса других интерфейсов — SCSI, UltraSCSI, ATA, PATA и прочие, но это уже дела давно минувших дней. В этой статье мы рассмотрим только актуальные сейчас интерфейсы.

Скорость передачи данных по SATA-интерфейсу достигает 560 Мбайт/с, чего с лихвой хватает для HDD-дисков, производительность которых варьируется от 90 до 235 Мбайт/с (есть отдельные прототипы, скорость которых достигает 480 Мбайт/с). Но вот для SSD-дисков этого недостаточно, уже сейчас их производительность достигает от 3000 до 3500 Мбайт/с. SAS-интерфейс также не подойдет, его максимальная скорость всего лишь 1200 Мбайт/c.

Для того, чтобы реализовать весь потенциал твердотельных накопителей, великие умы решили использовать PCIe-интерфейс. Сейчас он позволяет передавать данные со скоростью от 8 до 32 Гбайт/c. Чтобы унифицировать подключение SSD к PCIe-интерфейсу, была разработана спецификация NVMe. Точно так же, как в свое время была создана спецификация для подключения USB-Flash-накопителей.

Полностью разбирать NVMe мы не будем. В этой статье я хочу поделиться с вами сильно заинтересовавшей меня особенностью — NVMe Namespaces, или Пространства имен.

Надеюсь, среди читателей есть те, кто интересуется изучением этой технологии. Буду рад комментариям опытных пользователей. А если вы, как и я, только начинаете разбираться в теме, рекомендую ознакомиться с циклом статей об истории SSD. В свое время она помогла мне утрамбовать и разложить по полочкам много хлама в моей голове.

Ближе к NVMe


NVMe в Linux


Начну издалека. К изучению и поиску информации о пространствах имен меня побудил вопрос: «Почему NVMe-диски в Linux называются именно так?»

Все привыкли к обозначению дисков в Linux в виде блочных устройств. Они представляют собой файловые дескрипторы, которые обеспечивают интерфейс для взаимодействия с физическими или виртуальными устройствами. И такие устройства имеют не случайное, а вполне строгое название, содержащее некоторую информацию. Обратимся к следующему выводу из devfs:

root@thinkpad-e14:~$ ls -l /dev/ | grep -E "nvme"
crw-------  1 root    root    241,   0 окт 25 22:04 nvme0
brw-rw----  1 root    disk    259,   0 окт 25 22:04 nvme0n1
brw-rw----  1 root    disk    259,   1 окт 25 22:04 nvme0n1p1
brw-rw----  1 root    disk    259,   2 окт 25 22:04 nvme0n1p2
brw-rw----  1 root    disk    259,   3 окт 25 22:04 nvme0n1p3

Здесь перечислены все NVMe-устройства, подключенные к рабочей машине. Рассмотрим блочное устройства /dev/nvme0n1p1. Часть nvme, как ни странно, применяется для NVMe-устройств. Число, идущее следом, обозначает порядковый номер контроллера диска, который отвечает за все операции, производимые с накопителем. Заключительная часть p1 указывает на номер раздела на диске. И наконец, часть, которая заслуживает нашего внимания, — n1. Это и есть номер пространства.

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

/dev/sda — аналог /dev/nvme0n1
/dev/sda1 — аналог /dev/nvme0n1p1

Обратите внимание на устройство /dev/nvme0. Это NVMe-контроллер. Он является символьным устройством. Таким образом, мы можем обращаться к нему, отправляя определенные команды, чем мы и воспользуемся далее.

Namespace vs Partition


У вас может возникнуть вопрос: чем namespace отличается от partition? Отбросим все возможности и преимущества NVMe Namespace. Partition — это раздел диска на уровне хоста. Namespace же является разделом на уровне контроллера. То есть Namespace является неким логическим пространством, с которым хост работает как с блочным устройством.

Еще один уровень разделов добавляет гибкости в организации системы хранения, что позволяет использовать различные технологии, направленные на увеличение надежности, производительности и безопасности данных. Мы рассмотрим их позже.

Параметры NVMe-контроллера


Всеми операциями с данными занимается специальный NVMe-контроллер. Помимо этого, в своей памяти он хранит метаданные о себе и внутреннем устройстве информации: серийный номер, модель, всевозможные настройки диска, пространств, закрепленных за ним, формат данных и так далее.
Предлагаю рассмотреть их под микроскопом. Для этого я отправил контроллеру специальную команду, в ответ на которую он вернул метаданные. Позже я объясню, как это сделать, а сейчас взгляните на них. Так как данных достаточно много, я не стал вставлять их целиком, но вы обязаны знать, что те параметры, о которых я буду говорить далее, на самом деле существуют:

{
  "vid" : 5197,
  "ssvid" : 5197,
  "sn" : "00000000000000",
  "mn" : "00000000000000000000000000",
  "fr" : "7L1QFXV7",
  "rab" : 2,
  "ieee" : 9528,
  "cmic" : 0,
  "mdts" : 9,
  "cntlid" : 5,
  "ver" : 66304,
  "rtd3r" : 100000,
  "rtd3e" : 8000000,
  "oaes" : 512,
  "ctratt" : 0,
  "rrls" : 0,
  "crdt1" : 0,
  "crdt2" : 0,
  "crdt3" : 0,
  "oacs" : 23,
  "tnvmcap" : 256060514304,
  "unvmcap" : 0,
  ...
}

Метаданные хранятся в контроллере в виде последовательности байтов в порядке от старшего к младшему, поэтому далее я буду придерживаться следующего формата записи:
[интервал в байтах (формата Big-Endian)] / название параметра / расшифровка.

Пример для лучшего понимания. Следующая запись означает, что с 71 по 64 байт хранится значение параметра fr, который расшифровывается как firmware revision:

[71:64] / fr / firmware revision.
[23:4] / sn / serial number. Содержит серийный номер контроллера.
[63:24] / mn / model number. Содержит номер модели, или part number.
[71:64] / fr / firmware revision. Содержит номер ревизии прошивки контроллера.
[257:256] / oacs / optional admin command support. Указывает на наличие дополнительных команд и функций контроллера. Состоит он из 16 бит, каждый из которых отвечает за определенную команду. Если бит равен 1, то контроллер дает возможность:

  • [15:10] — зарезервированы;
  • [9] — получить статус LBA;
  • [8] — получить доступ к дорбелл буферу («Doorbell Buffer Config»);
  • [7] — управлять виртуализацией («Virtualization Management»);
  • [6] — использовать команды NVMe-Mi Recieve и NVMe-Mi Send («NVMe Management Interface»);
  • [5] — использовать директивы («Directives»);
  • [4] — использовать команды для самопроверки («Self-Test Commands»);
  • [3] — управлять пространствами имен («Namespace Management»);
  • [2] — обновлять прошивку контроллера (команды «Firmware Commit» и «Firmware Download»);
  • [1] — форматировать пространства имен («NVM Format»);
  • [0] — поддержка команд протокола безопасности («Security Send», «Security Receive»).

В данной статье мы затронем только те функции, которые относятся к пространствам имен, а именно «Namespace Management» и «NVM Format». Если вас интересуют подробности о других функциях, можно обратиться к спецификации NVM Express Revision 1.4.

Параметры NVMe-пространств


Теперь рассмотрим метаданные NVMe-пространств:

{
  "nsze" : 500118192,
  "ncap" : 500118192,
  "nuse" : 233042000,
  "nsfeat" : 0,
  "nlbaf" : 0,
  "flbas" : 0,
  "mc" : 0,
  "dpc" : 0,
  "dps" : 0,
  "nmic" : 0,
  "rescap" : 0,
  ...
  ]
}

[7:0] / nsze / namespace size. Это максимальный размер пространства в логических блоках. В данном случае — 500118192 512-байтных блоков, что, кстати, и указано в выводе blockdev:

root@thinkpad-e14:~$ sudo blockdev --getsz /dev/nvme0n1
500118192

[15:8] / ncap / namespace capacity. Это количество логических блоков, выделенных пространству для хранения данных в текущий момент.

[23:17] / nuse / namespace use. Это количество логических блоков, занятых данными в текущий момент.

Обратите внимание, что параметры nsze и ncap равны. Какой смысл указывать максимальный объем и объем, который выделен в текущий момент? Значит ли это, что в текущий момент может быть выделено меньше, чем доступно? Да!

Дело в том, что пространства имен поддерживают технологию Thin Provisioning. Это означает, что пространству выделяется только часть от указанного объема. Другая часть будет оставаться в общем пуле и выделяться этому или другим пространствам по требованию: при заполнении выделенного объема или при достижении критического порога. В общем, эта технология позволяет эффективнее использовать ресурсы для хранения данных. Подробнее и нагляднее с этой технологией можно ознакомиться в этой статье.

В нашей же ситуации nsze и ncap равны, так как пространство было создано без поддержки тонкого распределения. Выглядеть такое пространство на диске будет так:


ncap и nsize будут указывать на одно 一 общий объем пространства. В случае с использованием тонкого распределения на диске пространство будет выглядеть так:


Здесь nsze указывает на максимальный объем, ncap — на выделенный, а nuse в обоих случая показывает только то, что занято. Когда значение nuse достигнет значения ncap, то ncap увеличится, но не больше, чем nsze.

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

[24:24] / nsfeat / namespace features. Этот параметр особенно интересен. Он указывает на наличие дополнительных особенностей пространства. Состоит он из 8 бит (перечислены они тоже в Big Endian), каждый из которых отвечает за определенную функцию. Если значение бита равно 1, то функция активна, 0 — нет:

  • [7:5] — зарезервированы;
  • [4:4] — поддержка дополнительных полей для оптимизации ввода-вывода;
  • [3:3] — отключение переиспользования поля NGUID;
  • [2:2] — поддержка делоцированных и незаписанных блоков («Context Attributes»);
  • [1:1] — поддержка дополнительных полей для атомарной записи («Atomic Operations»);
  • [0:0] — поддержка тонкого распределения.

[26:26] / flbas / formatted lba size. Этот параметр указывает на структуру LBA. Также состоит из 8 бит:

  • [7:5] — зарезервированы;
  • [4:4] — при значении 1: указывает, что метаданные будут храниться в конце блока; при значении 0: метаданные передаются отдельным буфером;
  • [3:0] — позволяет выбрать один из 16 возможных форматов LBA.


[29:29] / dps / end-to-end data protection type settings. Указывает на тип сквозной защиты данных. Состоит из 8 бит:

  • [7:4] — зарезервированы;
  • [3:3] — указывает на тип передачи метаданных;
  • [2:0] — указывает на наличие защиты данных и ее тип.

[30:30] / nmic / namespace multi-path and Namespace Sharing Capabilities. Это поле указывает на поддержку функций, связанных с мультидоступом к пространствам имен:

  • [7:1]— зарезервированы;
  • [0:0]— значение 1 указывает на то, что данное пространство является публичным (public namespace) и может связываться с несколькими контроллерами, а значение 0 — на то, что пространство является приватным (private namespace) и привязывается только к одному.

На этом можно завершить краткий обзор параметров. Их количество огромно, что позволяет очень тонко настроить пространства имен под различные задачи, которые мы рассмотрим в конце статьи. Подробное описание каждого параметра можно изучить в спецификации NVM Express Revision 1.4.

Публичные и приватные пространства


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

Одним из этапов создания пространства является его закрепление за NVMe-контроллером. Доступ к пространству и будет осуществляться через тот контроллер, за которым оно закреплено. Но пространство может быть закреплено не только за одним контроллером — приватное, но и за несколькими контроллерами — публичное.

Если приватное пространство можно назвать обычным, так как из него ничего интересного слепить нельзя, то публичное пространство позволяет использовать такую возможность, как namespace multi-path.

Взаимодействие с NVMe


Приступим к вопросу взаимодействия с NVMe-устройствами: как посылать различные команды контроллеру, создавать пространства имен, форматировать их и т. д. Для этого в мире Linux существует утилита — nvme-cli. С ее помощью и можно выполнять данные операции.

nvme list


Чтобы вывести список NVMe-устройств, не обязательно обращаться к devfs подобным образом:

root@thinkpad-e14:~$ ls /dev/ | grep "nvme"
nvme0
nvme0n1
nvme0n1p1
nvme0n1p2
nvme0n1p3

Или использовать lspci, чтобы узнать что же такое подключено к машине:

root@thinkpad-e14:~$ lspci | grep -E "NVMe|Non-Volatile"
07:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd Device a809

Достаточно использовать команду nvme list:

root@thinkpad-e14:~$ nvme list -o json
{
  "Devices" : [
    {
      "NameSpace" : 1,
      "DevicePath" : "/dev/nvme0n1",
      "Firmware" : "9L1QFXV7",
      "Index" : 0,
      "ModelNumber" : "SAMSUNG MZALQ256HAJD-000L1",
      "ProductName" : "Non-Volatile memory controller: Samsung Electronics Co Ltd Device 0xa809",
      "SerialNumber" : "00000000000000",
      "UsedBytes" : 38470483968,
      "MaximumLBA" : 500118192,
      "PhysicalSize" : 256060514304,
      "SectorSize" : 512
    }
  ]
}

В качестве примера я вывел информацию в формате JSON. Как вы можете наблюдать, здесь выводится не просто список устройств, а еще и различная информация о них. Думаю, что часть атрибутов (например, DevicePath или ModelNumber) в комментариях не нуждаются, поэтому обращаю внимание только на некоторые:

  • Index — номер контроллера;
  • UsedBytes — используемый объем пространства в байтах;
  • PhysicalSize — максимальный объем пространства в байтах;
  • SectorSize — формат LBA, или логического блока, — минимального адресуемого блока данных;
  • MaximumLBA — максимальное количество логических блоков.

nvme id-ctrl, nvme id-ns


Ранее в статье, чтобы получить метаданные об устройстве, я отправлял контроллеру команду Identify. Для этого я использовал команду nvme id-ctrl для идентификации контроллера:

root@thinkpad-e14:~$ nvme id-ctrl /dev/nvme0

И nvme id-ns для идентификации пространства:
root@thinkpad-e14:~$ nvme id-ns /dev/nvme0n1

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

nvme create-ns, nvme delete-ns


Создание пространств имен проходит в несколько этапов. Сначала его нужно сформировать. Для этого используется команда nvme create-ns:

root@thinkpad-e14:~$ nvme create-ns /dev/nvme0 --nsze 1875385008 --ncap 1875385008 --flbas 0 --nmic 1 --dps 0
create-ns: Success, created nsid:1

Аргументы, которые указываются этой команде, вам уже знакомы. Мы их рассмотрели в разделе «Параметры NVMe-пространств».

Для удаления пространства используется команда nvme delete-ns:

root@thinkpad-e14:~$ nvme delete-ns /dev/nvme0n1          
delete-ns: Success, deleted nsid:1

nvme attach-ns, nvme detach-ns


Второй этап создания NVMe-пространств — привязка к контроллеру сформированного пространства. Для этого необходимо использовать команду nvme attach-ns:

root@thinkpad-e14:~$ nvme attach-ns /dev/nvme0 --namespace-id 1 --controllers 1
attach-ns: Success, nsid:1

Данной командой мы привязываем пространство с идентификатором 1 к контроллеру /dev/nvme0. Также обратите внимание на аргумент --controllers. Здесь перечисляются идентификаторы NVMe-контроллеров, к которым можно привязать пространство. Этот аргумент опционален и используется при создании публичных пространств.

По какой-то причине нумерация контроллеров начинается с 1, то есть контроллер /dev/nvme0 имеет идентификатор 1, который и указывается в аргументе --controllers. Надеюсь, это поможет вам избежать потерю времени на изучении следующей ошибки:

root@thinkpad-e14:~$ nvme attach-ns /dev/nvme0 --namespace-id 1 --controllers 0                            
NVMe Status:CONTROLLER_LIST_INVALID: The controller list provided is invalid(211c)

Чтобы отвязать пространство, используется команда nvme detach-ns:

root@thinkpad-e14:~$ nvme detach-ns /dev/nvme0n1 --namespace-id 1 --controllers 1
detach-ns: Success, nsid:1

После этого пространство исчезает из списка блочных устройств и его становится невозможно использовать. В аргументе --controllers также необходимо указывать только те контроллеры, от которых вы хотите отвязать пространство.

nvme reset


После привязки пространства к контроллеру, как правило, его можно использовать для работы. Однако случается так, что контроллер не может увидеть пространства. В таком случае его необходимо перезапустить — использовать команду nvme reset.

nvme format


В случае если необходимо изменить формат LBA у пространства, на помощь приходит команда nvme format:

root@thinkpad-e14:~$ nvme format /dev/nvme0n1 --lbaf 0
Success formatting namespace:1

Аргумент --lbaf указывает на формат LBA.

Однако эту команду также можно использовать и для безопасной затирки данных на NVMe-накопителе:

root@thinkpad-e14:~$ nvme format /dev/nvme0n1 --ses 1 -r
Success formatting namespace:1

Аргумент --ses указывает на уровень затирки:

  • 1 — удалить все данные;
  • 2 — удалить зашифрованные данные.

Аргумент -r указывает на то, что контроллер будет перезапущен после безопасной затирки.

Применение


Возможных вариантов использования пространств много. В основном они используются для увеличения производительности, резервирования и применяются в СХД, однако есть и более приземленные варианты использования.

Spare Area


Начнем с довольно обычной практикой использования. Spare Area, или Резервная область, была придумана еще до NVMe. Это специальное пространство на твердотельном накопителе, которое используется самим контроллером для внутренних операций и недоступно хосту.

Изменяя размер пространств, мы можем изменить и размер резервной области. Дело в том, что весь объем диска равен сумме объемов всех пространств и объема резервной области:


Соответственно, если мы уменьшим общий объем пространств, то оставшийся объем уйдет в пользу резервной области.

Если вы хотите узнать больше про резервную область, можете обратиться к этой статье.

Шифрование и изоляция



NVMe-диски поддерживают самошифрование по спецификации OPAL. Более того, для каждого пространства имен используются различные ключи шифрования.

Также контроллер предоставляет возможность защиты от записи. Существует три уровня:

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


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

Multiple Using


Как упоминалось ранее, пространства являются разделами на уровне контроллера, которые видны конечному хосту как отдельное устройство. А можно ли разделить NVMe-накопитель большого объема не несколько приватных пространств, каждое из которых затем выделить разным хостам? Можно! А с использованием сетевого протокола NVMe-oF (NVMe Over Fabrics) выделить эти пространства можно не только виртуальным хостам, но и физическим.

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


NVMe-контроллер позаботится о том, чтобы пространства были изолированы друга от друга: данные будут храниться в отделенных друг от друга областях накопителя, у каждого хоста будет собственная очередь для ввода-вывода. Тем не менее, spare area, или резервные области, по-прежнему будут общими.

Namespace Multi-path и Namespace Sharing


Namespace Sharing, или Public Namespaces, подразумевает под собой возможность совместного доступа одного или нескольких хостов к пространству через два и более контроллеров.


Для чего это необходимо? На рисунке показана схема использования публичных пространств. Да, интересно: мы можем обратиться к пространству NS B через контроллер NVMe Controller 1 и NVMe Controller 2. Но я не вижу в этом какой-то полезности… пока схема не будет выглядеть следующим образом:


Здесь мы видим, что контроллеры находятся на совершенно разных хостах и у нас есть несколько независимых путей к данным: через хост-контроллеры Host A (синие контроллеры) и Host B (фиолетовые контроллеры). Теперь это можно использовать для резервирования или для наращивания производительности: если синий путь будет сильно нагружен, то мы пойдем по фиолетовому.

Такой подход позволяет организовать высокопроизводительные и высоконадежные гибкие программно-определяемые СХД из обычных серверных платформ с использованием NVMe-oF.

Итог


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

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

Послесловие


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

  • Диски Samsung 970 EVO/970 EVO Plus с прошивками 2b2qexe7/2b2qexm7 не реализуют команды reset и format;
  • Диски Samsung 970 EVO/970 EVO Plus с прошивками 2b2qexe7/2b2qexm7 не реализуют управление пространствами при помощи команд create-ns, дelete-ns, detach-ns, atach-ns;
  • Диски Samsung PM991 с прошивкой 9L1QFXV7 имеют баг, из-за которого перезагрузка контроллера при помощи команды reset приводит к ошибке;
  • Диски Samsung PM991 с прошивкой 9L1QFXV7 имеют баг, из-за которого форматирование пространства при помощи команды format приводит к ошибке;
  • Диски Samsung PM991 с прошивкой 9L1QFXV7 не реализуют управление пространствами при помощи команд create-ns, delete-ns, detach-ns, atach-ns.

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

Полезные источники


  1. SSD: устройство, компоненты и принципы работы
  2. A Quick Tour of NVM Express
  3. NVMe 1.4 Specification
  4. NVMe Namespaces
  5. Base NVM Express — Part One
  6. NVMe Command Line Interface (NVMe-CLI)
  7. NVMe Over Fabrics
  8. Также по теме

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

А вы используете NVMe Namespaces на своих серверах?

  • 20,0%Да1
  • 40,0%Нет2
  • 40,0%Не использую NVMe2
Источник: https://habr.com/ru/company/selectel/blog/545078/


Интересные статьи

Интересные статьи

Из этой статьи вы узнаете, какие преимущества предоставляет контроль контракта API, что можно и нужно контролировать и как организовать процедуры контроля для исключения негативного влиян...
В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...
Современный веб практически немыслим без медиаконтента: смартфоны есть практически у каждой нашей бабушки, все сидят в соцсетях, и простои в обслуживании дорого обходятся компаниям. Вашему вн...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...
Некоторое время назад мне довелось пройти больше десятка собеседований на позицию php-программиста (битрикс). К удивлению, требования в различных организациях отличаются совсем незначительно и...