Основные функции
В стандарте определено более 60 вызовов функций. Они могут быть разделены на 4 категории:
- Потоковое управление — creating, joining thread
- Мьютексы
- Условные переменные
- Синхронизация между потоками используя read/write блокировки и барьеры.
- Спинлок
Опишем ряд самых основных функций, чтобы дать представление о том, как они работают.
Вызовы, связанные с потоком | Описание |
---|---|
Создание нового потока | |
Завершение работы вызвавшего потока | |
Ожидание выхода из указанного потока | |
Освобождение центрального процессора, позволяющее
выполняться другому потоку |
|
Создание и инициализация структуры атрибутов потока | |
Удаление структуры атрибутов потока |
Все потоки Pthreads имеют определенные свойства. У каждого потока есть свои идентификатор, набор регистров (включая счетчик команд) и набор атрибутов, которые сохраняются в определенной структуре. Атрибуты включают размер стека, параметры планирования и другие элементы, необходимые при использовании потока. Новый поток создается с помощью вызова функции . В качестве значения функции возвращается идентификатор только что созданного потока. Этот вызов намеренно сделан очень похожим на системный вызов (за исключением параметров), а идентификатор потока играет роль PID, главным образом для идентификации ссылок на потоки в других вызовах.
Когда поток заканчивает возложенную на него работу, он может быть завершен путем вызова функции . Этот вызов останавливает поток и освобождает пространство, занятое его стеком. Зачастую потоку необходимо перед продолжением выполнения ожидать окончания работы и выхода из другого потока. Ожидающий поток вызывает функцию , чтобы ждать завершения другого указанного потока. В качестве параметра этой функции передается идентификатор потока, чьего завершения следует ожидать.
Иногда бывает так, что поток не является логически заблокированным, но считает, что проработал достаточно долго, и намеревается дать шанс на выполнение другому потоку. Этой цели он может добиться за счет вызова функции . Для процессов подобных вызовов функций не существует, поскольку предполагается, что процессы сильно конкурируют друг с другом и каждый из них требует как можно больше времени центрального процессора. Но поскольку потоки одного процесса, как правило, пишутся одним и тем же программистом, то он добивается от них, чтобы они давали друг другу шанс на выполнение.
Два следующих вызова функций, связанных с потоками, относятся к атрибутам. Функция создает структуру атрибутов, связанную с потоком, и инициализирует ее значениями по умолчанию. Эти значения (например, приоритет) могут быть изменены за счет работы с полями в структуре атрибутов.
И наконец, функция удаляет структуру атрибутов, принадлежащую потоку, освобождая память, которую она занимала. На поток, который использовал данную структуру, это не влияет, и он продолжает свое существование.
Чтобы лучше понять, как работают функции пакета Pthread, рассмотрим простой пример, показанный ниже. Основная программа этого примера работает в цикле столько раз, cколько указано в константе (количество потоков), создавая при каждой итерации новый поток и предварительно сообщив о своих намерениях. Если создать поток не удастся, она выводит сообщение об ошибке и выполняет выход. После создания всех потоков осуществляется выход из основной программы. При создании поток выводит однострочное сообщение, объявляя о своем существовании, после чего осуществляет выход. Порядок, в котором выводятся различные сообщения, не определен и при нескольких запусках программы может быть разным.
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #define NUMBER_OF_THREADS 10 5 void *print_hello_world(void *tid) 6 { 7 /* Эта функция выводит идентификатор потока, а затем осуществляет выход */ 8 printf("Привет, мир. Тебя приветствует поток № %d\n", tid); 9 pthread_exit(NULL); 10 } 11 12 int main(int argc, char *argv[]) 13 { 14 /* Основная программа создает 10 потоков, а затем осуществляет выход. */ 15 pthread_t threadsNUMBER_OF_THREADS]; 16 int status, i; 17 for (i = ; i < NUMBER_OF_THREADS; i++) { 18 printf("Это основная программа. Создание потока № %d\n"", i); 19 status = pthread_create(&threadsi], NULL, print_hello_world, 20 (void *)i); 21 if (status != ) { 22 printf("Жаль, функция pthread_create вернула код ошибки %d\n"", 23 status); 24 exit(-1); 25 } 26 } 27 exit(NULL); 28 }
Версии
Запчасти до 1997 г.
До 1997 года POSIX содержал несколько стандартов:
-
POSIX.1 : Основные службы (включает стандарт ANSI C ) (IEEE Std 1003.1-1988)
- Создание и контроль процесса
-
Сигналы
- Исключения с плавающей точкой
- Нарушения сегментации / памяти
- Незаконные инструкции
- Ошибки автобуса
- Таймеры
- Работа с файлами и каталогами
- Трубы
- Библиотека C (Стандарт C)
- Интерфейс порта ввода / вывода и управление
- Триггеры процесса
-
POSIX.1b : расширения реального времени (IEEE Std 1003.1b-1993, позже появившийся как librt — библиотека расширений реального времени))
- Приоритетное планирование
- Сигналы в реальном времени
- Часы и таймеры
- Семафоры
- Сообщение передается
- Общая память
- Асинхронный и синхронный ввод-вывод
- Интерфейс блокировки памяти
-
POSIX.1c : Расширения потоков (IEEE Std 1003.1c-1995)
- Создание, управление и очистка потоков
- Планирование потоков
- Синхронизация потоков
- Обработка сигналов
-
POSIX.2 : Shell и служебные программы (IEEE Std 1003.2-1992)
- Командный интерпретатор
- Служебные программы
Версии после 1997 г.
После 1997 года Austin Group разработала версии POSIX. Спецификации известны под названием Single UNIX Specification , прежде чем они станут стандартом POSIX после официального утверждения ISO.
POSIX.1-2001 (с двумя TC)
POSIX.1-2001 (или IEEE Std 1003.1-2001) соответствует единой спецификации UNIX версии 3.
Этот стандарт состоял из:
- Базовые определения, выпуск 6,
- Системные интерфейсы и заголовки, выпуск 6,
- Команды и утилиты, выпуск 6.
IEEE Std 1003.1-2004 включал небольшое обновление POSIX.1-2001. В него включены два незначительных обновления или исправления, называемые техническими исправлениями (TC). Его содержимое доступно в сети.
POSIX.1-2008 (с двумя TC)
Базовые спецификации, выпуск 7 (или IEEE Std 1003.1-2008 , издание 2016 г.) аналогичны текущей версии 2017 г. (по состоянию на 22 июля 2018 г.).
Этот стандарт состоит из:
- Базовые определения, выпуск 7,
- Системные интерфейсы и заголовки, выпуск 7,
- Команды и утилиты, выпуск 7,
- Обоснование объема.
POSIX.1-2017
IEEE Std 1003.1-2017 (редакция IEEE Std 1003.1-2008) — Стандарт IEEE для информационных технологий — интерфейс переносимой операционной системы (POSIX (R)). Базовые спецификации, выпуск 7 доступен либо в Open Group, либо в IEEE, начиная с 22 июля 2018 г. действующий стандарт. Технически он идентичен POSIX.1-2008 с внесенными техническими исправлениями 1 и 2. Бесплатная онлайн-копия все еще может быть доступна.
Блокировка чтения-записи
Первый механизм, который мы рассмотрим это (блокировка чтения/записи). Фактически он, как и мьютекс позволяет организовывать взаимоисключение при доступе к общим данным, однако он в отличие от мьютекса позволяет учитывать то, модифицируем ли мы данные или же только читаем их. Действительно, одновременный доступ по чтению к общим данным из разных потоков выполнения не вызывает никаких проблем, и следовательно вполне логично было бы допускать до одновременной работы с общими данными несколько нитей, которые только читают, или же одну нить, которая модифицирует общие данные . Именно такую логику и реализует блокировку чтения/записи, он позволяет нескольким нитям одновременно читать общие данные, при условии, что нет нити, которая модифицирует их, и допускает только одну нить до модификации данных. При захвате блокировки чтения/записи нить обязана указать, что она будет только читать данные или желает модифицировать их. То есть здесь в отличие от мьютекса появляются два типа захватов – захват на чтение и захват на запись. Часто соответствующие два типа доступа называют также разделяемый доступ (по чтению) и эксклюзивный (исключительный) доступ (по записи). При этом если нить нарушит свои обещания, то общие данные могут испортиться, например, может нарушиться какой либо из инвариантов, что естественно может привести к ошибке. Блокировки чтения/записи целесообразно использовать в тех ситуациях, когда мы часто читаем общие данные и сравнительно редко модифицируем их. Типичным примером такого рода данных может служить кэш, из которого в основном читают данные, а обновляют его только если какие-то данные отсутствуют в нем.
Итак, в случае если нить пытается захватить блокировку на чтение и та не захвачена сейчас на запись (либо она свободна, либо уже захвачена на чтение) то захват ей удается и она продолжает свое выполнение, если же блокировка уже захвачена на запись то она ожидает ее освобождения. Если же нить хочет захватить блокировку на запись, то есть в эксклюзивном режиме, то она ожидает освобождения блокировки если та захвачена на чтение или на запись, и получает захват только если блокировка полностью свободна. Возникает вопрос кто получает блокировку если та освобождается и одновременно есть и ждущие писатели и ждущие читатели? Возможно, два варианта в случае если будет удовлетворена блокировка писателя, то можно говорить о предпочтении писателей, если же блокировку получат читатели, то можно говорить о предпочтении читателей. Оба варианта вполне разумны и полезны в разных ситуациях, первый, например, предпочтителен, в случае если мы хотим чтобы наши данные как можно скорее обновились и именно обновленными данными пользовались читатели, а второй удобен когда наша цель приоритетное обслуживание читателей а актуальность данных не так уж важна.
1 int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
1 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
При этом согласно стандарту блокировка будет установлена, если нет активной блокировки по записи и нет нитей ожидающих возможности установки блокировки записи. В других случаях нить будет спать до появления возможности установить блокировку. Таким образом, блокировки чтения/записи предлагаемые стандартом это блокировки с предпочтением писателей, так как читатели всегда будут стараться уступать им дорогу. Установить блокировку записи можно при помощи функции:
1 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
Эта функция как и следует ожидать устанавливает блокировку записи в случае, если для данного (блокировка чтения/записи) не установлено никаких других блокировок, в остальных случаях вызвавшая ее нить будет ожидать освобождения блокировки. Нить может снять любую установленную ею ранее блокировку при помощи функции:
1 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
При этом, поскольку в данный момент времени для данной блокировки чтения/записи данная нить может обладать либо только правом чтения, либо правом записи, то при снятии блокировки всегда известно, какого именно типа блокировку мы сейчас снимаем, и соответственно эта функция не нуждается в вариантах. И, наконец, как и в случае мьютекса ресурсы занимаемые свободной блокировкой чтения/записи могут быть освобождены при помощи функции:
1 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
Причины использования потоков
В традиционных операционных системах у каждого процесса есть адресное пространство и единственный поток управления. Фактически это почти что определение процесса. Тем не менее нередко возникают ситуации, когда неплохо было бы иметь в одном и том же адресном пространстве несколько потоков управления, выполняемых квазипараллельно, как будто они являются чуть ли не обособленными процессами (за исключением общего адресного пространства).
Основная причина использования потоков заключается в том, что во многих приложениях одновременно происходит несколько действий, часть которых может периодически быть заблокированной. Модель программирования упрощается за счет разделения такого приложения на несколько последовательных потоков, выполняемых в квазипараллельном режиме. Рассматривая потоки, мы добавляем новый элемент: возможность использования параллельными процессами единого адресного пространства и всех имеющихся данных. Эта возможность играет весьма важную роль для тех приложений, которым не подходит использование нескольких процессов (с их раздельными адресными пространствами).
Вторым аргументом в пользу потоков является легкость (то есть быстрота) их создания и ликвидации по сравнению с более «тяжеловесными» процессами. Во многих системах создание потоков осуществляется в 10–100 раз быстрее, чем создание процессов. Это свойство особенно пригодится, когда потребуется быстро и динамично изменять количество потоков.
Третий аргумент в пользу потоков также касается производительности. Когда потоки работают в рамках одного центрального процессора, они не приносят никакого прироста производительности, но когда выполняются значительные вычисления, а также значительная часть времени тратится на ожидание ввода-вывода, наличие потоков позволяет этим действиям перекрываться по времени, ускоряя работу приложения.
И наконец, потоки весьма полезны для систем, имеющих несколько центральных процессоров, где есть реальная возможность параллельных вычислений.
Обзор
Unix был выбран в качестве основы для стандартного системного интерфейса отчасти потому, что он был «нейтральным к производителю». Однако существовало несколько основных версий Unix, поэтому возникла необходимость в разработке системы общего знаменателя. Спецификации POSIX для Unix-подобных операционных систем изначально состояли из единого документа для основного интерфейса программирования , но со временем выросли до 19 отдельных документов (POSIX.1, POSIX.2 и т. Д.). Стандартизированная пользовательская командная строка и были основаны на оболочке UNIX System V. Многие программы, службы и утилиты пользовательского уровня (включая awk , echo , ed ) также были стандартизированы вместе с необходимыми службами программного уровня (включая базовый ввод-вывод : файл , терминал и сеть ). POSIX также определяет стандартный API библиотеки потоков, который поддерживается большинством современных операционных систем. В 2008 году большинство частей POSIX были объединены в единый стандарт (IEEE Std 1003.1-2008 , также известный как POSIX.1-2008).
По состоянию на 2014 год документация POSIX разделена на две части:
- POSIX.1, издание 2013 г .: базовые определения POSIX, системные интерфейсы, команды и служебные программы (включая POSIX.1, расширения для POSIX.1, службы реального времени, интерфейс потоков, расширения в реальном времени, интерфейс безопасности, доступ к сетевым файлам) и межпроцессная связь между процессами в сети, расширения переносимости пользователей, исправления и расширения, служебные программы защиты и управления и служебные программы пакетной системы. Это POSIX 1003.1-2008 с техническим исправлением 1.)
- Тестирование на соответствие POSIX: Набор тестов для POSIX сопровождает стандарт: VSX-PCTS или VSX POSIX Conformance Test Suite .
Разработка стандарта POSIX происходит в Austin Group (совместная рабочая группа IEEE, The Open Group и ISO / IEC JTC 1 ).
Барьеры
Второй механизм синхронизации, про который хотелось бы поговорить, это так называемый барьер (barrier). Что он делает? – останавливает нити. Точнее говоря, при инициализации барьера задается какое количество нитей он должен остановить, соответственно, нити преодолевающие барьер (дошедшие до него) не могут продолжать свое выполнение пока на этот барьер не наткнется указанное при инициализации количество нитей. Барьеры предназначены для того чтобы держать нити — члены определенной группы вместе. Наткнувшись на барьер, нить из группы не сможет продолжать выполнение, пока все члены группы не дойдут до барьера. Обычно барьеры используют для того, чтобы все нити выполняющие какой-то параллельный алгоритм достигли определенной его точки прежде чем какая-либо из них двинулась дальше.
Стандарт POSIX поддерживает барьеры в качестве опции. В программе они представляются при помощи непрозрачного типа , объекты которого можно инициализировать при помощи вызова функции:
1 int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t * attr, unsigned count);
Где последний параметр count задает число нитей, которые должны дойти до барьера (вызвать функцию ) прежде чем они смогут продолжить выполнение. Раз существует функция инициализирующая барьер, то существует и комплиментарная функция которая уничтожает его:
1 int pthread_barrier_destroy(pthread_barrier_t *barrier);
Как и в случае остальных примитивов синхронизации данная функция должна вызываться только в случае если на уничтожаемом барьере не ожидает ни одной нити. Ну и наконец существует функция вызов которой собственно и соответствует преодолению барьера:
1 int pthread_barrier_wait(pthread_barrier_t *barrier);
Как сказано в стандарте она синхронизирует выполнение нитей участников барьера. Нить вызвавшая ее блокируется до тех пор пока count нитей не вызовут ее для данного барьера. При возврате из в одной из нитей преодолевших барьер будет возвращено отличное от нуля значение , что позволяет выделить единственную нить в которой выполнять последовательный код (смотри наш пример с суммированием). После возврата всех нитей из барьер приходит в исходное состояние, то есть оказывается снова готов к использованию.
Задачи
- содействовать облегчению переноса кода прикладных программ на иные платформы;
- способствовать определению и унификации интерфейсов заранее при проектировании, а не в процессе их реализации;
- сохранять по возможности и учитывать все главные, созданные ранее и используемые прикладные программы;
- определять необходимый минимум интерфейсов прикладных программ, для ускорения создания, одобрения и утверждения документов;
- развивать стандарты в направлении обеспечения коммуникационных сетей, распределенной обработки данных и защиты информации;
- рекомендовать ограничение использования бинарного (объектного) кода для приложений в простых системах.
Споры
512- и 1024-байтовые блоки
POSIX требует 512-байтовых размеров блоков по умолчанию для утилит df и du , что отражает типичный размер блоков на дисках. Когда Ричард Столмен и команда GNU внедряли POSIX для операционной системы GNU , они возражали против этого на том основании, что большинство людей думают в терминах блоков размером 1024 байта (или 1 КиБ ). Переменная среды POSIX_ME_HARDER была введена, чтобы позволить пользователю принудительно установить поведение, соответствующее стандартам. Позднее имя переменной было изменено на POSIXLY_CORRECT . Эта переменная теперь также используется для ряда других особенностей поведения.
Поддержка операционными системами
В зависимости от степени совместимости со стандартами, ОС могут быть полностью или частично совместимы с POSIX. Сертифицированные продукты могут быть найдены на сайте IEEE. Бесплатный доступ к версии стандарта IEEE Std 1003.1-2004 находится на сайте «The open group».
POSIX-сертифицированные
C 1996 года стандарт SUS включает в себя стандарт POSIX, поэтому сертифицированные на стандарт UNIX операционные системы можно считать одновременно сертифицированными и на стандарт POSIX.
- Mac OS X (UNIX 03)
- Solaris (UNIX 03)
- IBM AIX (UNIX 03)
- HP-UX (UNIX 03)
- QNX Neutrino (POSIX PSE52)
- IRIX (UNIX 95)
- Integrity (POSIX 1003.1-2003) и её микроядро µ-velosity
- Inspur K-UX (UNIX 03)
- LynxOS (POSIX 1003.1-1996)
- UnixWare (UNIX 95)
- Tru64 UNIX (UNIX 95).
POSIX-совместимые
Заявлена полная совместимость с одной из версий стандарта POSIX.
- A/UX
- FreeBSD
- BSD/OS
- Minix
- MPE/iX
- OpenSolaris
- OpenVMS (POSIX 1003.1-1990, 1003.1b, 1003.4)
- VxWorks (POSIX PSE52).
По большей части POSIX-совместимые
Официально не сертифицированные как POSIX-совместимые, но соответствующие по большей части.
- BeOS
- Linux (большинство дистрибутивов — см. LSB)
- NetBSD
- Nucleus RTOS
- OpenBSD
- Sanos
- SkyOS
- Syllable
- Symbian OS (при помощи PIPS)
- DragonFlyBSD
- RTEMS (POSIX 1003.1b-1993, POSIX 1003.1h/D3)
- QNX Neutrino (POSIX 1003.1-2004)
POSIX для Windows
- Cygwin — обеспечивает частичное соответствие POSIX для некоторых продуктов Microsoft Windows.
- Подсистема для UNIX-приложений (SUA) — необязательная подсистема Windows, обеспечивающая готовое окружение для компиляции и исполнения POSIX-совместимых приложений. Соответствие стандарту POSIX.2 на уровне командной строки обеспечивается с помощью командной оболочки ksh.
- «Microsoft Windows Services for UNIX» — обеспечивает полное соответствие POSIX для некоторых продуктов Microsoft Windows. Операционные системы на базе Windows NT до Windows 2000 имели POSIX-уровень, встроенный в ОС, и «UNIX Services for Windows» предоставляло UNIX-подобное окружение. Для получения POSIX-совместимости в Windows XP нужно скачать и установить бесплатный пакет «Windows Services for UNIX». UNIX-подсистема встроена в Enterprise и Ultimate редакции Windows Vista и не может быть добавлена в другие редакции.
- UWIN — набор библиотек и утилит, обеспечивающих POSIX-совместимое окружение поверх Win32 API.
- UnxUtils — набор портированных под библиотеку Microsoft C Run-time (msvcrt.dll) утилит GNU.
История развития стандарта POSIX[2]
Первая версия спецификации IEEE Std 1003.1 была опубликована в 1988 г. В последующем многочисленные редакции IEEE Std 1003.1 были приняты как международные стандарты.
Этапы развития POSIX:
Год | Этап |
---|---|
1990 г. | Редакция, выпущенная в 1988 г., была переработана и стала основой для дальнейших редакций и дополнений. Она была одобрена как международный стандарт ISO/IEC 9945-1:1990. |
1993 г. | Выходит редакция 1003.1b-1993. |
1995 г. | Опубликована редакция 1003.1c-1995 (входит как часть в стандарт ANSI/IEEE POSIX 1003.1-1995). |
1996 г. | Внесены изменения IEEE Std 1003.1b-1993, IEEE Std 1003.1c-1995 и 1003.1i-1995, однако основная часть документа осталась неизменной. В 1996 г. редакция IEEE Std 1003.1 также была одобрена как международный стандарт ISO/IEC 9945-1:1996. |
1998 г. | Появился первый стандарт для «реального времени» – IEEE Std 1003.13-1998. Это расширение стандарта POSIX для встраиваемых приложений реального времени. |
1999 г. | Принято решение внести в основной текст стандарта первые за последние 10 лет существенные изменения, включая объединение со стандартом 1003.2 (Shell и утилиты), так как к тому моменты это были отдельные стандарты. PASC решил закончить изменения базового текста после завершения работы над стандартами IEEE 1003.1a, 1003.1d, 1003.1g, 1003.1j, 1003.1q и 1003.2b. |
2001 г. | Опубликована новая редакция IEEE Std 1003.1. Также было принято решение о приведении остальных проектов в соответствие с новым документом. |
2003 г. | 31 марта опубликована новая редакция стандарта 1003.1. |
2004 г. | Последняя на сегодняшний день редакция стандарта 1003.1 была опубликована 30 апреля и выпущена под эгидой Austin Common Standards Revision Group. В нее внесены изменения, касающиеся редакции стандарта 2001 г. Формально редакция 2004 г. известна как IEEE Std 1003.1, 2004 Edition, The Open Group Technical Standard Base Specifications, Issue 6 и включает IEEE Std 1003.1-2001, IEEE Std 1003.1-2001/Cor 1-2002 и IEEE Std 1003.1-2001/Cor 2-2004. |