За межами кордонів

01.09.2016

За межами кордонів

У будь-якому комп’ютері всі об’єкти, які ми зберігаємо або модифікуємо, з неминучістю мають межі. Всі ресурси обмежені. Стеки обмежені, черги обмежені, ємність файлової системи обмежена і навіть числа обмежені. Однак при написанні коду про це часто забувають.

На курсах по сертифікації програмного забезпечення, які проводяться в Лабораторії реактивного руху (JPL), ми робимо акцент на тому, що для створення надійного коду необхідно тонко відчувати кордону системи. У будь-якому комп’ютері для виконання обчислень у вашому розпорядженні є лише обмежений шматочок пам’яті і кінцевий відрізок часу, тому всі об’єкти, які ми зберігаємо або модифікуємо, з неминучістю мають межі. Всі ресурси обмежені. Стеки обмежені, черги обмежені, ємність файлової системи обмежена і навіть числа, уявіть собі, обмежені. Тому світ комп’ютерів сильно відрізняється від світу математики, що при написанні коду занадто багато не беруть до уваги. Справа, мабуть, в тому, що в більшості випадків не так вже й важливо, знаєте ви про обмеження вашого комп’ютера чи ні, оскільки більшу частину часу все буде працювати правильно.

Є чудовий фрагмент в серії коміксів Peanuts, де персонаж Пеппермінт Петті наполегливо вселяє своїй однокласниці, що алгебра насправді не так вже й важка: «x майже завжди дорівнює одинадцяти, а y – майже завжди дев’ять». В математиці, якщо обидва доданків x і y позитивні, то ясно, що їх сума повинна бути більше кожного з них. У комп’ютері не так. Якщо ми використовуємо цілі 32-бітові числа зі знаком, це властивість зберігається лише для сум не вище десь два мільярди. Оскільки це більшу частину часу так, можна з упевненістю сказати, що коли ми збільшуємо x на позитивну величину, результат «майже завжди» буде більше початкового значення.

Майже завжди

Припустимо, що ми управляємо 32-бітовим знаковим лічильником (signed counter). Ми запускаємо його з нульового значення і інкрементіруем раз в секунду. Тоді до того моменту, коли він переповниться, пройде приблизно 68 років. Саме так зберігалося кількість секунд в оригінальній операційній системі Unix, де за нульову позначку була прийнята дата 1 січня 1970 року. Неозброєним оком видно, що цей метод запису часу має обмеження. Такі 32-розрядний годинник в системі Unix можуть працювати безперебійно лише на відрізку часу порядку 136 років: з грудня 1901 роки (негативна шкала рахунку секунд раніше січня 1970 роки) до січня 2038 року. Якщо продовжувати роботу в Unix досить довго, то у вівторок 19 січня 2038 року відбудеться аналог проблеми 2000-го року (Y2K), коли виявиться, що годинник перескочили назад на 13 грудня 1901 року.

Полагодити годинник в Unix неважко. Якщо просто перевести лічильник часу на 64-бітний формат, то для його переповнення буде потрібно близько 293-х мільярдів років. І хоча цей часовий інтервал небесконечен, він більш ніж достатній, оскільки, за прикидками вчених, ще до того моменту, як такий лічильник дорахував до позначки в районі п’яти мільярдів років, нашу сонячну систему чекає вогненний кінець.

Але якщо ми почнемо відраховувати час з більшою точністю, ніж посекундно, картина зміниться. Якщо, наприклад, ми будемо інкрементіровать наш 32-бітний лічильник кожні 100 мс, то знаковий лічильник (signed counter) переповниться приблизно через 6,8 років, а беззнаковий (unsigned counter) – через 13, 6 років. Якщо збільшити точність до 10 мс, то знаковий лічильник переповниться через 248,6 днів, а беззнаковий – через 497,1 днів.

Час на борту космічних апаратів

Космічний апарат НАСА «Діп імпакт» (Deep Impact) був запущений в січні 2005 року. Його вбудований контроллер обчислював час у вигляді числової послідовності 100-мілісекунд інтервалів, які відлічувалися з 1 січня 2000 року. Якщо додати 13,6 років, то отримаємо якусь дату серпня 2013 року, яка знаходиться далеко за межами запланованого терміну роботи місії. Призначення цього космічного апарату – скинути зонд-імпактора на комету Темпеля 1 і дослідити склад утворився пилової хмари. Місія завершився 4 липня 2004 року.

Часто буває так, що космічні апарати після успішного виконання основного завдання виконують додаткові завдання, за умови, що у них ще залишилися в достатній кількості необхідні ресурси. У листопаді 2010 року під час першої додаткової місії «Діп імпакт» пролетів на близькій відстані від комети Хартлі-2 (Hartley 2). Після цього у нього залишалося ще достатньо палива для іншої додаткової місії – зближення з астероїдом 2002 GT. Якби все пішло добре, то цей космічний апарат під новим ім’ям EPOXI досяг би астероїд у 2020 році.

