Jonathan CorbetПорой кажется, что на фронте борьбы с проблемой 2038 года наступило относительное затишье. Однако время идет, и тот день, когда 32-битные значения типа time_t больше не смогут корректно отображать даты, наступит уже меньше чем через 21 год. Этот срок может показаться большим, однако сравнительно долгий жизненный цикл многих встраиваемых систем подразумевает, что некоторые из них, будучи введенными в строй в наше время, все еще будут работать, когда наступит критический момент. Арнд Бергманн — один из основных разработчиков, занимающихся этой проблемой. На конференции Linaro Connect 2017 он поделился новостями о текущем положении дел в этой области.

Согласно Бергманну, работа ведется сразу в трех независимых направлениях, первое из которых — само ядро Linux. В течение последних пяти лет он искал способы подготовить ядро к 2038 году. Значительная часть этой работы подразумевает конвертирование 32-битных меток времени (timestamps) в 64-битные значения — даже на 32-битных системах. Некоторые 32-битные метки времени также используются в пользовательских API, что значительно усложняет проблему. У Бергманна есть план по улучшению таких API за счет приспособленных к 2038 году версий проблемных системных вызовов, но эти изменения еще не были применены, за исключением недавно добавленного системного вызова statx() в версии 4.11, которыйзаменит семейство вызовов stat(). В то же время остается немало других системных вызовов, нуждающихся в такой замене.

Дипа Динамани также занимается решением проблем, связанных с ядром. Она начинала в качестве стажера по программе Outreachy и по окончании стажировки продолжила работать над этой задачей. Динамани решила одну из самых сложных проблем, разработав собственный набор патчей для прослойки виртуальной файловой системы, и также планирует заняться другими системными вызовами. Вызов setsockopt(), среди прочих, может представлять особую трудность: его не так-то легко поправить или эмулировать на уровне glibc. Заметный прогресс есть в работе по созданию патчей для модуля device mapper и подсистемы ввода. Бергманн также написал патч для подсистемы video4linux, но он был отклонён и требует другого подхода. Примерно так же обстоят дела со звуковой подсистемой. Другими проблемными компонентами ядра являются система управления ключами и часы реального времени.

Некоторые системные вызовы не получат замены, поскольку для них лучшим решением оказывается эмуляция в библиотеках C — это второе направление в подготовке к 2038 году. Бергманн отметил, что сообщество glibc проделало особенно большую работу в этой области. На уровне библиотек планируется обеспечить полную обратную совместимость. Это означает, что можно будет собирать программы как с 32-битными, так и с 64-битными метками времени, при этом последние можно использовать даже в старых версиях ядра. Другими словами, разработчики glibc ищут решения, которые бы работали на всех платформах и с минимальными искажениями. (Подробности можно узнать из черновика проекта).

Третье направление связано со сборками дистрибутивов, причем настоящий прогресс здесь возможен только после того, как будут решены первые две задачи. По мнению Бергманна, маловероятно, что авторы дистрибутивов все еще будут поддерживать 32-битные системы в 2038 году, так что у них нет причин для беспокойства. Единственным исключением может стать дистрибутив Debian, авторы которого, похоже, заинтересованы в продолжении поддержки, даже несмотря на очевидную трудоемкость этого процесса. В какой-то момент может потребоваться полная пересборка, что едва ли обрадует как авторов, так и пользователей, но это решение, по крайней мере, гарантированно работает. В такой системе ключевым условием является обеспечение совместимости; сейчас в эксплуатацию вводится код, который может быть не приспособлен к наступлению 2038 года, но хочется, чтобы он, по возможности, работал и дальше.

Автомобильные системы — еще одна большая проблемная область. Многие устройства, например мобильные телефоны, перестанут работать к 2038 году по многим другим причинам, так что нет смысла готовить их к его наступлению. Однако автомобили находятся в эксплуатации гораздо дольше. В ходу по-прежнему могут быть камеры, и очень вероятно, что будет существовать множество тесно интегрируемых систем, например, в инфраструктуре зданий. Некоторые из таких систем подвергнутся сбоям в 2038 году. Вот почему так важно решить проблему как можно скорее.

