- •Часть 4. Локальное взаимодействие процессов
- •Глава 16. Блокирование записей 89
- •12.2. Процессы, потоки и общий доступ к информации
- •12.3. Живучесть объектов ipc
- •12.4. Пространства имен
- •12.5. Действие команд fork, exec и exit на объекты ipc
- •12.6. Комментарии к примерам ipc
- •12.7. Выводы по главе 12
- •12.8. Упражнения по главе 12
- •Глава 13. Именованные и неименованные каналы
- •13.1. Введение
- •13.2. Приложение типа клиент-сервер
- •13.3. Программные каналы
- •13.4. Функции popen и pclose
- •13.5. Именованные каналы (fifo)
- •13.6. Некоторые свойства именованных и неименованных каналов
- •13.7. Один сервер, несколько клиентов
- •13.8. Последовательные и параллельные серверы
- •13.9. Ограничения программных каналов и fifo
- •13.10. Выводы по главе 13
- •13.11. Упражнения по главе 13
- •Глава 14. Программные потоки
- •14.1. Введение
- •14.2. Концепция потоков
- •14.3. Идентификация потоков
- •14.4. Создание потока
- •14.5. Завершение потока
- •Функции управления процессами и потоками
- •14.6. Установка атрибутов потока
- •14.7. Реентерабельность
- •Альтернативные версии функций, безопасные в многопоточной среде
- •14.8. Локальные данные потоков
- •14.9. Принудительное завершение потоков
- •Некоторые точки выхода, определенные стандартом Posix.1
- •14.10. Потоки и сигналы
- •14.11. Выводы по главе 14
- •14.12. Упражнения по главе 14 Глава 15. Средства синхронизации потоков
- •15.1. Введение
- •15.2. Взаимные исключения: установка и снятие блокировки
- •15.2.1. Схема производитель-потребитель
- •15.2.2. Блокирование и опрос
- •15.2.3. Предотвращение тупиковых ситуаций
- •15.3. Условные переменные
- •15.3.1. Ожидание и сигнализация
- •15.3.2. Исключение состояния гонок
- •15.4. Блокировки чтения-записи
- •15.5. Атрибуты средств синхронизации потоков
- •15.5.1. Атрибуты взаимных исключений
- •Поведение взаимных исключений различных типов
- •15.5.2. Атрибуты условных переменных
- •15.5.3. Атрибуты блокировок чтения-записи
- •15.6. Выводы по главе 15
- •15.7. Упражнения по главе 15
- •Глава 16. Блокирование записей
- •16.1. Введение
- •16.2. Блокирование записей и файлов
- •16.3. Блокирование записей с помощью fcntl по стандарту Posix
- •16.4. Рекомендательная блокировка
- •16.5. Обязательная блокировка
- •16.6. Приоритет чтения и записи Выводы по главе 16
- •Упражнения по главе 16 Глава 17. System V ipc
- •17.1. Введение
- •17.2. Ключи типа key_t и функция ftok
- •17.3. Структура ipc_perm
- •17.4. Создание и открытие каналов ipc
- •17.5. Разрешения ipc
- •17.6. Программы ipcs и ipcrm
- •17.7. Ограничения ядра
- •17.8. Выводы по главе 17
- •17.9. Упражнения по главе 17
- •Глава 18. Очереди сообщений System V
- •18.1. Введение
- •18.2. Функция msgget
- •18.3. Функция msgsnd
- •18.4. Функция msgrcv
- •18.5. Функция msgctl
- •18.6. Пример программы клиент-сервер
- •18.7. Мультиплексирование сообщений
- •18.7.1. Пример: одна очередь на приложение
- •18.7.2. Пример: одна очередь для каждого клиента
- •18.8. Ограничения, накладываемые на очереди сообщений
- •18.9. Выводы по главе 18
- •18.10. Упражнения по главе 18
- •Глава 19. Семафоры System V
- •19.1. Введение
- •19.2. Функция semget
- •19.3. Функция semop
- •19.4. Функция semctl
- •19. . Ограничения семафоров System V
- •19. . Выводы по главе 19
- •19. . Упражнения по главе 19 Глава 20. Введение в разделяемую память
- •20.1. Введение
- •20.2. Функции mmap, munmap и msync
- •20.3. Увеличение счетчика в отображаемом в память файле
- •20.4. Неименованное отображение в память
- •20.5. Обращение к объектам, отображенным в память
- •20.6. Выводы по главе 20
- •20.7. Упражнения по главе 20
- •Глава 21. Разделяемая память System V
- •21.1. Введение
- •21.2. Функция shmget
- •21.3. Функция shmat
- •21.4. Функция shmdt
- •21.5. Функция shmctl
- •21.6. Ограничения, накладываемые на разделяемую память
- •21.7. Выводы по главе 21
- •21.8. Упражнения по главе 21
Функции управления процессами и потоками
|
Процессы |
Потоки |
Описание действий для потоков |
|
fork |
pthread_create |
Создать новый поток управления |
|
exit |
pthread_exit |
Завершить существующий поток управления |
|
waitpid |
pthread_join |
Вернуть код завершения потока управления |
|
atexit |
pthread_cleanup_push |
Зарегистрировать функцию обработки выхода из потока управления |
|
getpid |
pthread_self |
Вернуть идентификатор потока управления |
|
abort |
pthread_cancel |
Запросить принудительное завершение потока управления |
По умолчанию код завершения потока сохраняется до тех пор, пока для этого потока не будет вызвана функция pthread_join. Основная память потока может быть немедленно освобождена по его завершении, если поток был обособлен. Когда поток обособлен, функция pthread_join не может использоваться для получения его кода завершения, в этом случае она возвращает код ошибки EINVAL. Обособить поток можно с помощью функции pthread_detach.
#include <pthread.h>
int pthread_detach (pthread_t tid);
/* функция возвращает 0 в случае успеха, код ошибки – в случае неудачи */
Как мы увидим в следующей главе (разделе), существует возможность создания потока, который изначально находится в обособленном состоянии, изменив атрибуты потока, передаваемые функции pthread_create.
14.6. Установка атрибутов потока
Во всех примерах главы 14, где мы вызывали функцию pthread_create, мы передавали ей значение NULL вместо указателя на структуру pthread_attr_t. Структура pthread_attr_t используется для того, чтобы изменить значения атрибутов по умолчанию и связать эти атрибуты с создаваемым потоком. Для инициализации структуры pthread_attr_t можно обратиться к функции pthread_attr_init. После вызова этой функции структура pthread_attr_t будет заполнена значениями атрибутов по умолчанию, которые поддерживает данная реализация Unix. Для изменения отдельных атрибутов необходимо обратиться к помощи других функций, которые будут описаны в этом разделе. Для разрушения структуры pthread_attr_t используется функция pthread_attr_destroy.
#include <pthread.h>
int pthread_attr_init (pthread_attr_t *attr);
int pthread_attr_destroy (pthread_attr_t *attr);
/* обе функции возвращают 0 в случае успеха, код ошибки – в случае неудачи */
Если функция pthread_attr_init реализована таким образом, что она размещает какие-либо данные в динамически выделенной области памяти, то функция pthread_attr_destroy освободит ее. Кроме того, функция pthread_attr_destroy заполнит структуру ошибочными значениями, чтобы функция pthread_create возвращала ошибку при случайном использовании такой структуры.
Структура pthread_attr_t непрозрачна для приложения. Это означает, что приложение ничего не должно знать о внутреннем содержимом структуры, что способствует повышению переносимости приложений. Следуя этому соглашению, стандарт Posix.1 определяет по две отдельные функции для получения и изменения значений каждого атрибута. Здесь мы упомянем только один атрибут – атрибут обособленности потока (detachstate).
В разделе 14.5 мы уже использовали понятие “обособленный поток”. Если нас не интересует код завершения существующего потока, мы можем обратиться к функции pthread_detach, чтобы позволить операционной системе утилизировать ресурсы, занятые потоком, после его завершения.
Если заранее известно, что код завершения потока не потребуется, то можно сразу же создать и запустить поток в обособленном состоянии, изменив значение атрибута detachstate в структуре pthread_attr_t. Для этой цели предусмотрена функция pthread_attr_setdetachstate, которой передается одно из двух возможных значений – PTHREAD_CREATE_DETACHED, чтобы запустить поток в обособленном состоянии, или PTHREAD_CREATE_JOINABLE, чтобы запустить поток в нормальном состоянии, в котором приложение сможет получить код завершения потока.
#include <pthread.h>
int pthread_attr_getdetachstate (pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);
/* обе функции возвращают 0 в случае успеха, код ошибки – в случае неудачи */
Чтобы получить текущее состояние атрибута detachstate, следует использовать функцию pthread_attr_getdetachstate. По адресу, который передается во втором аргументе, функция запишет одно из двух возможных значений: PTHREAD_CREATE_DETACHED или PTHREAD_CREATE_JOINABLE, в зависимости от значения указанного атрибута в структуре pthread_attr_t.
Пример. В листинге 12.1 приведена функция, которую можно использовать для создания потока в обособленном состоянии.
Листинг 12.1. Создание потока в обособленном состоянии
int makethread(void *(*fn)(void *), void *arg)
{
int err;
pthread_t tid;
pthread_attr_t attr;
err = pthread_attr_init(&attr);
if (err != 0)
return(err);
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err == 0)
err = pthread_create(&tid, &attr, fn, arg);
pthread_attr_destroy(&attr);
return(err);
}
Обратите внимание, что мы игнорируем значение, возвращаемое функцией pthread_attr_destroy. В данном случае мы корректно инициализировали атрибуты потока, поэтому вызов функции pthread_attr_destroy должен быть удачным. Тем не менее, если бы вызов этой функции завершился неудачно, то восстановление после такой ошибки было бы достаточно сложным: мы должны были бы разрушить только что созданный поток, который, возможно, уже работает асинхронно по отношению к этой функции. Самое худшее, что может случиться в случае игнорирования значения, возвращаемого функцией pthread_attr_destroy, – это утечка небольшого объема памяти, который, возможно, был распределен функцией pthread_attr_init. Но в любом случае, если вызов функции pthread_attr_init успешен, а pthread_attr_destroy – неудачен, у нас все равно нет никакой стратегии восстановления после такой ошибки, потому что содержимое структуры с атрибутами потока непрозрачно для приложения. Для освобождения памяти, занимаемой структурой pthread_attr_t, определена одна-единственная функция pthread_attr_destroy, и ее вызов завершился неудачно.