Але цьому не судилося збутися. Бортовий хронометражний лічильник переповнився 13 серпня 2013 року, що ініціювало обробку виняткової ситуації. Це виняток призвело до скиду бортової апаратури в початковий стан, в результаті чого вона повинна була перейти в режим безпечної роботи. У реальності це скидання обнулив всю пам’ять за винятком бортового годинника, що призвело до зациклення апарату на скиданні. Навіть в цьому відчайдушному режимі наземні контролери можуть «реанімувати» космічну місію, подаючи «апаратні команди», минаючи головний процесор. Однак таке втручання потрібно робити швидко, до того, як космічний апарат «заблукає» і більш не зможе отримувати команди з Землі. Така допомога не встигла вчасно, і 20 вересня 2013 космічний апарат був оголошений втраченим. Його вбив 32-розрядний лічильник.

Це, на жаль, не єдиний приклад числового переповнення, що приводить до проблем навіть в системах з особливими вимогами до забезпечення безпеки (safety-critical systems).

Літаки

Пасажирський літак Боїнг 787 (Дримлайнер) складається з безлічі підсистем, де є вбудовані контролери. У них, як правило, передбачені внутрішній годинник і відлік часу. Контролери, вбудовані в блоки управління генераторами (generator control units – GCUs) відраховують час кожні 10 мс. Відповідні лічильники містять знакові 32-бітові числа. Тепер перед тим, як продовжити читання, гляньте ще раз в таблицю.

9 липня 2015 Федеральне управління цивільної авіації США випустило директиву льотної придатності (AD), яка дозволяє всім авіакомпаніям перезавантажувати блоки управління генераторами на всіх Боїнгах 787 як мінімум кожні 248 днів [1]. Директива говорить: «Мета AD – запобігти повній втраті подачі змінної напруги, в результаті чого можлива втрата керованості літака». Далі пояснюється, що «Ця AD з’явилася після того, як було виявлено, що літак моделі 787, в системи якого електроенергія подається безперервно протягом 248 днів, може виявитися знеструмленим через одночасне переходу блоків управління генераторами в відмовостійкий режим роботи. Причиною цього стану стали програмні лічильники в складі ПО генераторів …, які кожні 248 днів безперервної генерації електроенергії переходять в режим переповнення ».

Звідки беруться ці 248 днів, ми вже знаємо, проте, як і раніше, важливо бачити, як часто програмна помилка такого типу трапляється на практиці, причому навіть в системах, які пройшли досить жорсткий процес сертифікації. Програмне забезпечення для цивільних літаків завжди розробляється і тестується з великою ретельністю. Процес розробки повинен відповідати процедурам, обумовленим в стандарті DO-178B (і його наступнику DO-178C). Немає ніякого сумніву, що програмне забезпечення для Боїнга 787 писалося в поспіху. Перший Боїнг 748 був показаний публіці 8 липня 2007 року, а перший політ відбувся 15 грудня 2009 року. Перший комерційний політ був здійснений два роки по тому. Можна припустити, що до того моменту літак і яка керує ним ПО були під пильною увагою розробників протягом вагомої кількості років. Коли в на початку 2015 року було виявлено проблема переповнення, в експлуатації було майже 300 літаків.

Ресурсні обмеження

Той факт, що відмови складних систем можуть бути викликані подібними, до неподобства простими, причинами виглядає парадоксом. Пояснити це можна тим, що найпильнішу увагу приділяється об’єктивно складним завданням проекту; на периферії уваги опиняються саме прості елементи. Цим можна пояснити, чому потрапляння на відому кордон ресурсу може привести систему на грань відмови. За прикладами далеко ходити не треба; тут я приведу два з недавніх новин.

LightSail – «Сонячне вітрило»

20 травня 2015 року «Планетарне товариство» (Planetary Society) запустило в тестовий політ невеликий космічний апарат. Програма експерименту, названого LightSail ( «Сонячне вітрило»), полягала в випробуваннях великого сонячного вітрила. Всього через два дні випробувальний політ зіткнувся з несподіваною проблемою. У новинній стрічці «Mission Update» від 20 травня проблема описувалася так: «В даний час LightSail, ймовірно, завмер, на зразок того, як перестає реагувати настільний комп’ютер» [2].

Причина була відносно проста. Бортовий файл реєстрації зберігав записи всіх телеметричних даних, переданих апаратом; коли файл перевищив обсяг 32 Мбайт, стався фатальний збій системи. Чому 32 Мбайт? У прес-релізах це не пояснювалося, але ця цифра відповідає межі старої файлової системи FAT 12, яка ще використовується в старих вбудованих системах. В ідеології FAT12 для зберігання в файлової системі послідовності 512-байтних блоків використовується 16-бітна адресація, і так, 216 × 512 байт виходить за межі 32 Мбайт.

Curiosity – марсохід «Цікавість»