В то же время с исправлением некоторых компонентов возникнут трудности, даже когда задачи по подготовке ядра, библиотек C и дистрибутивов будут в основном решены. Многие из этих трудностей связаны с использованием 32-битных значений типа time_t в форматах файлов. Например, cpio сломается, что вызовет определенные проблемы, так как он используется в формате RPM-пакетов. В файловых системах NFSv3, ext3 и XFS тоже будут наблюдаться сбои из-за использования 32-битных меток времени. Первые две системы, вероятно, выйдут из употребления задолго до начала 2038 года, а для XFS уже разрабатываются варианты решений. Наконец, есть множество приложений, пока не попавших в поле зрения разработчиков, а также корпоративных систем, к которым у сообщества нет доступа.

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

В конце выступления Джон Шульц спросил, могут ли наработки сообщества BSD, которое (в некоторых версиях) уже решило проблему 2038 года, помочь Linux. Бергманн ответил, что помощь будет «незначительной». У BSD-дистрибутивов есть то преимущество, что они могут пересобраться с нуля, так что у них нет необходимости поддерживать совместимость с пользовательскими ABI тем же способом. Их опыт по подготовке приложений к 2038 году не лишен ценности, но неизвестно, насколько полезным он окажется для сообщества Linux.

Примечание команды PVS-Studio. Нас заинтересовала эта проблема и мы планируем реализовать в анализаторе PVS-Studio диагностику, которая будет предупреждать об использовании 32-битных переменных типа time_t. Оригинал данной статьи был опубликован на сайте lwn.net (CC-SA license).
Поделиться с друзьями
-->

