Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 186 187 188 189 190 191 192 193 194 ... 263
Перейти на страницу:

Однако общими становятся не только глобальные переменные. Все потоки одного процесса разделяют:

■ инструкции процесса;

■ большую часть данных;

■ открытые файлы (например, дескрипторы);

■ обработчики сигналов и вообще настройки для работы с сигналами (действие сигнала);

■ текущий рабочий каталог;

■ идентификаторы пользователя и группы пользователей.

У каждого потока имеются собственные:

■ идентификатор потока;

■ набор регистров, включая счетчик команд и указатель стека;

■ стек (для локальных переменных и адресов возврата);

■ переменная errno;

■ маска сигналов;

■ приоритет.

ПРИМЕЧАНИЕ

Как сказано в разделе 11.18, можно рассматривать обработчик сигнала как некую разновидность потока. В традиционной модели Unix у нас имеется основной поток выполнения и обработчик сигнала (другой поток). Если в основном потоке в момент возникновения сигнала происходит корректировка связного списка и обработчик сигнала также пытается изменить связный список, обычно начинается путаница. Основной поток и обработчик сигнала совместно используют одни и те же глобальные переменные, но у каждого из них имеется свой собственный стек.

В этой книге мы рассматриваем потоки POSIX, которые также называются Pthreads (POSIX threads). Они были стандартизованы в 1995 году как часть POSIX.1c и будут поддерживаться большинством версий Unix. Мы увидим, что все названия функций Pthreads начинаются с символов pthread_. Эта глава является введением в концепцию потоков, необходимым для того, чтобы в дальнейшем мы могли использовать потоки в наших сетевых приложениях. Более подробную информацию вы можете найти в [15].

26.2. Основные функции для работы с потоками: создание и завершение потоков

В этом разделе мы рассматриваем пять основных функций для работы с потоками, а в следующих двух разделах мы используем эти функции для написания потоковой модификации клиента и сервера TCP.

Функция pthread_create

Когда программа запускается с помощью функции exec, создается один поток, называемый начальным (initial) или главным (main). Дополнительные потоки создаются функцией pthread_create.

#include <pthread.h>

int pthread_create(pthread_t* tid, const pthread_attr_t *attr,

 void *(*func)(void*), void *arg);

Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки

Каждый поток процесса обладает собственным идентификатором потока (thread ID), относящимся к типу данных pthread_t (как правило, это unsigned int). При успешном создании нового потока его идентификатор возвращается через указатель tid.

У каждого потока имеется несколько атрибутов: его приоритет, исходный размер стека, указание на то, должен ли этот поток являться демоном или нет, и т.д. При создании потока мы можем задать эти атрибуты, инициализируя переменную типа pthread_attr_t, что позволяет заменить значение, заданное по умолчанию. Обычно мы используем значение по умолчанию, в этом случае мы задаем аргумент attr равным пустому указателю.

Наконец, при создании потока мы должны указать, какую функцию будет выполнять этот поток. Выполнение потока начинается с вызова заданной функции, а завершается либо явно (вызовом pthread_exit), либо неявно (когда вызванная функция возвращает управление). Адрес функции задается аргументом func, и она вызывается с единственным аргументом-указателем arg. Если этой функции необходимо передать несколько аргументов, следует поместить их в некоторую структуру и передать адрес этой структуры как единственный аргумент функции.

Обратите внимание на объявления func и arg. Функции передается один аргумент — универсальный указатель void*. Это позволяет нам передавать потоку с помощью единственного указателя все, что требуется, и точно так же поток возвращает любые данные, используя этот указатель.

Возвращаемое значение функций Pthreads — это обычно 0 в случае успешного выполнения или ненулевая величина в случае ошибки. Но в отличие от функций сокетов и большинства системных вызовов, для которых в случае ошибки возвращается -1 и переменной errno присваивается некоторое положительное значение (код ошибки), функции Pthreads возвращают сам код ошибки. Например, если функция pthread_create не может создать новый поток, так как мы превысили допустимый системный предел количества потоков, функция возвратит значение EAGAIN. Функции Pthreads не присваивают переменной errno никаких значений. Соглашение о том, что 0 является индикатором успешного выполнения, а ненулевое значение — индикатором ошибки, не приводит к противоречию, так как все значения Exxx, определенные в заголовочном файле <sys/errno.h>, являются положительными. Ни одному из имен ошибок Exxx не сопоставлено нулевое значение.

Функция pthread_join

Мы можем приостановить выполнение текущего потока и ждать завершения выполнения какого-либо другого потока, используя функцию pthread_join. Сравнивая потоки и процессы Unix, можно сказать, что функция pthread_create аналогична функции fork, а функция pthread_join — функции waitpid.

#include <pthread.h>

int pthread_join(pthread_t tid, void **status);

Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки

Следует указать идентификатор tid того потока, завершения которого мы ждем. К сожалению, нет способа указать, что мы ждем завершения любого потока данного процесса (тогда как при работе с процессами мы могли с помощью функции waitpid ждать завершения любого процесса, задав аргумент идентификатора процесса, равный -1). Мы вернемся к этой проблеме при обсуждении листинга 26.11.

Если указатель status непустой, то значение, возвращаемое потоком (указатель на некоторый объект), хранится в ячейке памяти, на которую указывает status.

Функция pthread_self

Каждый поток снабжен идентификатором, уникальным в пределах данного процесса. Идентификатор потока возвращается функцией pthread_create и, как мы видели, используется функцией pthread_join. Поток может узнать свой собственный идентификатор с помощью вызова pthread_self.

#include <pthread.h>

pthread_t pthread_self(void);

Возвращает: идентификатор вызывающего потока

Сравнивая потоки и процессы Unix, можно отметить, что функция pthread_self аналогична функции getpid.

Функция pthread_detach

Поток может быть либо присоединяемым (joinable), каким он является по умолчанию, либо отсоединенным (detached). Когда присоединяемый поток завершает свое выполнение, его статус завершения и идентификатор сохраняются, пока другой поток данного процесса не вызовет функцию pthread_join. В свою очередь, отсоединенный поток напоминает процесс-демон: когда он завершается, все занимаемые им ресурсы освобождаются и мы не можем отслеживать его завершение. Если один поток должен знать, когда завершится выполнение другого потока, нам следует оставить последний присоединяемым.

Функция pthread_detach изменяет состояние потока, превращая его из присоединяемого в отсоединенный.

#include <pthread.h>

int pthread_detach(pthread_t tid);

Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки

Эта функция обычно вызывается потоком при необходимости изменить собственный статус в следующем формате:

pthread_detach(pthread_self());

Функция pthread_exit

Одним из способов завершения потока является вызов функции pthread_exit.

#include <pthread.h>

void pthread_exit(void *status);

Ничего не возвращает вызвавшему потоку

Если поток не является отсоединенным, идентификатор потока и статус завершения сохраняются до того момента, пока какой-либо другой поток данного процесса не вызовет функцию pthread_join.

Указатель status не должен указывать на объект, локальный по отношению к вызывающему потоку, так как этот объект будет уничтожен при завершении потока.

Существуют и другие способы завершения потока.

■ Функция, которая была вызвана потоком (третий аргумент функции pthread_create), может возвратить управление в вызывающий процесс. Поскольку, согласно своему объявлению, эта функция возвращает указатель void, возвращаемое ею значение играет роль статуса завершения данного потока.

1 ... 186 187 188 189 190 191 192 193 194 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии