- PVSM.RU - https://www.pvsm.ru -

История POSIX: путь к портируемому ПО

История POSIX: путь к портируемому ПО - 1

Как мы к этому пришли?

В ранние годы развития компьютеров программисты могли лишь мечтать о портируемости. Все программы писались непосредственно в машинном коде для каждой компьютерной архитектуры, на которой они должны были работать. Языки ассемблера [1] с мнемоническими именами каждой команды CPU и другие удобства сильно упростили жизнь программистов, но программы по-прежнему были привязаны к архитектуре. Тогда ещё не изобрели операционных систем, поэтому программа не только управляла всей компьютерной системой, но и должна была инициализировать всю периферию, а также управлять ею. На самом деле, такие низкоуровневые программы реализовывали драйверы для каждого используемого ими устройства. И каждый раз, когда программу нужно было перенести на оборудование с другой архитектурой, она в буквальном смысле переписывалась с учётом различий архитектуры набора команд CPU [2], структуры памяти и так далее.

Именно так произошло с Unix, который изначально был написан Кеном Томпсоном на языке ассемблера более пятидесяти лет назад. Первые версии Unix писались для платформы PDP-7 [3], а для портирования его на PDP-11 [4] нужно было переписывать код. Когда Дэннис Ритчи создал язык программирования C, и вместе с Томпсоном [5] они переписали на нём основную часть кода Unix, внезапно оказалась возможной портируемость ПО. Тому были две главные причины. Во-первых, код, написанный на языке высокого уровня, не зависит от платформы, потому что компиляторы транслируют его в язык ассемблера целевой архитектуры. Это ещё важнее для целевых платформ на основе процессоров RISC [6], так как они требуют написания гораздо большего количества ассемблерных команд, чем процессоры CISC [7]. Даже при портировании Unix на другую платформу основная сложность заключалась лишь в адаптации зависящих от архитектуры частей кода. С другой стороны, сама операционная система абстрагирует все особенности оборудования от пользовательской программы.

Программистам не нужно реализовывать многозадачность, управление памятью и драйверы для используемых ими устройств, потому что всё это часть ядра ОС и работает в адресном пространстве ядра. Пользовательские программы работают в пользовательском адресном пространстве и получают доступ ко всем предоставляемым ОС функциям при помощи интерфейса системных вызовов. В ОС реального времени [8], например, в Zephyr OS [9] ситуация немного отличается [10], но принцип изоляции и защиты памяти для пользовательских программ сохраняется. Это приводит к двум выводам:

  • Пользовательские программы становятся портируемыми, если они написаны на высокоуровневом языке программирования для конкретной ОС. При соблюдении обоих требований программы компилируются в команды целевого CPU и компонуются с системными функциями, предоставляемыми libc [11] и относящимися к ОС библиотеками, для получения доступа к оборудованию.

  • Портируемость обеспечивается на уровне исходного кода.

Рождение POSIX

Это могло быть концом истории, но случилось нечто судьбоносное. Из-за юридических ограничений компания AT&T не имела права продавать Unix, а значит, и зарабатывать на новорождённой ОС, которая завоёвывала всё большую популярность. Однако оказалось, что можно распространять Unix любым заинтересованным организациям по цене носителя. Так Unix попал в 1974 году в Беркли и во множество других мест, приведя к созданию различных производных ОС. Одни из самых известных и по-прежнему популярных ОС основаны на ПО, распространявшемся Беркли (BSD), например, FreeBSD и OpenBSD. Несмотря на общих предков и принципы, каждая операционная система пошла по собственному уникальному пути.

Каждая из этих ОС имела уникальный интерфейс (API) и реализацию подсистем ядра, системных вызовов, системных инструментов и так далее. Даже libc, предоставляющая общую функциональность и обёртки поверх системных вызовов, была очень привязана к ОС. Все эти ОС походили на Unix, но в то же время было невозможно взять исходный код программы, написанной для одной ОС, и перекомпилировать его на другой.