Інший приклад мені набагато ближче. Космічна програма НАСА «Марсіанська наукова лабораторія» (Mars Science Laboratory) була розроблена і втілена в тій підрозділі, де я працював. В рамках цієї програми 6 серпня 2012 року було успішно доставлений на поверхню Марса великий марсохід Curiosity ( «К’юріосіті» – «Цікавість»). З тих пір марсохід надійно функціонує, проте за цей час в його програмному забезпеченні сталося кілька збоїв.

Один з них стався приблизно через півроку після початку роботи на поверхні Марса, 27 лютого 2013 року. Несподівано сбойнул банк флеш-пам’яті. Програмне забезпечення марсохода використовує флеш-пам’ять для підтримки файлової системи, де зберігаються тимчасові файли з телеметричними даними і фотознімками поверхні Марса перед їх передачею на Землю. Файлова система у флеш-пам’яті запрограмована на режим «тільки для читання», на випадок, якщо станеться щось несподіване. Отже, коли стався збій банку пам’яті, це і була та сама несподіванка.

Під час нормального функціонування марсоходу багато завдань використовують файлову систему флеш-пам’яті для тимчасового зберігання результатів своєї роботи. Оскільки працює флеш-пямять повільно, а зберігати необхідно великі обсяги інформації, програмне забезпечення використовує метод буферизації – для тимчасового зберігання даних в оперативній пам’яті (ОЗУ) перед тим, як перенести їх в файлову систему флеш-пам’яті, а потім передати на Землю. Все працювало чудово, майже завжди.

Знаючи, що всі ресурси у вбудованих системах обмежені, можна задатися питанням, а що станеться, коду ОЗУ заповниться? Якби ця система була спроектована правильно, це траплялося б рідко і тривало б недовго, оскільки дані, тимчасово збережені в ОЗУ, перекочовували б врешті-решт у флеш-пам’ять. Таким чином, в цьому рідкісному випадку комп’ютер марсохода призупиняє роботу завдань, генеріруюшіх дані, до тих пір, поки не звільниться достатню кількість ОЗУ, після чого виконання відповідних завдань триває.

Можна також запитати, що відбувається, коли заповнюється флеш-пам’ять? Ця подія траплялося б ще рідше, оскільки від менеджерів місії потрібно постійно підтримувати достатній запас вільної флеш-пам’яті протягом всієї місії. Однак, якщо вона все ж заповнюється, то програмне забезпечення пишеться таким чином, щоб прати нізкопріорітение файли, щоб звільнити необхідну кількість флеш-пам’яті. Високопроіорітетние файли, які зберігаються у флеш-пам’яті, в кінці кінців будуть передані на Землю, і простір, яке вони займали, знову буде вільним.

Всі ці міркування виглядали досить продуманими і обгрунтованими, щоб успішно пройти всі експертизи проекту, аналіз коду і тестування блоків програми, що працюють з урахуванням відомих ресурсних лімітів. Однак один тестовий кейс (контрольний приклад) ні випробуваний – саме те, що трапився на Марсі в лютому 2013 року. Коли флеш-пам’ять внаслідок апаратної помилки знаходиться в режимі «тільки для читання», ніяких подальших змін зробити не можна: неможливо щось додати або стерти. Тепер ОЗУ повільно заповнюється новими даними, чекаючи, коли вони перепишуть у флеш-пам’ять. У якийсь момент ОЗУ не може вміщати нову інформацію. Відповідні завдання одна за одною припиняють свою роботу, і в підсумку марсохід стає неактивний.

Якщо у цій історії є щось позитивне, так це те, що навіть в цьому, здавалося, безнадійному випадку, наземні контролери «полагодили» марсохід і через кілька днів відновили його нормальну роботу. А наш журнал вивчених уроків поповнився ще одним записом. Залишилося сподіватися тільки на те, що у цього журнал теж з’явиться гранична межа.

Чи слід вам турбуватися? Програмісти, звичайно ж, знають, що всі ресурси, з якими вони працюють, обмежені. Але важко весь час пам’ятати про це витвережуючи факт. З огляду на частоту, з якою ці проблеми виникають, слід їх опрацьовувати. Проводьте ретельно сплановане тестування системних блоків і всієї системи, при якому досягаються відомі системні межі і робиться спроба їх перевершити. При використанні 32-бітних годин, протестуйте їх на перспективу за межі 248 днів, 1,3 і 13,6 років, і подивіться, чи працює ваша система як і раніше коректно. Якщо ви забули про ці тестах, то у вас, звичайно, багато часу, щоб про це подумати, але спати ви будете напевно краще, якщо все ж не забудете.

Герард Хольцман (Gerard I. Holzman), NASA / JPL, за матеріалами RTSoft.

Статьи

10.10.2018

Мільйони виробників насправді стикаються зі страхом банкрутства, в разі того, якщо вони не проведуть цифрові перетворення, але, на жаль, це не так просто. Якщо (читать далее)

01.10.2018

У доступному для огляду майбутньому польові шини не зникнуть з промислової автоматизації, однак стандарт Ethernet для швидкісних мереж (TSN) спільно з незалежним стандартом сумісності (читать далее)