Комментарии (34)


  1. Cobolorum
    05.05.2017 12:03
    -5

    На подобные темы всегда вспоминается анекдот:
    1999 год программист успешно решает проблемы 2000-года, рубит деньги мешками и вот наступает 30 декабря 1999 и его посещает мысль, а если все что не дал не сработает все же рухнет! И решает себя заморозить и разморозить в январе.
    Ну и замораживается….
    Просыпает и первый вопрос: Проблемы с моим кодом были?
    Ему в ответ: Проблем с кодом не было, но есть другая проблема?
    Программист: Какая?
    Ему в ответ: Сегодня 10 января 9999 года….


  1. master1312
    05.05.2017 12:24

    В свое время я нашел в библиотеке Qt волшебную функцию QDateTime::toMSecsSinceEpoch(). Так что я уже готов к 2038-у году. :)


  1. nemavasi
    05.05.2017 12:26
    +8

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


    1. romankonstant
      05.05.2017 15:01
      +6

      Зато потом можно 292 млрд. лет спать спокойно


      1. Keyten
        05.05.2017 18:40

        А дальше?


        1. Kriger91
          05.05.2017 19:08
          +6

          Ну там уже точно пенсию дадут всем кто доживёт…


  1. AndrewKevich
    05.05.2017 14:00
    +1

    А мне вот интересно, что будут делать со спутниками GPS, которые в космосе находятся? Они же тоже время раздают. И я думаю просто так их тоже не обновишь, а срок службы мне кажется тоже довольно большой…


    1. John_Minority
      05.05.2017 14:26

      Они к этому времени все обновятся.


      1. Jabher
        05.05.2017 16:18
        +1

        уточню: сами железки обновятся. не прошивка. у них срок гарантированной службы 10 лет что ли сейчас (по крайней мере у глонасса).


    1. SvyatoslavMC
      05.05.2017 14:45
      -1

      По-моему, в такие устройства заранее закладывают возможность удалённой прошивки.


      1. Jef239
        06.05.2017 02:05
        +2

        А вы в курсе, что даже эфемериды и альманахи (несколько килобайт) не на каждом витке удается заложить?

        Linux там не используется, ибо он совсем не система реального времени. Счетчики времени там очень точные, то есть переполнение идет часто. Посчитаем. При обновлении счетчика раз в 1 мкс (размер импульса GPS), 32битный счетчик переполняется раз в 4.9 дней. Так что там все штатно.

        Отгадка простая — не нужна спутнику дата. От слова «совсем» не нужна.


    1. Jef239
      06.05.2017 01:56
      +8

      Ну что ж, давайте расскажу.

      GPS передает время с начала недели и 10 битный номер недели (0-1023). Таким образом внутри 19.6 летнего периода все чисто. А вот дата с точностью 19.6 лет (эпоха GPS) — зашивается в приемник. Или берется из ГЛОНАСС-М. Ближайшая смена эпохи — полночь по гринвичу с 6 на 7 апреля 2019 года.

      ГЛОНАСС передает время с начала суток по Москве и номер дня в четырехлетнем периоде. Ближайшая смена эпохи — в полночь по Москве с 31 декабря 2019 года на 1 января 2020 года. Собственно ни одно ГЛОНАСС уже не летает.

      ГЛОНАСС-М дополнительно передает 5 бит номера четырехлетнего периода, «первый год первого четырехлетия соответствует 1996 году.» Переход через 0 будет в 2116 году.

      GALILEO очень похож на GPS, но номер недели у него 12 битный, а период счета — 78 лет. Ноль отсчета 22 августа 1999 года (точка смены эпохи GPS), а смена эпохи GALILEO нас ждет в 2078 году.

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

      С современными бытовыми приемниками довольно просто — они все GPS + ГЛОНАСС-М. Проблем есть с двуями типами приемников:

      • GPS-приемники без ГЛОНАСС, выпущенные до августа 1999 года, то есть уже пережившую одну смену эпохи.
      • ГЛОНАСС-приемники, не понимающие номер четырехлетия.


      К первому типу относится авиационная аппаратура, например на А-320 стоит приемник GPS разработки примерно 1992 года. Второй тип — это разные приемники минобороны РФ.

      К счастью, для расчетов координат дата не нужна. И точное время GPS — это отметка начала секунды (1PPS), а не полная дата. Так что дата на индикаторе полетит, но в целом никаких проблем не будет.


  1. KongEnGe
    05.05.2017 14:49

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


  1. Loki3000
    05.05.2017 16:14
    +4

    Любопытно, а когда придумывали хранить метку времени в 32-битном числе, действительно считали 2038 год бесконечно далеким или просто пришлось пойти на такой компромисс ради производительности?


    1. KongEnGe
      05.05.2017 17:11
      +11

      Дело было даже не в производительности, а в стоимости памяти. Плюс соображение «через 50 лет решать связанные с этим вопросы будем точно не мы».


    1. Toledo
      05.05.2017 21:06

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


  1. tandzan
    05.05.2017 17:55

    Решил разобраться, доживут ли мои DIY часы с NTP клиентом до внуков, оказалось у них переполнение вообще в 2036 г.


    1. Envek
      05.05.2017 20:18

      О, расскажите поподробнее, кто съел два года в часах?


      1. tandzan
        05.05.2017 20:25
        +3

        Тут расписано.


  1. Antervis
    05.05.2017 19:59

    есть вероятность, что исправят легаси код, который пахнет с лохматых 80-х


  1. AVI-crak
    05.05.2017 22:43
    +2

    Можно решить эту проблему раз и навсегда — если принять за нулевой год рождение нашей вселенной.
    Так сказать — подготовится к использованию машин времени.
    Историю тоже стоит переписать на конкретные года, а то там путаница с маркерами «наша эра», «меловой период», и так далее.


    1. lany
      06.05.2017 12:17
      +3

      Если вы мыслите в масштабах Вселенной, вас больно укусит теория относительности, ещё до машин времени. Абсолютной шкалы времени в принципе нет, и с этим надо жить.


  1. crea7or
    05.05.2017 22:53
    +1

    Это всё из-за int'ов. Был бы uint — было бы в два раза больше. Но в целом в it так вообще часто.


    1. nanshakov
      05.05.2017 23:52

      Часто? Можно примеры? интересно.


      1. Busla
        06.05.2017 00:29
        +2

        на DVD была проблема с файлами больше 2 Гб, хотя по стандарту iso9660 закладывалось ограничение в 4 Гб


      1. Antelle
        06.05.2017 01:33
        +2

        zip vs zip64


      1. crea7or
        06.05.2017 01:37
        +1

        даже malloc был изначально с int'ом.


    1. Envek
      06.05.2017 23:39
      +1

      Я думаю, что здесь int сделали сознательно — чтобы можно было кодировать timestamp'ы до 1970-го года (на момент разработки Unix вполне себе даже настоящего времени — Википедия говорит, что самый первый Unix появился ещё в 1969).


  1. Antelle
    06.05.2017 01:35

    Интересно, кто-то будет через 21 год спрашивать, что делать с Windows XP?


    1. Jef239
      06.05.2017 02:07

      Да. Банкоматы на XP и скорее всего доживут.


    1. Envek
      06.05.2017 23:34

      А в винде где-то Unix Timestamp используется? 0_о


  1. decomeron
    06.05.2017 09:29
    +1

    А после 64 что будет, кто нибудь знает? а то вдруг все таки придется дожить.


    1. LynXzp
      07.05.2017 17:08
      +2

      65 или 128, это смотря как считать. Извините, не сдержался.


  1. gshep
    07.05.2017 12:24
    -2

    Я хоть сварщик и не самого высокого разряда, но гари чуть понюхать
    успел и могу высказать своё скромное мнение на этот счёт.


    Первое — это вопрос "откуда вообще проблема возникает"? Может она
    надумана? Нет, не надумана и виноваты в ней в первую очередь говнокодеры:
    в подавляющем большинстве объект типа time_t преобразуется в int/long
    и рассматривается как количество секунд, пройденных с начала эпохи. Почему
    же я, такой надменный, смею называть этих лапшаделов негативным словом?
    Просто в силу того, что в документации сказано, что внутренее строение
    типа time_t
    не задано! [http://en.cppreference.com/w/c/chrono/time_t]
    Раз не задано, то и работать с ним напрямую нельзя, только
    с использованием специального API:


    1. time;
    2. localtime;
    3. gmtime;
    4. mktime;
    5. difftime — из-за чего, собственно, и преобразовывают к целому:
      чтобы вычислить период времени, измеряемый в секундах. Да, раньше 89/90го
      стандарта этой функции не было, но её возможно реализовать через
      предыдущие, но это же сложнее!

    Во вторую очередь — это само API является кривым. Даже если бы все
    работали с объектами time_t "правильно", то решить проблему 2038го года
    получилось бы костыльно:


    1. текущее АПИ предполагает, что объекты копируются по значению.
      Это мешает сделать простое using time_t = std::uint64_t;
    2. нет функций по созданию и удалению объектов типа time_t. Это
      означает, что при попытке сохранять в time_t индекс объекта в некой
      таблице, придётся либо выделить память процессу сразу под всё
      возможное их количество, либо выделять по мере необходимости — вызов
      mktime; и освобождать весь этот массив по завершении процесса.

    В-третьих, как должно выглядеть нормальное АПИ. Оно должно выглядеть
    также, как и CreateFile/CloseHandle из WinAPI:


    Заголовок спойлера
    /* да, для красоты можно и сказать, что есть некий тип, но
            его кишки вам смотреть запрещено!
    
            struct TimeType;
            using time_t = TimeType *;
        */
        using time_t = void *;
        const auto INVALID_TIMET_VALUE = /* ... */;
    
        time_t create_time(/* some arguments */);
    
        /* код ошибки - на всякий случай */
        int free_time(time_t time_);
    
        int mktime(struct tm * time_, time_t outTime_);
    
        /* sample */
        time_t currentTime = create_time(/* ... */);
        if (INVALID_TIMET_VALUE == currentTime)
        {
            fprintf(stderr, "create_time failed; errno = %d\n", errno);
            return;
        }
    
        const auto result = mktime(anotherTimeRepresentation, currentTime);
        if (0 != result)
        {
            fprintf(stderr, "mktime failed; errno = %d\n", errno);
            return;
        }
    
        /* работаем с currentTime */
    
        const auto freeResult = free_time(currentTime);