Более чем тридцать пять лет назад эти проблемы с портируемостью ПО привели к появлению в 1988 году первого стандарта POSIX [12]. Эту аббревиатуру придумал Ричард Столлман [13], добавивший в X в конец Portable Operating System Interface. Сейчас торговой маркой POSIX™ владеет IEEE [14], а UNIX® — это зарегистрированная торговая марка The Open Group [15]. Она должна предоставлять спецификацию интерфейса [16], общего для различных операционных систем Unix, в том числе языков программирования и инструментов [17]. Важно отметить, что портируем интерфейс, а не реализация.

Это стало общей платформой, позволившей компилировать один и тот же исходный код пользовательской программы для любой ОС без модификаций при условии соблюдения обеими сторонами одного стандарта. В определённой степени это справедливо и сегодня, так как большинство современных популярных Unix-подобных систем, например, Linux и *BSD не соответствует полностью и строго стандарту POSIX, а использует его как руководство. Кроме POSIX существует Single UNIX Specification [18] (SUS), в 2001 году консолидированная с несколькими стандартами POSIX. Однако последняя версия SUS (SUSv4 2018) расширяет спецификацией X/Open Curses последний стандарт POSIX (POSIX.1-2017), который, по сути, служит его базовой спецификаций.

Существует множество операционных система наподобие MacOS [19], полностью совместимых со стандартами POSIX и SUS, проходящих тесты совместимости The Open Group, а потому имеющих право называться операционными системами Unix [20], а не просто Unix-подобными. Изначально POSIX создавался только для Unix-подобных ОС, но со временем стал настолько популярным, что его спецификацию в виде Operating System Abstraction Layer (OSAL) [21] частично реализовали (некое подмножество интерфейса, применимое к целевой системе) в несвязанных с Unix операционных системах, например, в Windows [22]FreeRTOS [23]Zephyr [24] и так далее.

Спецификация POSIX

Самый первый стандарт был ратифицирован IEEE в 1988 году под названием IEEE Std 1003.1-1988, поэтому он называется POSIX.1-1988. С тех пор стандарт претерпел множество ревизий, а разные подмножества спецификации ратифицировались под разными названиями. Например, POSIX.1-1990 (IEEE 1003.1-1990) определяет системный интерфейс и среду вычисленийPOSIX.2 (IEEE Std 1003.2-1992) определяет язык команд (шелл) и инструменты и так далее. Очень хорошее и краткое описание ревизий стандарта можно найти на странице man Linux standards(7) [25]. Можно даже найти ссылки на некоторые старые ревизии, например, на POSIX.2, изучая исходный код Bash [26]. В 2001 году POSIX.1, POSIX.2 и Single UNIX Specification (SUS) были объединены в общий документ под названием POSIX.1-2001. Несмотря на запутывающее название, на самом деле он включает в себя спецификации шелла и инструментов из POSIX.2. Последней версией стандарта является POSIX.1-2017, также известный как IEEE Std 1003.1-2017 [12]; он почти полностью идентичен POSIX.1-2008.

Документ стандарта, по сути, описывает спецификацию, распространяющуюся на два окружения (среду сборки и среду исполнения); он представлен в виде нескольких томов:

  • Базовые определения [27]: определяет общие для всех томов термины и концепции, требования совместимости (символьные константы, опции, группы опций), среду вычислений (локали, регулярные выражения, структуру папок, tty, переменные окружения и так далее) и файлы заголовков языка C, которые должны реализовываться соответствующими стандарту системами.

  • Системные интерфейсы [28]: определяет стандарт языка C (ISO C99, ISO/IEC 9899:1999 [29]), функции системных сервисов и расширение стандартной библиотеки C (libc) относительно файлов заголовков и функций.

  • Шелл и утилиты [30]: определяет интерфейс уровня исходного кода для Shell Command Language (sh) и системных утилит (awk, sed, wc, cat, ...), в том числе поведение, параметры командной строки, статусы выхода и так далее.

  • Аргументация [31]: включает в себя вопросы портируемости, субпрофилирования, групп опций и дополнительную аргументацию, не подходящую ко всем остальным томам.

