Так и живем, не пропустив ни дня,
И каждый день проходит словно дважды.
Стандарт POSIX допускает различные подходы к реализации многопоточности в рамках одного процесса. Возможны три основных подхода:
Как правило, при этом количество пользовательских нитей в процессе может превосходить количество системных нитей.
Реализация планировщика в пользовательском адресном пространстве не представляет
больших сложностей; наброски реализаций таких планировщиков приводятся во многих
учебниках по операционным системам, в том числе в [Иртегов 2002]. Учебная многозадачная
ОС
При практическом применении такого планировщика, однако, возникает серьезная проблема.
Если какая-то из нитей процесса исполняет
Наиболее известная реализация пользовательских нитей – это волокна (fibers) в Win32. Считается, что волокна дешевле системных нитей Win32, хотя данных, подтверждающих это утверждение практическими измерениями, у меня нет. Волокна могут использоваться совместно с системными нитями Win32 , но при этом волокна привязаны к определенной нити и исполняются только в контексте этой нити. Волокна не должны исполнять блокирующиеся системные вызовы; попытка сделать это приведет к блокировке нити. Некоторые системные вызовы Win32 имеют неблокирующиеся аналоги, предназначенные для использования в волокнах, но далеко не все. Это резко ограничивает применение волокон в реальных приложениях.
Ядро типичной современной ОС уже имеет планировщик, способный переключать процессы. Переделка этого планировщика для того, чтобы он мог переключать несколько нитей в пределах одного процесса, также не представляет больших сложностей. При этом возможны два подхода к такой переделке.
В рамках первого подхода, системные нити выступают как подчиненная по отношению к
процессу сущность. Идентификатор нити состоит из
Solaris 10 использует системные нити, так что каждой нити POSIX Threads API соответствует
собственный
В рамках другого подхода, системные нити являются сущностями того же уровня, что процесс. Иногда все, что объединяет нити одного процесса – это общее адресное пространство. Наиболее известная ОС, использующая такой подход – Linux. В Linux, нити выглядят как отдельные записи в таблице процессов и отдельные строки в выводе команд top(1) и ps(1), имеют собственный идентификатор процесса.
В старых версиях Linux это приводило к своеобразным проблемам при реализации POSIX Threads API; так, в большинстве Unix-систем завершение процесса системным вызовом exit(2) приводит к немедленному завершению всех его нитей; в Linux вплоть до 2.4 завершалась только текущая нить. В Linux 2.6 был внесен ряд изменений в ядро, приблизивших семантику многопоточности к стандарту POSIX. Эти изменения известны как NPT (Native POSIX Threads).
Наш курс рассчитан на стандартную семантику POSIX Threads API. При программировании для старых (2.4 и младше) версий ядра Linux необходимо изучить особенности поведения этих систем по документации, поставляющейся с системой, или по другим источникам.
В гибридной реализации многопоточный процесс имеет несколько
Причина, по которой этот подход нашел практическое применение – это убеждение разработчиков первых многопоточных версий Unix, что пользовательские нити дешевле системных, требуют меньше ресурсов для своего исполнения.
При планировании пользовательских нитей возникает проблема блокирующихся системных
вызовов. Когда какая-то нить вызывает
Таким образом, если все пользовательские нити исполняли блокирующиеся системные вызовы,
то количество
В старых версиях Solaris поддерживался довольно сложный API, позволявший управлять
количеством
Большинство систем, реализующих POSIX Threads, требуют сборки многопоточной программы
с библиотекой libpthread.so или libpthread.a. Как правило, это достигается запуском
компилятора с ключом -lpthread.
Большинство C и C++ компиляторов интерпретируют ключ -l следующим образом. К
параметру ключа (у ключа -lpthread параметром является строка pthread) спереди
добавляется строка lib, а сзади – строка .so или .a, в зависимости от того, какой режим
сборки задан другими ключами – статический или динамический. Таким образом
получается строка libpthread.so. Затем файл с таким именем ищется в каталогах,
перечисленных в переменной среды LIBPATH. Если эта переменная не установлена,
используются каталоги /lib, /usr/lib, и, возможно, некоторые другие каталоги, зашитые в
компилятор.
Так, компилятор Sun Studio 11 при установке по умолчанию ищет дополнительные
библиотеки в каталоге /opt/SUNWspro/lib.
Компилятор GCC, входящий в поставку Solaris 10, ищет дополнительные библиотеки в
каталоге /usr/sfw/lib ; в действительности, при сборке GCC ему можно указать пути к
дополнительным библиотекам; при сборке GCC из исходных текстов по умолчанию он
настраивается на размещение драйвера компилятора (команд gcc и g++) в /usr/local/bin, а
библиотек – в каталоге /usr/local/lib. Эти каталоги можно изменять параметрами скрипта
configure, который необходимо запустить перед началом сборки компилятора.
В Solaris 10 ключ -lpthread использовать не обязательно – все функции POSIX Thread API
включены в стандартную библиотеку языка C libc.so, которая подключается по умолчанию.
Для совместимости со старыми сборочными скриптами в поставку Solaris 10 также включена
пустая библиотека libpthread.so, содержащая ссылки на соответствующие функции в libc.so.
В некоторых дистрибутивах Linux, библиотека libstdc++.so (стандартная библиотека
языка С++) содержит ссылки на функции libpthread.so. Чтобы облегчить сборку
однопоточных программ с такой библиотекой, в библиотеку libstdc++.so были включены
пустые функции, одноименные функциям POSIX Thread API. При сборке
многопоточных программ с такой библиотекой необходимо обязательно указывать ключ -lpthread. Без этого ключа программа соберется (редактор связей не выдаст сообщений о
неопределенных символах), но работать не будет (вызов функций POSIX Thread API
приведет к ошибке сегментации).
Кроме того, многие компиляторы – в том числе и Sun Studio 11 – рекомендуют компилировать
все модули, входящих в многопоточную программу, с ключом -mt. В старых системах это
могло быть жизненно необходимо, так как в зависимости от наличия или отсутствия этого
ключа компилятор мог подключать разные версии стандартной библиотеки времени
исполнения. В Solaris 10 этот ключ отвечает только за определение некоторых
препроцессорных символов. Впрочем, в следующем разделе мы увидим, что некоторые эти
символы также важны при сборке некоторых программ.
Кроме того, ключ -mt может выключать некоторые оптимизации, опасные при многопоточном
исполнении. Поэтому если компилятор поддерживает ключ -mt, рекомендуется его
использовать как при компиляции, так и при сборке многопоточных программ.
Компилятор GCC не поддерживает ключ -mt, вместо этого рекомендуется использовать ключ
-threads или -pthread на тех платформах, где эти ключи поддерживаются. GCC, входящий в
поставку Solaris 10, поддерживает ключи -threads и –pthread ; GCC из поставки Debian Sarge
поддерживает только ключ -pthread.
Ниже приводится пример кода, который позволяет протестировать ваш компилятор и
проверить наличие типичных препроцессорных символов, используемых в include-файлах
стандартной библиотеки языка С для проверки того, однопоточная или многопоточная
программа сейчас компилируется. _LIBC_REENTRANT используется в Linux, _REENTRANT в
Solaris. На других платформах могут использоваться другие символы. Попробуйте собрать эту
программу с разными ключами компилятора и проверить результат. Полезно также поискать
соответствующие символы в файлах каталога /usr/include и посмотреть, какие именно
конструкции они контролируют.
int main() {
#if defined(_LIBC_REENTRANT)
printf("_LIBC_REENTRANT defined as %d\n", _LIBC_REENTRANT);
#endif
#if defined(_REENTRANT)
printf("_REENTRANT defined as %d\n", _REENTRANT);
#endif
#if defined(_TS_ERRNO)
printf("_TS_ERRNO defined as %d\n", _TS_ERRNO);
#endif
#if defined(_POSIX_C_SOURCE)
printf("_POSIX_C_SOURCE defined as %d\n", _POSIX_C_SOURCE);
#endif
}
Получите вывод препроцессора для программы примера 2.2 с различными ключами компиляции.
Вывод препроцессора у большинства компиляторов С (в том числе у компиляторов Sun Studio
11 и GCC) генерируется ключом -E и выдается в stdout. Для того, чтобы перенаправить вывод
препроцессора в файл, используйте переназначение ввода-вывода. Можно также использовать
для просмотра вывода компилятора фильтры more(1) или less(1).
Напоминаю, что переменная errno хранит код ошибки последнего неудачно завершенного
системного вызова. Код ошибки EMFILE означает исчерпание лимита дескрипторов файлов на
процесс. Таким образом, функция open_with_wait пытается открыть файл и, если лимит
дескрипторов исчерпан, блокирует нить в надежде, что какая-то другая нить освободит
дескриптор. Функции pthread_cond_wait(3C), pthread_cond_signal(3C), pthread_mutex_lock(3C), pthread_mutex_unlock(3C) и используемые ими типы данных изучаются далее в нашем курсе.
Посмотрите, в какой код превращается обращение к переменной errno. Посмотрите, как этот
код зависит от используемых ключей компиляции. Найдите в файле /usr/include/errno.h
макроопределения, ответственные за эту замену. От каких предопределенных символов
препроцессора они зависят?
Подумайте, к чему привело бы в многопоточной программе обращение к переменной errno как
к обычной переменной. Можно ли, как это рекомендуется в некоторых старых руководствах по
Unix, описывать переменную errno как extern int errno, или следует обязательно использовать
определение из файла /usr/include/errno.h?
pthread_cond_t fileopen_cond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t fileopen_mutex=PTHREAD_MUTEX_INITIALIZER;
int open_with_wait(const char *pathname, int flags, mode_t mode) {
int code;
pthread_mutex_lock(fileopen_mutex);
do {
code=open(pathname, flags, mode);
if (code < 0 errno==EMFILE) {
pthread_cond_wait(fileopen_cond, fileopen_mutex);
}
} while (code < 0 errno==EMFILE);
pthread_mutex_unlock(fileopen_mutex);
return code;
}
int close_with_wakeup(int handle) {
int code;
code=close(handle);
pthread_cond_signal(fileopen_cond);
return code;
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.