- •Операционные системы для программиста
- •Введение
- •1. Основные понятия
- •1.1. Понятие операционной системы
- •1.2. Системные соглашения для доступа к функциям ос
- •1.3. Особенности разработки программ в базовых ос
- •1.4. Командный интерфейс пользователя в ос
- •1.5. Информация об ошибках системной функции
- •2. Программный доступ к файловой системе
- •2.1. Понятия дескрипторов, идентификаторов и хэндлов
- •2.2. Ввод и вывод в стандартные файлы.
- •2.3. Базовые средства использования файлов
- •2.4. Многопользовательская блокировка файлов
- •2.5. Установка произвольной позиции в файле
- •3. Принципы построения ос
- •3.1. Модульная структура построения ос
- •3.2. Использование прерываний в ос
- •3.3. Управление системными ресурсами
- •3.4 Строение ядра операционной системы
- •3.5. Структура операционной системы типа Windows nt
- •4. Многофункциональный консольный вывод
- •4.1. Функции управления курсором
- •4.2. Многократный вывод символов и атрибутов
- •4.3. Вывод в произвольную позицию экрана
- •4.4. Ввод данных, размещенных предварительно на экране
- •5. Системные функции ввода для консольных устройств
- •5.1. Системные функции ввода текстовых строк
- •5.2. Событийно-управляемый ввод
- •5.3. Системные функции ввода с клавиатуры
- •5.4. Опрос ввода с клавиатуры в программе
- •5.5. Системные функции мыши для текстового режима
- •6. Файловые системы
- •6.1. Структуры файловых систем для пользователя
- •6.2. Методы распределения внешней памяти
- •6.3. Принципы построения файловых систем типа fat
- •6.4. Современные модификации файловой системы fat
- •6.5. Особенности построения файловой системы hpfs
- •6.6. Принципы построения файловой системы ntfs
- •6.7. Особенности строения файловых систем для Unix
- •6.8. Программный опрос файловой системы
- •7. Обеспечение множественности процессов
- •7.1. Основные понятия теории вычислительных процессов
- •7.2. Программное порождение процессов
- •7.3. Уничтожение процессов
- •7.4. Ожидание завершения процессов
- •8. Многопоточное функционирование ос
- •8.1. Понятие нити и связь Хе с процессом
- •8.2. Создание нитей (thread) в программе
- •8.3. Уничтожение нитей
- •8.4. Приостановка и повторный запуск нити
- •8.5. Ожидание завершения нити
- •9. Средства взаимодействия программных единиц
- •9.1. Абстрактные критические секции
- •9.2. Абстрактные семафоры
- •9.3. Семафоры взаимоисключения
- •9.4. Семафоры событий
- •9.5. Средства группового ожидания
- •9.6. Программные критические секции
- •9.7. Программные семафоры с внутренним счетчиком
- •10. Управление памятью
- •10.1. Виртуальная память
- •10.2. ЏодкРчка страниц для реализациШ виртуальной памяти
- •10.3. Системные функции распределения памяти
- •10.4. Совместное использование памяти
- •10.5. Отображение файлов в оперативную память
- •10.6. Динамически распределяемая память
- •11. Средства коммуникации процессов
- •11.1. Неименованные коммуникационные каналы Unix
- •11.2. Переназначение хэндлов для доступа к каналу
- •11.3. Неименованные каналы в Windows
- •11.4. Именованные каналы в Windows nt
- •11.5. Именованные каналы в Unix
- •12. Взаимодействие пользователя с ос
- •12.1. Интерфейсы операционных систем
- •12.2. Командные и операционные оболочки (shells)
- •12.3. Основные команды базовых операционных систем
- •12.4. Групповое выполнение и фоновый запуск команд
- •12.5. Стандартный ввод-вывод и конвейеры командной строки
- •12.6. Командные файлы и сценарии
- •Библиографический список
8.3. Уничтожение нитей
Одной из немногих операций, которые можно выполнить над нитью как системным объектом, является уничтожение нити. Для уничтожения нити в OS/2 служила системная функция DosKillThread. Эта функция описывалась прототипом
APIRET DosKillThread(TID tid)
и имела единственный аргумент – идентификатор уничтожаемой задачи. Уничтожить можно было только нить того же процесса, но не нить другого.
Для самоуничтожения нити, иными словами прекращения ее работы по инициативе процедуры самой нити в операционных системах Unix, использующих стандарт POSIX, служит функция с прототипом
int pthread_exit(int code),
параметр которой задает код возврата, а для прекращения работы нити по инициативе другой нити служит системная функция с именем pthread_cancel. Она имеет прототип
int pthread_cancel(pthread_t tid),
где в качестве аргумента задается идентификатор нити, ранее полученный от функции создания нити. Останавливаемая нить и нить, вызывающая функцию pthread_cancel, обязательно должны относиться к одному процессу (но никогда к разным процессам!).
Средства управления нитями лучше всего развиты в современных версиях Unix, в частности в Linux. Прекращение работы нити по явному приказу другой нити является довольно решительной мерой, учитывая, что нити процесса выполняют общую работу. Поэтому уничтожение нити в общем случае может иметь непредусмотренный побочный эффект на результаты работы всего процесса. Эти соображения привели в Unix к введению средств, гибко управляющих возможностью уничтожения нити. В настоящее время такие средства состоят из двух вспомогательных функций pthruat_setcanceltype() и pthread_setcancelstate() и состояний нити относительно возможности уничтожениџ. Эти ёостояния называются асинхѐонно отменяемым, синхронно отменяемым и неотменяемым. Асинхронно отменяемое состояние отвечает отсутствию ограничений на уничтожение нити – нить в этом состоянии можно уничтожить в любой момент и в любом месте ее выполнения. Синхронно отменяемая нить может быть уничтожена только тогда, когда она находится в некоторых точках своего выполнения – так называемых точках отмены (cancel points). Нить, находящуюся в неотменяемом состоянии, невозможно уничтожить приказом со стороны, пока она по своей инициативе не сменит это состояние. Переключение между отменяемыми состояниями и неотменяемым производится с помощью функции pthread_setcancelstate(), имеющей прототип
int pthread_setcancelstate(int state, int *oldstate),
где первый аргумент должен задаваться одной из символических констант PTHREAD_CANCEL_DISABLE и PTHREAD_CANCEL_ENABLE, определяющими состояние после его переустановки этой функцией. Второй аргумент в случае неиспользования может задаваться как NULL, в противном случае он передает адрес переменной для записи кода старого состояния отменяемости (представимого этими же символическими константами). Заметим, что рассматриваемая функция действует на ту именно нить, которая выполняет эту функцию и не предоставляет никаких возможностей для смены состояния отменяемости другой нити.
Переключение между асинхронно отменяемым и синхронно отменяемым состояниями нити задается функцией pthread_setcanceltype(), имеющей прототип
int pthread_setcanceltype(int type, int *oldtype),
где первый аргумент должен задаваться одной из символических констант PTHREAD_CANCEL_DEFERRED и PTHREAD_CANCEL_ASYNCHRONOUS, причем первая константа дает состояние именно синхронно отменяемой нити, а второй аргумент служит для возврата предыдущего состояния отменяемости, но может задаваться значением NULL, если такая информация не требуется.
Точка отмены явно создается между двумя последовательными вызовами специальной функции pthread_testcancel(), не имеющей аргументов. Такая конструкция отмечает место процедуры нити, в которой она может быть уничтожена. Кроме явного указания точек отмены, операционная система вводит в программу неявные точки отмены, в которых по усмотрению разработчиков ОС допустимо относительно безболезненное уничтожение нитей. К сожалению, такие точки не всегда достаточно документированы в справочной системе. Рекомендуется искать информацию о них с помощью вызова справки
man pthread_cancel
Точки отмены неявно могут устанавливаться длительно выполняющими функциями ввода-вывода, в частности вывода на экран.
Описанные средства демонстрируются примером программы для Unix, приведенной в листинге 8.3.1.
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
char lbuk[ ]="abcdefghijklmnoprstuwxyz";
pthread_t tid1, tid2, tid3;
void procthread1(void *arg)
{int k, j;
for (k=0; k<24; k++)
{printf("\033[%02d;20H",k+1);
printf("\033[1;34m");
for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);
if (k= =5) {pthread_kill(tid2, SIGSTOP);
printf("-Suspend thread2");}
if (k= =11) {pthread_kill(tid2, SIGCONT);
printf("-Resume thread2");}
printf("\n");
usleep(800000);
}
}
void procthread2(void *arg)
{int k, j;
for (k=0; k<24; k++)
{printf("\033[%02d;40H",k+1);
printf("\033[1;32m");
for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);
if (k= =5) {pthread_cancel(tid3);
printf("-Terminate thread3");}
printf("\n");
usleep(1300000);
}
}
void procthread3(void *arg)
{int k, j;
// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
/// pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
for (k=0; k<24; k++)
{printf("\033[%02d;60H",k+1);
printf("\033[1;31m");
for (j=0; j<(int)arg; j++)
printf("%c",lbuk[k]);
printf("\n");
usleep(1100000);
}
}
int main()
{int k;
int rc;
printf("\033[2J\n");
rc=pthread_create(&tid1, NULL, (void*)procthread1, (void*)2);
rc=pthread_create(&tid2, NULL, (void*)procthread2, (void*)3);
rc=pthread_create(&tid3, NULL, (void*)procthread3, (void*)4);
for (k=0; k<24; k++)
{printf("\033[%02d;1H",k+1);
printf("\033[1;37m");
printf("%c++",lbuk[k]);
printf("\n"); usleep(1000000);
}
pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL);
printf("\033[0m");
return 0;
}
Листинг 8.3.1 Уничтожение нити по явному приказу другой нити в Linux
В этой программе вторая нить, реализующая действия процедуры procthread2 на пятом шаге (при k=5), отдает приказ на уничтожение третьей нити (нити с процедурой procthread3).
Если убрать символы комментариев перед первым оператором третьей нити, то вызов функции pthread_setcancelstate() в этом операторе будет переключать эту нить в состояние неотменяемости, и при запуске программы можно наблюдать, что приказ на уничтожение нити в действительности не выполняется. Попытки же перевести указанную нить в состояние синхронной отменяемости не имеют явного эффекта из-за того, что операторы printf вывода на экран неявно создают точки отмены, на которых и происходит прекращение работы этой нити по приказу на ее уничтожение.
В операционных системах Windows для уничтожения процессов служит системная функция TerminateThread, которая имеет прототип
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode).
Код возврата из завершившейся или принудительно прекращенной приказом TerminateThread нити может быть получен в другой нити того же процесса путем вызова вспомогательной функции GetExitCodeThread, которая имеет прототип
BOOL GetExitCodeThread(HANDLE hThread, DWORD *pExitCode).
Если задача, указанная аргументом hThread, в момент запроса кода возврата еще работает, то функция возвращает значение STILL_ACTIVE в качестве значения второго аргумента. Код возврата нормально завершающейся нити формирует функция завершения ExitThread, которая имеет прототип
void ExitThread(DWORD dwExitCode).
Обычно функцию уничтожения потока используют только тогда, когда управление потоком утеряно и он ни на что не реагирует.
Аналогично рассмотренным выше примерам, функцию уничтожения нити в операционных системах Windows можно продемонстрировать изменением программы из листинга 8.2.2, вставив оператор
if (k= =5) TerminateThread(hthread3, 0)
после операторов
for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);
в процедуре procthread2.