Текущий стандарт POSIX определяет совместимость уровня исходного кода только для двух языков программирования [32]языка C (C99) и языка команд шелла. Однако некоторые из программ, определённые в «Утилитах», например, awk, также имеют свой собственный язык. Строго говоря, стандартная библиотека C (libc) не обязана реализовывать никакой дополнительной функциональности (функции и заголовки), не определённой стандартом C (в данном случае ISO C99), но большинство из них это делают. Например, стандарт ISO C99 определяет 24 файла заголовков, включая математические функции (math.h), стандартный ввод-вывод (stdio.h), дату и время (time.h), управление сигналами (signal.h), операции со строками (string.h) и так далее. Однако последний стандарт POSIX определяет 82 файла заголовков и, будучи полностью совместимым с ISO C99, расширяет его потоками POSIX (pthreads.h), семафорами (semaphore.h) и многим другим.

Кроме того, современные реализации libc, например, musl libc [33], тоже сильно привязаны к ОС, представляя библиотечные функции для доступа к сервисам операционных систем (обёртки для системых вызовов). Иногда пересечение со спецификациями POSIX приводит к возникновению трудностей в реализации слоя приложений POSIX в несвязанных с Unix операционных системах, использующих портируемые автономные реализации libc с собственной поддержкой POSIX, например, picolibc [34] вместе с POSIX-библиотекой Zephyr [35].

Опции и группы опций

Хотя POSIX стандартизирует системный интерфейс (заголовки и функции языка C), шелл и утилиты, необязательно следовать всей спецификации, чтобы быть совместимым с POSIX [36]. Некоторые фичи в «Системных интерфейсах POSIX», «Шелле и утилитах POSIX» и «Системных интерфейсах XSI» опциональны. Файл заголовка unistd.h [37] содержит определения стандартных символьных констант для опций [38], отражающих отдельную фичу, и для групп опций [39], определяющих множество взаимосвязанных функций или опций. Имена групп опций, в отличие от опций, обычно не начинаются с символа подчёркивания. Совместимые с POSIX (POSIX Conformant) системы должны реализовывать и поддерживать множество обязательных опций с одной или несколькими дополнительными опциями. Символьные константы для обязательных опций должны иметь определённые значения, например, 200809L, в то время как другие опции могут быть

  • неопределёнными или содержать -1; это означает, что опция не поддерживается для компиляции

  • 0; это означает, что опция может как поддерживаться, так и не поддерживаться в среде исполнения

  • каким-то другим значением; это означает, что опция поддерживается всегда

