Software timestamp что это
Datetime или timestamp
На днях я столкнулся с тем, что многие разработчики не знают в чём отличие типов данных DATETIME и TIMESTAMP в MySQLе, а так же как хранить дату и время, если необходимо учитывать разные часовые пояса для разных пользователей веб-приложения. Поэтому хочу дать ниже разъяснения с пояснениями.
DATETIME
Хранит время в виде целого числа вида YYYYMMDDHHMMSS, используя для этого 8 байтов. Это время не зависит от временной зоны. Оно всегда отображается при выборке точно так же, как было сохранено, независимо от того какой часовой пояс установлен в MySQL. Даю пример:
mysql> create table `dt1` ( col datetime NOT NULL );
mysql> SET @@session.time_zone=’+00:00′;
mysql> select now();
+———————+
| now() |
+———————+
| 2009-06-04 18:13:56 |
+———————+
mysql> insert into dt1 values(now());
mysql> insert into dt1 values(now());
TIMESTAMP
Хранит 4-байтное целое число, равное количеству секунд, прошедших с полуночи 1 января 1970 года по усреднённому времени Гринвича (т.е. нулевой часовой пояс, точка отсчёта часовых поясов). При получении из базы отображается с учётом часового пояса. Часовой пояс может быть задан в операционной системе, глобальных настройках MySQL или в конкретной сессии. Запомните, что сохраняется всегда количество секунд по UTC (универсальное координированное время, солнечное время на меридиане Гринвича), а не по локальному часовому поясу. Пример:
Ещё одно отличие! TIMESTAMP по умолчанию NOT NULL, а его значение по умолчанию равно NOW().
mysql> insert into dt1 values(null);
ERROR 1048 (23000): Column ‘col’ cannot be null
mysql> insert into tm1 values(null);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tm1;
+———————+
| col |
+———————+
| 2009-06-04 18:25:08 |
| 2009-06-04 18:25:26 |
| 2009-06-04 18:32:50 |
+———————+
Дополнение. Для тех, кого смущает использование функции NOW().
SO_TIMESTAMPING в картинках. Прием пакета
Бывает, что приложению требуется узнать точное время приема или отправки сетевого пакета. Например, для синхронизации часов (см. PTP, NTP) или тестирования задержек в сети (см. RFC2544).
Наивным решением будет запоминать в приложении время сразу после получения пакета от ядра (или перед отправкой ядру):
Ясно, что полученное таким образом время может заметно отличаться от момента, когда пакет был получен сетевым устройством. Для получения более точного времени нужна поддержка от операционной системы, драйвера и/или сетевого устройства.
Начиная с версии 2.6.30 Линукс поддерживает опцию сокета SO_TIMESTAMPING. Она позволяет пользовательскому сокету получать временные метки для отправляемых и принимаемых пакетов. Временные метки могут быть сняты самим ядром, драйвером или сетевым устройством (см. список поддерживающих устройств и драйверов). О том, что это вообще такое и как этим пользоваться, стоит почитать в Documentation/networking/timestamping.txt
В этой статье я расскажу о том, как пакеты доставляются от сетевого устройства пользователю, когда при этом снимаются временные метки, как они доставляются пользователю и насколько они точны. Приведенные примеры кода ядра взяты из версии 4.1.
Начальные знания
struct sk_buff
Самые интересные для нас поля struct skb_shared_info :
struct net_device и struct sock
Мы не станем подробно рассматривать эти структуры. Сейчас достаточно понять, что первая позволяет ядру общаться с устройством, а вторая — с пользовательским сокетом. Для принятого пакета: skb->dev указывает на устройство, которым он был получен; skb->sk на сокет, которому будет доставлен. Для отправляемого — наоборот.
SOFTIRQ
Когда процессор получает прерывание, он вызывает соответствующий ему обработчик. Выполнение обработчика происходит в контексте прерывания — для обслуживающего прерывание процессора почти все прерывания выключены. То есть обработчик прерывания не будет прерван, пока не завершится сам. Чем меньше процессор находится в контексте прерывания, тем скорее он сможет обслужить новые прерывания и отреагировать на события от других устройств.
Несмотря на то, что обработчику прерывания может требоваться много процессорного времени, обычно большая часть его работы может подождать. Именно поэтому действия обработчика прерывания принято разделять на верхнюю(Top Half) и нижнюю(Bottom Half) половины. Top Half в контексте прерывания выполняет срочные действия и планирует для выполнения Bottom Half. Bottom Half будет запущена ядром позже вне контекста прерывания и может быть прервана во время работы другими прерываниями.
SOFTIRQ — Механизм ядра, позволяющий запланировать отложенный вызов функции. Часто используется для реализации Bottom Half. Всего в ядре v4.1 десять разных SOFTIRQ (см. список), обработчики для которых определяются при компиляции ядра. Во время своего выполнения обработчик может быть прерван только аппаратным прерыванием. Для каждого процессора своя маска запланированных SOFTIRQ, т.е обработчик будет вызван на том же процессоре, с которого был запланирован. (На самом деле, можно ухитриться и указать на каком конкретно процессоре запланировать SOFTIRQ.) Одно и то же SOFTIRQ может быть запланировано и
выполнено независимо на двух разных процессорах. При отправке и получении пакетов используются два из них: NET_RX_SOFTIRQ с обработчиком net_rx_action и NET_TX_SOFTIRQ с обработчиком net_tx_action. (см. net_rx_action и net_tx_action)
Прием пакета
Для общения с сетевыми устройствами Линукс использует смесь прерываний и поллинга (polling). (см. NAPI)
Вот как это выглядит:
Top Half
poll_list * — список, создаваемый ядром для каждого ядра процессора(как одно из полей struct softnet_data ). Он хранит устройства, с которых NET_RX_SOFTIRQ предстоит получить пакеты.
Bottom Half
Эта функция проходит по списку poll_list и для каждого устройства dev, пока на нем есть пакеты, вызывает виртуальную функцию драйвера napi->poll (см. include/linux/netdevice.h.), которая:
Стоит отметить, что net_rx_action() позволяет обрабатывать не более netdev_budget (экспортируется в /proc/sys/net/core/netdev_budget ) пакетов за раз и ограничивает время своего выполнения 2/HZ секундами(на x86 по умолчанию HZ = 1000, т.е ограничение времени = 2мс).
netif_receive_skb()
netif_receive_skb() — это функция, начиная с которой пакет попадает из драйвера в ядро. (На самом деле, она просто служит оберткой для других функций, которые и выполняют всю работу.) Посмотрим, что же делает ядро с полученным skb:
Обычно пакет обрабатывается тем процессором, на котором был запланирован SOFTIRQ, а это тот процессор, на который пришло прерывание. Некоторые сетевые карты шлют только одно прерывание только одному процессору, не позволяя распараллелить обработку пакетов на многопроцессорных системах. Для решения этой проблемы был придуман Receive Packet Steering (RPS).
__netif_receive_skb_core()
Прежде всего эта функция снимает временную метку, если она не была снята в netif_receive_skb_internal() :
Теперь мы имеем skb, который содержит:
Остается доставить его всем желающим получателям.
В зависимости от требуемого L3 протокола и устройства получатели могут регистрироваться в нескольких местах:
Частые пользователи всех 4-х списков — AF_PACKET сокеты, каждый из них при системном вызове bind регистрирует в соответствующем списке обработчик. (Обработчика зовут packet_rcv ) На самом деле есть еще куча получателей, но мы ограничимся только теми, которые доставляют пакет сокетам в пространстве пользователя.
Важное различие: ip_recv зарегистрирована как обработчик всего один раз в ptype_base сколько бы AF_INET сокетов вы не создали, а packet_rcv регистрируется для каждого AF_PACKET сокета по разу в каком-нибудь из списков.
Тестирование Задержек
Мы видели, что для принимаемых пакетов дважды снимаются временные метки: сетевой картой(Thard), ядром сразу при получении (Tsoft). Подводя итог, посмотрим чем вызываются задержки между ними и моментом доставки пакета пользователю(Tuser):
Если за один вызов NET_RX_SOFTIRQ не удалось обработать наш пакет(был превышен netdev_budget или ограничение по времени 2/HZ), то ядро позволит выполняться другим процессам, пока аппаратное прерывание или ksoftirqd снова не вызовут `do_softirq()`. Также не стоит забывать о том, что есть и другие SOFTIRQ, на выполнение которых тоже уходит время.
В зависимости от того, что это за сокеты, доставка может занимать разное время.
Чтобы примерно оценить, насколько велики эти задержки и насколько они предсказуемы, воспользуемся программой rxtest. Эта программа:
Проверка проводилась на Core i7 с Linux 4.0 с сетевой картой Intel 82599ES под управлением драйвера ixgbe.
В моем случае сетевая карта имеет свои аппаратные часы и снимает временные метки по ним. И нет никакой гарантии, что эти часы как-то синхронизированы с jiffies ядра. Для того, чтобы это исправить, запустим программу phc2sys из linuxptp:
Её нужно держать открытой на протяжении всей проверки. Она будет заниматься подстройкой часов сетевой карты под системное время и выводить текущее расхождение часов. В моем случае абсолютное значение расхождения не превышало 10нс.
Это приведет к тому, что сетевая карта будет снимать временные метки для всех пакетов типа Sync протокола PTP. Почему именно Sync PTP? Потому что наша сетевая карта не умеет снимать временные метки для всех пакетов. Ей обязательно нужно указать какой-нибудь тип пакетов протокола PTP. (Это свзязано с тем, что поддержка аппаратных временных меток в Linux была введена для работы протокола PTP, позволяющего синхронизировать время с точностью до наносекунд.)
Тем временем шлем с другого конца кабеля по 10 PTP Sync пакетов в секунду. (Я брал примеры PTP пакетов с https://wiki.wireshark.org/Protocols/ptp и слал их с помощью tcpreplay.)
Результат выполнения rxtest:
При этом тестируемому сетевому интерфейсу не был присвоен ip-адрес и наши пакеты не попадали на обработку протоколом IP. Этим можно объяснить совсем небольшую задержку Tuser — Tsoft.
Адекватного исследования задержек и их зависимости от разных параметров (netdev_budget, частота принимаемых пакетов, нагрузка на процессор, конфигурация ядра, количество и тип открытых сокетов) хватило бы на целую статью. Цель этой статьи — полить воду рассмотреть механизм доставки пакетов и то, чем вызываются задержки.
На этом все. Буду рад любым отзывам и критике.
Timestamp
Timestamp может ссылаться на time code или digitally signed timestamp, который предназначен для подтверждения существования определённого документа в определённое время, как часть электронной подписи.
Timestamp очень полезен для журналирования событий.
Многие источники также используют термин timestamp, имея в виду POSIX-время, количество секунд прошедшее с 00:00:00 UTC 1 января, 1970 года.
Содержание
История
Идея использования временно́й печати (перевод автора «timestamping») информации актуальна довольно давно. Например, когда Роберт Гук открыл свой закон в 1660 году, он не хотел его публиковать, но хотел иметь право на авторство. Поэтому он сначала выпустил анаграмму ceiiinosssttuv и позднее опубликовал перевод ut tensio sic vis (лат: упругость, как сила). Похожая ситуация случилась с Галилеем, в его исследованиях фаз Венеры сперва была опубликована анаграмма. Современный пример, современной исследовательской организации может позднее понадобиться доказать, что их идея была разработана до определённой даты. Один из способов решения — перенести всё на компьютер и записать в лабораторную тетрадь зашифрованный ключ целостности данных. В дальнейшем, для проверки, что файл в хранилище не изменялся, вам надо будет пересчитать зашифрованный ключ и сравнить его с ключом в лабораторной тетради.
Доверие электронной отметке времени
Электронная отметка — это способ достоверно следить за временем создания и модификации документа. «Достоверно» здесь значит, что никто, даже владелец этого документа, не в состоянии изменить однажды созданную информацию так, чтоб её целостность не нарушилась. Административная сторона включает прозрачную сборку управления отметками времени, их создание и обновление.
Защищённая отметка времени — это отметка, выданная при свидетелях. Trusted third party (TTP) ведёт себя как timestamping authority (TSA). Это используется для подтверждения существования определённых данных до определённого момента времени (контракты, данные исследования, медицинские записи и т. п.) без возможности дописывания задним числом. Сложные TSA могут использоваться для повышения надёжности и уменьшения уязвимости.
Создание временной метки
Эта техника основана на цифровых подписях и хеш-функциях. Сначала хеш вычисляется из данных. Хеш — своего рода цифровая контрольная сумма файла оригинальных данных: другая строка битов для установленных данных. Если оригинальные данные были изменены, то это будет результат полностью в другом хеше. Этот хеш посылается TSA, TSA генерирует timestamp для хеша и вычисляет хеш этого объединения. Этот хеш, например, может быть подписан в цифровой форме с приватным ключом TSA. Этот подписанный хеш и timestamp возвращаются на подписанную сторону timestamp, который хранит их с оригинальными данными (см. диаграмму).
Впоследствии оригинальные данные не могут быть вычислены из хеша (поскольку хеш-функция является функцией в одну сторону (необратимой)), TSA никогда не видит оригинальные данные, которые допускается использовать в этом методе для конфиденциальных данных.
Проверка временной метки
Все, кто доверяет создателю временной метки (TSA), могут убедиться, что документ уже существовал на момент времени, который был представлен создателем. Также является неопровержимым тот факт, что оригинальные данные принадлежали лицу, запросившему электронную отметку времени, именно в момент создания этой электронной отметки. Для доказательства этого (см. диаграмму) вычисляется хеш оригинальных данных, к нему добавляется timestamp, полученный от TSA, и вычисляется хеш этого объединения, назовём его хешем A.
Затем проверяется цифровая подпись TSA путём дешифрования подписанного хеша, полученного от TSA, с помощью открытого ключа TSA. В результате получается дешифрованный хеш, который назовём хешем B. Если хеш A идентичен хешу B, значит, электронная отметка времени не подвергалась изменениям и была выпущена TSA. Если хеши не совпадают, можно утверждать, что либо электронная отметка времени была изменена, либо она не была выпущена TSA.
Заблуждения программистов о Unix-времени
Вчера Дэнни поинтересовался любопытными фактами о Unix-времени, а я вспомнил, что иногда оно работает совершенно неинтуитивно.
Вот эти три факта кажутся в высшей степени разумными и логичными, не так ли?
Настольные часы 1770-х годов. Собрано Джоном Леру. Из коллекции Wellcome. Опубликовано под лицензией CC BY
У всех трёх заблуждений одна причина: високосные секунды. Если вы не знакомы с дополнительными секундами, вот краткая справка:
Время UTC определяется двумя факторами:
Когда два времени выпадают из синхрона, в UTC добавляется или удаляется секунда, чтобы вернуть синхронизацию. С 1972 года служба IERS (которая управляет этим делом) добавила 27 дополнительных секунд. В результате получилось 27 суток UTC продолжительностью в 86 401 секунду. Теоретически возможно появление суток продолжительностью 86 399 секунд (минус одна). Оба варианта противоречат фундаментальному предположению о Unix-времени.
Время Unix предполагает, что каждый день длится ровно 86 400 секунд (60 × 60 × 24 = 86 400), без всяких дополнительных секунд. Если происходит такой скачок, то время Unix либо перепрыгивает через секунду, либо отсчитывая две секунды за одну. По состоянию на 2019 год в нём отсутствует 27 високосных секунд.
Так что наши заблуждения нужно дополнить следующим образом:
До сих пор на практике секунды никогда не удалялись (и замедление вращения Земли означает, что это маловероятно), но если бы это когда-либо произошло, это означало бы, что день UTC стал на одну секунду короче. В этом случае последняя секунда UTC (23:59:59) отбрасывается.
В каждых сутках Unix одинаковое количество секунд, поэтому последняя Unix-секунда укороченного дня не будет соответствовать никакому времени UTC. Вот как это выглядит, в интервалах по четверти секунды:
Это уже 27 раз произошло на практике. По окончании суток UTC добавляют дополнительную секунду 23:59:60. В сутках Unix одинаковое количество секунд, поэтому он не может добавить дополнительную секунду — вместо этого приходится повторять метки времени Unix для последней секунды. Вот как это выглядит, в интервалах по четверти секунды:
Если стартовать в 23:59:60.50 и подождать полсекунды, время Unix возвращается на полсекунды, а метка времени Unix 101 соответствует двум секундам UTC.
Вероятно, это не единственные странности времени Unix — только то, что я вчера вспомнил.
Виртуальное время. Часть 1: источники времени в компьютере
Человек, имеющий одни часы, твердо знает, который час. Человек, имеющий несколько часов, ни в чём не уверен.
Закон Сегала
Требования на источники времени
Конечно же, достаточный набор свойств источника зависит от способа использования в программах. Например, одно устройство может предоставлять низкое разрешение и высокую длительность считывания, но при этом быть энергонезависимым и очень стабильным, а другое позволять измерять очень короткие промежутки времени, но при этом быстро переполняться, да ещё и не быть синхронизированным ни с чем больше.
Обзор таймеров в архитектуре PC
Источников времени в системе может быть несколько. Прикладные программы редко обращаются к каким-либо из них напрямую. Вместо этого используются всевозможные API, предлагаемые использованным языком программирования (например, C++11 < chrono >), средой исполнения (например, gettimeofday из POSIX или QueryPerformanceCounter на MS Windows), или даже системными вызовами используемой операционной системы.
Самой ОС также необходимо знать время и уметь отмерять его отрезки для планирования работы пользовательских потоков, учёта потреблённых ими ресурсов, профилировки производительности, управления энергопотреблением и т.п. При этом сама ОС работает напрямую с интерфейсами, предоставляемыми аппаратурой. Так как таймеров присутствует много, современные ОС умеют выбирать один «центрально» используемый в начале загрузки, исходя из своих представлений о «качестве» обнаруженных устройств (например, на некоторых системах часть таймеров может быть занесена в «чёрный список» из-за известных проблем в работе) или же настроек пользователя (параметр clocksource у ядра Linux и опции useplatformclock, tscsyncpolicy, disabledynamictick у BCDEDIT в Windows).
Опишу наиболее часто встречаемые устройства, являющиеся часами и таймерами в PC.
Общераспространённые
Часы реального времени (Real Time Clock, RTC) — источник текущей даты и времени для нужд ОС. Типичное разрешение этого таймера — одна секунда. Все системы, удовлетворяющие стандарту ACPI, имеют чип RTC, совместимый с Motorola MC146818, присутствовавшем в оригинальном IBM PC/AT с 1984 года. В современных системах RTC обычно интегрирован в набор системной логики южного моста на материнской плате (что означает довольно большую задержку при чтении). Энергонезависимость этого таймера обеспечивается специальной батарейкой. Принципы программирования RTC вызывают ностальгию по BCD-числам и проблеме Y2K.
Programmable Interval Timer (PIT) 8253 или 8254 от Intel — стандартный счётчик и таймер, имеющийся в PC с самого начала существования этой платформы (1981 год). Как и RTC, изначально был отдельной микросхемой, а ныне является частью системной логики. Довольно интересное устройство, содержащее три таймера (хотя последние два всегда были зарезервированы под задачи обновления ОЗУ и работу PC-спикера соответственно) и позволяющее запрограммировать их в различные режимы: периодические прерывания, однократное (one-shot) прерывание по таймауту, меандр и т.д.
Первый канал PIT до сих пор может использоваться ОС как источник прерываний для работы вытесняющего планировщика задач. Однако по современным меркам он не очень удобен в работе: низкая частота осциллятора 1193181,8 Гц (странное значение — это историческое наследие от частоты развёртки NTSC), ширина счётчика всего 16 бит (частое переполнение) при ширине регистров статуса и команд всего в восемь бит (т.е. приходится передавать или читать значение по частям), да и доступ к регистрам через медленный и негибкий механизм PIO (команды IN/OUT процессора).
Local APIC (advanced programmable interrupt controller), встроенный во все современные процессоры Intel (начиная с архитектуры P54C) и который в своём составе имеет ещё и таймер. Более того, каждый логический процессор имеет свой собственный LAPIC, что может быть удобно для выполнения работы, локальной для текущего ядра, без необходимости управления ресурсами. Однако, данное устройство не имеет фиксированной известной частоты; последняя скорее привязана к частоте ядра. Поэтому перед использованием программе необходимо её вычислить (калибровать), а для этого нужно дополнительное референсное устройство. Режимы, поддерживаемые LAPIC: однократное прерывание, периодические прерывание, и период, определяемый TSC.
Таймер в составе ACPI, почему-то называемый Performance Monitoring Timer (PMTIMER) — ещё одно устройство, которое поддерживается всеми системами, реализующими стандарт ACPI, с 1996 года. Данный таймер имеет частоту 3.579545 МГц, ширина регистра-счётчика может быть 24 или 32 бита. Сам таймер всегда активен при включенном питании системы и не зависит от режима работы центрального процессора.
High Precision Event Timer (HPET) — устройство, созданное как замена устаревшему PIT. Согласно стандарту, HPET должен содержать осциллятор, работающий с фиксированной частотой по крайней мере в 10 МГц, величину которой можно программно прочитать из его статусного регистра, и монотонно увеличивающий значение счётчик шириной в 64 бита. Также он должен содержать минимум три компаратора шириной в 32 или 64 бита, которые и используются для генерации прерываний по истечении запрограммированных периодов времени. Как и PIT, он способен работать в периодическом режиме или в режиме однократного прерывания. При этом метод его программирования (MMIO вместо PIO) удобнее и быстрее, чем у PIT, что вместе с повышенным разрешением, позволяет задавать интервалы более точно и с меньшей задержкой. Требуемая стабильность генератора равна 0,05% для интервалов длиннее 1 мс и 0,2% для промежутков короче 100 мкс; много это или мало — зависит от приложений.
Несмотря на то, что HPET уже давно присутствует в PC (с 2005 года), операционные системы не торопятся начать его использовать. Частично это вызвано не самым удобным способом задания интервалов с помощью возрастающего счётчика вместо убывающего — из-за немгновенности операций существует риск «не успеть» и задать событие в прошлом. Зачастую ОС используют таймер из APIC или PMTIMER, или же функциональность TSC, использующую такты процессора в качестве источника времени.
Трудная судьба инструкции RDTSC
История TSC достаточно интересна и поучительна, чтобы остановиться на ней подольше.
Сама идея очень прозрачная — использовать в качестве источника времени сам процессор, а точнее его тактовый генератор. Текущий номер такта сохраняется в регистре TSC (timestamp counter).
С помощью TSC можно как узнавать время от начала работы, так и замерять интервалы времени с помощью двух чтений. TSC также работает как будильник в связке с APIC в режиме TSC deadline.
Что ж, TSC — вполне естественная штука с простой логикой и простым сценарием использования, которая должна обладать многими полезными свойствами: высокое разрешение (один такт ЦПУ), низкая задержка при чтении (десятки тактов), редкие переполнения (64-битного счётчика должно хватать минимум на 10 лет), монотонность чтений (ведь счётчик всегда увеличивает своё значение), равномерность (процессор всегда работает), согласованность с другими таймерами (при старте системы можно выставить нужное значение записью в MSR).
Разве что-то могло пойти не так? На пути к успешному использованию TSC в качестве основного средства измерения времени в PC встала последующая эволюция процессоров. Новые возможности, появившиеся в процессорах после Pentium, «испортили» RDTSC и много лет мешали использовать её как основной таймер в популярных ОС. Так, в 2006 году один из Linux-разработчиков Ingo Molnar писал:
Мы наблюдали, что в течение 10 лет ни одной реализации gettimeofday, основанной на TSC и работающей в общем случае, не было написано (а я написал первую версию для Pentium, так что и я в этом повинен), и что лучше мы обойдёмся без неё.
We just observed that in the past 10 years no generally working TSC-based gettimeofday was written (and i wrote the first version of it for the Pentium, so the blame is on me too), and that we might be better off without it.
Отмечу, что со временем в архитектуру IA-32 вносились коррективы, устранявшие проявившиеся недостатки, и в настоящий момент TSC может (пока опять не сломали) быть использован в том качестве, в котором он задумывался.
Прочие устройства
Выше я описал наиболее часто распространённые и используемые устройства по определению времени. Конечно же, конкретные системы могут иметь дополнительные устройства, уникальные для процессора, интегрированной логики или даже в форме специализированных периферийных устройств (например, сверхточные атомные часы). Степень их доступности из программ зависит от того, существует ли драйвер для конкретного устройства в выбранной ОС. Так, пробежавшись по исходникам Linux, я нашёл как минимум ещё два поддерживаемых источника времени для сборок x86: устройство NatSemi SCx200 в системах AMD Geode, и Cyclone для систем IBM x440. К сожалению, в Интернете не очень много документации по ним.
Заключение
Я надеюсь, что из этой заметки стало понятно, что работа со временем внутри компьютера на системном уровне на самом деле далека от тривиальной. Требования к устройствам, поставляющим время, зависят от решаемой задачи, и не всегда легко найти полностью подходящий вариант. При этом сами устройства зачастую содержат «архитектурные особенности», способные сломать голову несчастному программисту.
Однако это всё архитектурная присказка к симуляционной сказке. На самом деле мне хотелось рассказать о том, как можно моделировать весь этот зоопарк устройств. В следующей статье я опишу, как проявляется капризная природа времени при создании виртуальных окружений — симуляторов и мониторов виртуальных машин. Спасибо за внимание!