uuid что это в ноутбуке
Двигаем биты — или как реализовать свой стандарт UUID
Я работаю над открытой реализацией предложенного стандарта идентификаторов UUIDv7. На данный момент спецификация существует в виде IETF черновика. Черновик уже пережил два переиздания, и мы постоянно обновляем спецификации. Но сам документ — это дело простое. Для того чтобы кто-то мог воспользоваться новыми UUIDv7, нам надо написать как можно больше открытых имплементаций на различных языках.
Мне выпала стезя писать клиент на Golang. И всё бы было достаточно просто, если бы не сам стандарт. Для создания UUIDv7 вам нужно будет постоянно двигать различные биты в разных направлениях.
В этой статье я расскажу, с чем столкнулся, помогая с разработкой на golang.
Работа над подобным проектом — эта та ещё веселуха. Черновик стандарта находится в разработке, и после того как мы пробуем сделать что-то одно, идём, пробуем и разбираемся, что же получилось в итоге.
Во-первых, если вы посмотрите на обсуждение в github, то читать придётся очень много. Каждый из участников пытается спокойно обосновать свою точку зрения, это спокойствие обычно заключается в том, что любой комментарий занимает от двух до пяти страниц текста. Все эти комментарии сопровождаются вдумчивым вступлением и очень осторожным объяснением того, почему автор предыдущего комментария — дурак.
Во-вторых, надо быть готовым воевать. Над стандартом сейчас активно работают три человека. Я подключился к процессу имплементации. И всё бы хорошо… Открываешь документ, начинаешь писать код, аккуратно выравниваешь все значения в памяти, коммитишь код, идёшь, проверяешь комментарии… И впадаешь в ступор. Там уже дерутся по поводу того, сколько бит должна занимать та-самая-фича, которую ты только что писал. Тяжело вздыхаешь и идёшь переписывать уже существующий код.
Иногда, после того как я писал код, ко мне приходили и спрашивали моего мнения. «Удобно ли тебе писалось?». Я наивно отвечал: «Да не очень, но ничего, написалось». В таком случае мне радостно отвечали: «Отлично! Мы пересмотрим стандарт в таком случае, чтобы сделать его проще!». Фейспалм.
Посему основными требованиями для проекта были:
Для начала быстрое введение в тему UUIDv7
Вместо генерирования случайных значений в идентификаторах — мы будем зашивать дату и время в сам идентификатор. Разница между UUIDv4 и v7 заключается в том, что последний является бинарно-сортируемым.
Огромное количество движков баз данных позволяет хранить UUID как первичный ключ в таблице. При создании новых записей в распределённых системах создание уникальных идентификаторов — это больная тема. Наш новый стандарт создаёт записи, которые можно отсортировать бинарно. При этом мы можем существенно ускорить и упростить сортировку значений в базах данных. Более того, это положительно отразится на индексах.
Итак, давайте посмотрим на то, с чем мы работаем:
Вот наш герой. Первые 36 бит занимает unix-timestamp. Само по себе это уже гарантирует бинарную совместимость. Мы решили сократить 64 бита ts до 36 бит. При этом мы сможем сохранять даты до 4147 года. Оставшиеся биты нам понадобятся для хранения субсекундной точности. В UUID нам важно сохранить как можно больше энтропии. Первые 28 бит полного unix-ts хранят в себе нули на следующие два тысячелетия, посему мы их отсеиваем.
Далее у нас есть два замечательных прикола, называющиеся «обратной совместимостью». Нам надо хранить значения полей ver и var для совместимости с UUIDv4.
Итак, как вы видите, ничего не равняется ни по каким границам байтов. Более того, значение в поле subsec разбито на две части полем ver. Жизнь была бы намного проще, если бы мы могли просто выровнять всё по байтам и сложить их вместе. Но увы. Придётся выкручиваться.
Давайте посмотрим, что же такое UUID?
Какую бы версию UUID мы ни реализовывали, в конечном счёте нам надо выдать 16 байт. А так как нам нужно что-то, что очень легко портируется, переносится и адаптируется, то было бы глупо хранить UUIDv7 в любом другом формате, кроме как массив байт.
В файле uuid_base.go мы описываем этот массив и создаём простые форматтеры, позволяющие выводить сам идентификатор в нескольких широко используемых форматах.
Разврат начинается, когда мы добираемся до самой процедуры генерации идентификатора. В файле uuid_v7.go вы можете найти функцию Next, которая, собственно говоря, и генерирует идентификатор. В текущей реализации пользователь может установить определённые параметры генерации, поэтому сами поля заполняются по-разному.
Если вы посмотрите на эту логику, то увидите, что нам постоянно нужно выставлять определённые биты в определённых позициях в этом массиве данных.
Мы могли создать массив из 128 бит в памяти, но это было бы ужасно расточительно и требовало дополнительной конвертации данных из массива в 128 бит в 16-ти байтовый массив. Вместо этого мы будем писать в память напрямую.
Для этого мы создадим побитовый индексатор, который позволяет получать определённый бит из массива напрямую:
Так как в будущем этот код будет использоваться для генерации UUID версий 6, 7 и 8, то мы пытаемся повторно использовать базовый код. Посему, так как мы не можем напрямую приводить тип UUIDv7 к массиву бит, нам придётся создать функцию, которая будет обращаться к аналогичной функции базового класса.
А после добро пожаловать в мир очень простой магии.
Преобразуем позицию текущего бита в позицию байта путём деления на 8 без остатка, после чего берём остаток, который по факту является позицией бита в байте.
Создаём байт, равный единице. В бинарном представлении у нас получается 0000 0001. Сдвигаем выставленный бит в позицию, которую нам надо получить, и делаем OR. Сравниваем результат с нулём, и у нас в руках нужный бит.
Описанные выше операции тривиальны и не занимают много времени на процессоре, экономя память.
Для выставления бита возьмём за базу тот же код:
Для того чтобы выставить бит, мы опять создаём байт с одним битом и сдвигаем выставленный бит налево, после чего делаем побитовое OR, что гарантирует результат с поднятым битом.
Для того чтобы этот бит опустить, мы делаем побитовое AND с NOT нашего числа.
Итого: мы можем без особых проблем манипулировать битами в памяти напрямую.
Осталось только создать пару вспомогательных функций, которые сделают жизнь легче.
Для начала в данном проекте нам потребуется пара индексаторов:
По факту длина нашего UUID равна 123 битам, а не 128. Пять бит уходят на поля var и ver. Поэтому, когда мы складываем биты вместе, нам нужно знать их «относительную позицию» от начала нашего идентификатора.
В дополнение, так как нам нужно складывать последовательности битов, давайте напишем функцию, которая позволяет добавить последовательность битов к идентификатору и возвращает положение курсора для последующей записи данных в массив.
Так как мы пользуемся нашими функциями для индексирования, то поля var и ver останутся незаполненными. Их надо будет забить статическими значениями после того, как генерация была завершена.
Со всем вышеописанным работа с битами в байтах напрямую становится проще.
Мы просто генерируем куски нашего идентификатора и собираем их в памяти с помощью одной функции.
Эта функция получает и возвращает значение u.currentPosition, которое позволяет следить за тем, куда мы пишем в данный момент.
Страшная работа по написанию битов в байты выглядит не так уж страшно.
Берём всё, упаковываем, тестируем, создаём релиз, отдаём ребятам «на попробовать». И после сидим и ждём, что будет дальше, и что мы будем менять. Так как переписывать придётся всё, то надо перестать париться о том, что какой-то код можно будет спасти, или о том, что какой-то код можно будет использовать ещё раз.
При этом мы используем 16 байт памяти и не двигаем наш конечный массив туда-сюда по памяти. Экономим память один к шестидесяти четырём, так как массив из 128 бит хранит каждый бит в одном байте из-за выравнивания, и конечный объём памяти на хранение одного идентификатора (128*8) будет равняться одному килобайту, что расточительно для того, что занимает 16 байт.
Весь код доступен по лицензии MIT, посему, если вам приходится работать с битами в golang, вы запросто можете копировать функции из bitsetter.go. Этот код может работать с массивами произвольной длины.
Единственным ограничением будет потенциальное переполнение счётчика битов. Но я очень надеюсь, что вам не придётся писать код для ворочанья битов в гигабайтах информации.
НЛО прилетело и оставило здесь промокоды для читателей нашего блога:
Как генерируются UUID
Вы наверняка уже использовали в своих проектах UUID и полагали, что они уникальны. Давайте рассмотрим основные аспекты реализации и разберёмся, почему UUID практически уникальны, поскольку существует мизерная возможность возникновения одинаковых значений.
Современную реализацию UUID можно проследить до RFC 4122, в котором описано пять разных подходов к генерированию этих идентификаторов. Мы рассмотрим каждый из них и пройдёмся по реализации версии 1 и версии 4.
Теория
UUID (universally unique IDentifier) — это 128-битное число, которое в разработке ПО используется в качестве уникального идентификатора элементов. Его классическое текстовое представление является серией из 32 шестнадцатеричных символов, разделённых дефисами на пять групп по схеме 8-4-4-4-12.
Информация о реализации UUID встроена в эту, казалось бы, случайную последовательность символов:
Значения на позициях M и N определяют соответственно версию и вариант UUID.
Версия
Номер версии определяется четырьмя старшими битами на позиции М. На сегодняшний день существуют такие версии:
Вариант
Это поле определяет шаблон информации, встроенной в UUID. Интерпретация всех остальных битов в UUID зависит от значения варианта.
Мы определяем его по первым 1-3 старшим битам на позиции N.
1 0 0 0 = 8
1 0 0 1 = 9
1 0 1 0 = A
1 0 1 1 = B
Так что если вы видите UUID с такими значениями на позиции N, то это идентификатор в варианте 1.
Версия 1 (время + уникальный или случайный идентификатор хоста)
В этом случае UUID генерируется так: к текущему времени добавляется какое-то идентифицирующее свойство устройства, которое генерирует UUID, чаще всего это MAC-адрес (также известный как ID узла).
Идентификатор получают с помощью конкатенации 48-битного МАС-адреса, 60-битной временной метки, 14-битной «уникализированной» тактовой последовательности, а также 6 битов, зарезервированных под версию и вариант UUID.
Тактовая последовательность — это просто значение, инкрементируемое при каждом изменении часов.
Временная метка, которая используется в этой версии, представляет собой количество 100-наносекундных интервалов с 15 октября 1582 года — даты возникновения григорианского календаря.
Возможно, вы знакомы с принятым в Unix-системах исчислением времени с начала эпохи. Это просто другая разновидность Нулевого дня. В сети есть сервисы, которые помогут вам преобразовать одно временное представление в другое, так что не будем на этом останавливаться.
Хотя эта реализация выглядит достаточно простой и надёжной, однако использование MAC-адреса машины, на которой генерируется идентификатор, не позволяет считать этот метод универсальным. Особенно, когда главным критерием является безопасность. Поэтому в некоторых реализациях вместо идентификатора узла используется 6 случайных байтов, взятых из криптографически защищённого генератора случайных чисел.
Сборка UUID версии 1 происходит так:
Поскольку эта реализация зависит от часов, нам нужно обрабатывать пограничные ситуации. Во-первых, для минимизации коррелирования между системами по умолчанию тактовая последовательность берётся как случайное число — так делается лишь один раз за весь жизненный цикл системы. Это даёт нам дополнительное преимущество: поддержку идентификаторов узлов, которые можно переносить между системами, поскольку начальное значение тактовой последовательности совершенно не зависит от идентификатора узла.
Помните, что главная цель использования тактовой последовательности — внести долю случайности в наше уравнение. Биты тактовой последовательности помогают расширить временную метку и учитывать ситуации, когда несколько UUID генерируются ещё до того, как изменяются процессорные часы. Так мы избегаем создания одинаковых идентификаторов, когда часы переводятся назад (устройство выключено) или меняется идентификатор узла. Если часы переведены назад, или могли быть переведены назад (например, пока система была отключена), и UUID-генератор не может убедиться, что идентификаторы сгенерированы с более поздними временными метками по сравнению с заданным значением часов, тогда нужно изменить тактовую последовательность. Если нам известно её предыдущее значение, его можно просто увеличить; в противном случае его нужно задать случайным образом или с помощью высококачественного ГПСЧ.
Версия 2 (безопасность распределённой вычислительной среды)
Главное отличие этой версии от предыдущей в том, что вместо «случайности» в виде младших битов тактовой последовательности здесь используется идентификатор, характерный для системы. Часто это просто идентификатор текущего пользователя. Версия 2 используется реже, она очень мало отличается от версии 1, так что идём дальше.
Версия 3 (имя + MD5-хэш)
Если нужны уникальные идентификаторы для информации, связанной с именами или наименованием, то для этого обычно используют UUID версии 3 или версии 5.
Они кодируют любые «именуемые» сущности (сайты, DNS, простой текст и т.д.) в UUID-значение. Самое важное — для одного и того же namespace или текста будет сгенерирован такой же UUID.
Обратите внимание, что namespace сам по себе является UUID.
В этой реализации UUID namespace преобразуется в строку байтов, конкатенированных с входным именем, затем хэшируется с помощью MD5, и получается 128 битов для UUID. Затем мы переписываем некоторые биты, чтобы точно воспроизвести информацию о версии и варианте, а остальное оставляем нетронутым.
Важно понимать, что ни namespace, ни входное имя не могут быть вычислены на основе UUID. Это необратимая операция. Единственное исключение — брутфорс, когда одно из значений (namespace или текст) уже известно атакующему.
При одних и тех же входных данных генерируемые UUID версий 3 и 5 будут детерминированными.
Версия 4 (ГПСЧ)
Самая простая реализация.
6 битов зарезервированы под версию и вариант, остаётся ещё 122 бита. В этой версии просто генерируется 128 случайных битов, а потом 6 из них заменяется данными о версии и варианте.
Такие UUID полностью зависят от качества ГПСЧ (генератора псевдослучайных чисел). Если его алгоритм слишком прост, или ему не хватает начальных значений, то вероятность повторения идентификаторов возрастает.
В современных языках чаще всего используются UUID версии 4.
Её реализация достаточно простая:
Версия 5 (имя + SHA-1-хэш)
Единственное отличие от версии 3 в том, что мы используем алгоритм хэширования SHA-1 вместо MD5. Эта версия предпочтительнее третьей (SHA-1 > MD5).
Практика
Одним из важных достоинств UUID является то, что их уникальность не зависит от центрального авторизующего органа или от координации между разными системами. Кто угодно может создать UUID с определённой уверенностью в том, что в обозримом будущем это значение больше никем не будет сгенерировано.
Это позволяет комбинировать в одной БД идентификаторы, созданные разными участниками, или перемещать идентификаторы между базами с ничтожной вероятностью коллизии.
UUID можно использовать в качестве первичных ключей в базах данных, в качестве уникальных имён загружаемых файлов, уникальных имён любых веб-источников. Для их генерирования вам не нужен центральный авторизующий орган. Но это обоюдоострое решение. Из-за отсутствия контролёра невозможно отслеживать сгенерированные UUID.
Есть и ещё несколько недостатков, которые нужно устранить. Неотъемлемая случайность повышает защищённость, однако она усложняет отладку. Кроме того, UUID может быть избыточным в некоторых ситуациях. Скажем, не имеет смысла использовать 128 битов для уникальной идентификации данных, общий размер которых меньше 128 битов.
Уникальность
Может показаться, что если у вас будет достаточно времени, то вы сможете повторить какое-то значение. Особенно в случае с версией 4. Но в реальности это не так. Если бы вы генерировали один миллиард UUID в секунду в течение 100 лет, то вероятность повторения одного из значений была бы около 50 %. Это с учётом того, что ГПСЧ обеспечивает достаточное количество энтропии (истинная случайность), иначе вероятность появления дубля будет выше. Более наглядный пример: если бы вы сгенерировали 10 триллионов UUID, то вероятность появления двух одинаковых значений равна 0,00000006 %.
А в случае с версией 1 часы обнулятся только в 3603 году. Так что если вы не планируете поддерживать работу своего сервиса ещё 1583 года, то вы в безопасности.
Впрочем, вероятность появления дубля остаётся, и в некоторых системах стараются это учитывать. Но в подавляющем большинстве случаев UUID можно считать полностью уникальными. Если вам нужно больше доказательств, вот простая визуализация вероятности коллизии на практике.
Uuid что это в ноутбуке
Принесли мне старичка Lenovo Thinkpad T400 подготовить перед продажей. К слову, мне всегда нравились старые ThinkPadы, от них веет каким-то величием и надежностью. Может это выветривающийся дух IBM, а может самовнушение, ну да черт с ним. После осмотра и чистки ноута я заметил отсутствие UUID (Universally Unique Identifier) – универсального идентификатора, используется для привязки софта и т.д.. Данная проблема довольно старая, темы на форумах начинаются где-то с 2006, но она актуальна и на новых девайсах.
На фото ниже можно увидеть что значение UUID забито нулями.
Возможные варианты слета или отсутствия серийных номеров и других идентификаторов:
*Замена материнской платы. Платы обычно идут с доноров ( со своими идентификаторами) или чистые, в последнем варианте информация меняется на данные прошлой материнки.
*Перезапись/ Замена Bios. Может происходить при обновлении биоса, замены прошивки с донора в которой данная область затерта и схожих действиях.
* Рандомные глюки софта/железа.
По идее, без серийников можно жить и параноики этому будут даже рады (хотя на леново можно и поинтересней чего зашить, тот же Libreboot, но об этом как-то в другой статье), но все зависит от железа. В некоторых случаях ничего не происходит, в других же возможны проблемы с привязкой софта, рандомными перезагрузками и т.д.
У этого экземпляра слет случился после обновления биоса с довольно старой версии.
Способ восстановления подходит при отсутствии UUID, серийника материнской платы и ноута или при их значении invalid.
Вы делаете все на свой страх и риск, я вас предупредил.
После загрузится красивое лого Thinkpad с варнингом о том, что воровать плохо и появится основное меню:
Нам интересны первый и четвертый пункт.Остальные не очень интересны, но это:
2)Проверка звука
3) Форматирование диска
5)Удаление рекавери раздела
6)Записи при обслуживании.
Выбрав первый вариант Set system identification, мы можем:
1)Добавить серийные номера ноутбука, материнки.
2)Отобразить существующие.Что и сделано на фото.
3)Удалить записанные в памяти.
После перезагрузки и захода в биос все отображается так как должно.
Немного любви с чисткой и ноут прослужит новому владельцу еще пяток лет. Это ведь ThinkPad.
Системный номер раздела диска UUID / GUID / serial number
На чистом диске нет никаких разделов и соответственно нет никаких номеров раздела.
В чем отличие UUID от GUID
UUID (Universally unique identifier «универсальный уникальный идентификатор») — UUID представляет собой 16-байтный (128-битный) номер. В каноническом представлении UUID изображают в виде числа в шестнадцатеричной системе счисления, разделённого дефисами на пять групп в формате 8-4-4-4-12.
GUID (Globally Unique Identifier) — это так называется у Microsoft — фактически это последняя реализация UUID (да, там были свои предыдущие версии и свой зоопарк).
Именно по этому актуальная разметка диска от Microsoft называется GPT (GUID Partition Table), читаем статью
В целом используется как идентификатор (в составе также закодирована дата и время создания):
Почему такая загадочная запись?
Очень удобно переводить двоичные числа в шестнадцатеричный формат (а в десятичный формат — очень неудобно).
Помним, что для половинки байта (4 бита):
Bin | Hex | Dec |
0000 | 0 | 0 |
0001 | 1 | 1 |
0010 | 2 | 2 |
0011 | 3 | 3 |
0100 | 4 | 4 |
0101 | 5 | 5 |
0110 | 6 | 6 |
0111 | 7 | 7 |
1000 | 8 | 8 |
1001 | 9 | 9 |
1010 | A | 10 |
1011 | B | 11 |
1100 | C | 12 |
1101 | D | 13 |
1110 | E | 14 |
1111 | F | 15 |
Т.е. один байт (8 бит) вида 11111111 легко представляется в виде FF = т.е. каждая половинка байта — это F (15 в десятичной системе).
Поэтому 128 бит легко превращаются в номер из 32 цифр в шестнадцатеричной системе счисления, 128/4 = 32
В номере UUID <8e44ac32-40e2-11ea-93a4-bff4e4da2abb> каждые два разряда фактически кодируют один байт.
Посмотрим на структуру номера
xxxxxxxx-xxxx-Mxxx—Nxxx-xxxxxxxxxxxx
4 бита M обозначают версию («version») UUID, а 1-3 старших бита N обозначают вариант («variant») UUID.
Первые две цифры кодируют дату и время создания.
Такое разделение на группы основано на структуре UUID:
Название поля | Длина (в байтах) | Длина (число 16-ричных цифр) | Содержимое |
---|---|---|---|
time_low | 4 | 8 | целое число, обозначающее младшие 32 бита времени |
time_mid | 2 | 4 | целое число, обозначающее средние 16 бит времени |
time_hi_and_version | 2 | 4 | 4 старших бита обозначают версию UUID, младшие биты обозначают старшие 12 бит времени |
clock_seq_hi_and_res clock_seq_low | 2 | 4 | 1-3 старших бита обозначают вариант UUID, остальные 13-15 бит обозначают clock sequence |
node | 6 | 12 | 48-битный идентификатор узла |
Как вытащить дату и время из GUID?
bdb62d89-cede-11e4-b12b-d4ae52b5e909
дата содержится в первых символах, bdb62d89-cede-11e4 которые нужно переставить задом наперед: 11e4-cede-bdb62d89
первый символ отбрасываем, убираем «лишние» знаки «-«(тире)
интервал в десятых долях микросекунд (HEX) получается равным: интервал 16= 1E4CEDEBDB62D89
переводим его в десятичный интервал интервал 10 = HexToDec(интервал 16);в результате получаем: интервал 10 = 136 461 344 788 852 105
находим интервал в секундах: интервал Сек = интервал 10 / 10 000 000;
Делаем сдвиг даты от 15.10.1582 г. + 13 646 134 478 + сдвиг на часовой пояс (Московское время) от «мирового времени» (GMT) = 20.03.2015 16:54:38
Использование UUID / GUID как номера раздела (тома) на диске
В LInux изначально используется UUID как системный номер раздела.
В Windows свой зоопарк.
Для FAT 32 — серийный номер из 4 байт = 8 символов в шестнадцатеричной системе
Для NTFS — серийный номер из 8 байт = 16 символов в шестнадцатеричной системе
Системный номер раздела записан непосредственно на диске — создается при форматировании диска. В серийном номер также закодирована дата и время создания раздела.
ВАЖНО: каждый диск «помнит» дату и время создания на нем конкретного раздела, это фактически записано в номере созданного раздела (при форматировании). Нужна шапочка из фольги…
Этот номер мы можем увидеть в свойствах раздела, который показывают программы для управления разделами.
Номер 4610e64f 10e64611 — 16 цифр в шестнадцатеричной системе
Правую половинку номера тома мы также можем увидеть через команду DIR в режиме командной строки
10e6-4611
Он используется Windows уже для регистрации (например раздела) — как устройства, подключенного к системе, вот на фото ниже (как это красиво называется — «точка монтирования» — Mount point).
Этот номер уже записан в недрах реестра — в отличии от серийного номера раздела, записанного в заголовке тома на диске.
Этот же номер мы можем увидеть в bcdedit — как номер основного диска С для работы системы
Видно, что номер GUID используется также для идентификации текущей операционной системы (т.е. в загрузчике явно указано, какую операционную систему нужно загружать и на каком диске она находится).
Вы можете сохранить ссылку на эту страницу себе на компьютер в виде htm файла
Вы будете видеть наш сайт у себя в ленте
Нажмите «Нравится» или напишите сообщение