Эти символьные константы используются пользовательскими приложениями для проверки доступности конкретных фич. На уровне исходного кода C константы могут проверяться или во время сборки (в директивах препроцессинга #if), или в среде исполнения вызовом одной из следующих функций: sysconf()pathconf()fpathconf() или confstr(3). В исходном коде шелла для проверок в среде исполнения должна использоваться утилита getconf [40]. Очень хорошую коллекцию опций POSIX вместе с соответствующими именами для использования в качестве параметров sysconf(3), а также список файлов заголовков и функций, представляющих эти опции, можно найти на странице man Linux posixoptions(7) [41].

Группы опций субпрофилирования [42] предназначены для использования в системах, где реализация полной спецификации POSIX не имеет смысла. Например, встроенные системы реального времени обычно имеют ограничения по ресурсам, поэтому не содержат шеллов, интерфейсов пользователя, а ядра ОС часто проектируются так, чтобы выполняться как единый процесс (с множественными потоками). Такие системы могут реализовывать только подмножества соответствующих функций, определяемых группами опций.

Подведём итог

  • Развитие высокоуровневых языков программирования наподобие C наряду с операционными системами, абстрагирующими аппаратные подробности, обеспечили портируемость ПО на уровне исходного кода.

  • В 1988 году появился стандарт POSIX, целью которого было создание спецификации портируемого интерфейса для Unix-подобных операционных систем, что позволило компилировать программы под разные платформы.

  • Со временем стандарт POSIX эволюционировал; его последней версией стал POSIX.1-2017 (IEEE Std 1003.1-2017).

  • Современные Unix-подобные системы наподобие Linux и *BSD соответствуют стандарту POSIX не строго, а используют его в качестве руководства.

  • POSIX стандартизирует C API (файлы заголовков и функции), шелл и утилиты.

  • От совместимых с POSIX систем ожидается реализация обязательных опций и возможная поддержка дополнительных опциональных фич.

  • Приложения могут проверять наличие фич POSIX и во время компиляции, и в среде исполнения при помощи символьных констант и системных функций.

  • Для систем с ограниченными ресурсами, например, для встроенных платформ реального времени, POSIX позволяет реализовывать подмножества полной спецификации посредством «субпрофильных» групп опций.

Автор: Beeline_tech

Источник [43]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/395185

Ссылки в тексте:

[1] Языки ассемблера: https://en.wikipedia.org/wiki/Assembly_language

[2] архитектуры набора команд CPU: https://en.wikipedia.org/wiki/Instruction_set_architecture

[3] PDP-7: https://en.wikipedia.org/wiki/PDP-7

[4] PDP-11: https://en.wikipedia.org/wiki/PDP-11

[5] вместе с Томпсоном: https://www.invent.org/sites/default/files/2019-02/Inductee-UNIX_Thompson_Ritchie.jpg

[6] процессоров RISC: https://en.wikipedia.org/wiki/Reduced_instruction_set_computer

[7] процессоры CISC: https://en.wikipedia.org/wiki/Complex_instruction_set_computer

[8] ОС реального времени: https://en.wikipedia.org/wiki/Real-time_operating_system

[9] Zephyr OS: https://www.zephyrproject.org/

[10] немного отличается: https://www.youtube.com/watch?v=4_uL43V79xw

[11] libc: https://en.wikipedia.org/wiki/C_standard_library

[12] стандарта POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/nframe.html

[13] придумал Ричард Столлман: https://opensource.com/article/19/7/what-posix-richard-stallman-explains

[14] IEEE: https://www.ieee.org/about/index.html

[15] The Open Group: https://www.opengroup.org/about-us

[16] спецификацию интерфейса: https://www.techtarget.com/whatis/definition/POSIX-Portable-Operating-System-Interface

[17] языков программирования и инструментов: https://stackoverflow.com/a/31865755

[18] Single UNIX Specification: https://en.wikipedia.org/wiki/Single_UNIX_Specification

[19] множество операционных система наподобие MacOS: https://en.wikipedia.org/wiki/POSIX#POSIX-oriented_operating_systems

[20] операционными системами Unix: https://www.opengroup.org/openbrand/register/

[21] Operating System Abstraction Layer (OSAL): https://en.wikipedia.org/wiki/Operating_system_abstraction_layer

[22] Windows: https://en.wikipedia.org/wiki/Cygwin

[23] FreeRTOS: https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_POSIX/index.html

[24] Zephyr: https://docs.zephyrproject.org/latest/services/portability/posix/index.html

[25] standards(7): https://man7.org/linux/man-pages/man7/standards.7.html

[26] исходный код Bash: https://git.savannah.gnu.org/cgit/bash.git/tree/jobs.c#n4269

[27] Базовые определения: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/toc.html

[28] Системные интерфейсы: https://pubs.opengroup.org/onlinepubs/9699919799/idx/xsh.html

[29] ISO C99, ISO/IEC 9899:1999: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

[30] Шелл и утилиты: https://pubs.opengroup.org/onlinepubs/9699919799/idx/xcu.html

[31] Аргументация: https://pubs.opengroup.org/onlinepubs/9699919799/idx/xrat.html

[32] только для двух языков программирования: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_04

[33] musl libc: https://musl.libc.org/about.html

[34] picolibc: https://keithp.com/picolibc/

[35] POSIX-библиотекой Zephyr: https://docs.zephyrproject.org/latest/services/portability/posix/implementation/index.html

[36] совместимым с POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_03

[37] Файл заголовка unistd.h: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html

[38] опций: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_06

[39] групп опций: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_05

[40] getconf: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html

[41] posixoptions(7): https://man7.org/linux/man-pages/man7/posixoptions.7.html

[42] Группы опций субпрофилирования: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_subprofiles.html

[43] Источник: https://habr.com/ru/companies/beeline_tech/articles/840040/?utm_campaign=840040&utm_source=habrahabr&utm_medium=rss