Последовательность системных вызовов при работе с socket. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Последовательность системных вызовов при работе с socket.



Механизм программных гнезд (Sockets), вообще говоря, позволяет любому процессу обмениваться сообщениями с любым другим процессом, независимо от того, выполняются они на одном компьютере или на разных, соединенных сетью. Программные гнезда входят в число обязательных компонентов стандартной среды ОС UNIX, однако реализуются в разных системах по-разному. В BSD-ориентированных системах Sockets исторически реализуются в ядре ОС, и пользователям предоставляются пять специальных системных вызовов: socket, bind, listen, connect и accept.

socket-создает гнездо

bind-присваивает гнезду имя (привязка)

listen-задает длину очереди (в этой очереди клиентские сообщения ждут когда их будут обрабатывать)

accept-принимает запрос на соединение от клиентского гнезда

connect-посылает запрос на соединение с серверным гнездом

send, sendto-передает сообщение к удаленному гнезду

recv, recvto-принимает сообщения из гнезда

shutdown-закрывает гнездо

Потоки стандарта Posix.1c

Поток состоит из следующих элементов:

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

• динамического стека;

• набора регистров (счетчик команд, указатель стека);

• сигнальной маски;

• значения приоритета;

• специальной памяти.

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

Потоку при создании назначается соответствующая функция. Поток завершается, когда эта назначенная функция возвращает результат или когда поток вызывает функцию pthread_exit. Когда в процессе создается первый поток, то фактически создаются два потока: один - для выполнения указанной функции, а другой - для продолжения выполнения процесса. Последний поток завершается, когда функция main возвращает результат или он сам вызывает функцию pthread_exit.

Все потоки в процессе совместно используют одни сегменты данных и кода. Когда один поток записывает данные в глобальные переменные в процессе, остальные потоки сразу же видят эти изменения. Если какой-либо поток вызывает функцию exit или exec, то завершаются все потоки и сам процесс. Поэтому завершающийся поток, если он не хочет разрушить процесс, в котором выполняется, должен вызывать функцию pthread_exit.

Поток может изменить свою сигнальную маску с помощью функции pthread_sigmask. Сигнал, передаваемый процессу, получат все потоки, которые не замаскировали его. Поток может посылать сигналы в другие потоки этого же процесса, используя функцию pthread_kill, но не может посылать сигналы в потоки другого процесса, так как уникальность идентификаторов потоков не распространяется на другие процессы.

Потоку присваивается целочисленное значение приоритета. Чем больше это значение, тем чаще планируемое выполнение потока. Значение приоритета потока можно запросить с помощью функции pthread_attr_getschedparam и изменить с помощью функции pthread_attr_setschedparam. Поток может намеренно передать выполнение другим потокам с таким же приоритетом; для этого используется функция sched_yield. Кроме того, поток может ожидать завершения другого потока и получить его код возврата с помощью функции pthread_join.

Потоки бывают присоединяемые (joinable) и отсоединенные (detached). Различия между ними заключаются в управлении ресурсами по завершению потока. Присоединяемый поток после своего завершения не освобождает память до вызова pthread_join, который освобождает всю занимаемую память (стек и т.п.) и возвращает результат выполнения потока. Отсоединенный поток этого не требует – все ресурсы возвращаются системе непосредственно по его завершению, узнать результат в таком случае невозможно ввиду того, что нет переменной, в которую этот результат может быть сохранен.

 

27) Системный вызов pthread_create.

#include <pthread.h>

int pthread_create(pthread_t * thread, pthread_attr_t *attr, void* (*start_routine)(void*), void * arg);

Эта функция создаёт новый поток для выполнения функции, адрес которой задан аргументом start_routine. Функция указанная в этом аргументе должна принимать один входной параметр типа void* и возвращать данные такого же типа. Фактический аргумент, который передается в start_routine функцию в начале выполнения нового потока, указывается в аргументе arg.

Идентификатор нового потока возвращается через аргумент thread. Если этому аргументу присвоено значение NULL, идентификатор не возвращается. Аргумент attr содержит атрибуты, присваиваемые вновь создаваемому потоку. Значение этого аргумента может быть равно NULL, если новый поток должен использовать атрибуты по умолчанию, или адресу объекта содержащего атрибуты. В POSIX.1c определяется набор API для создания, удаления, запроса и установки атрибутов потоков – это функции семейства pthread_attr_init. Объект, содержащий атрибуты, может быть связан с несколькими потоками, чтобы при каждом обновлении атрибутов сделанные изменения распространялись на все потоки, связанные с этим объектом.

 

28)Системные вызовы pthread_self,exit и sched_yield

#include <pthread.h>

pthread_t pthread_self(void);

Возвращает идентификатор потока самому потоку.

#include <pthread.h>

void pthread_exit(void *retval);

Завершает поток. Значение аргумента retval – адрес статической переменной, которая содержит код возврата завершаемого потока. Если никакой другой поток не будет использовать код завершающегося потока, значение переменной retval можно указать как NULL.

#include <sched.h>

int sched_yield(void);

Поток вызывает функцию sched_yield, чтобы передать выполнение другим потокам с таким же приоритетом. При успешном выполнении данная функция возвращает 0, а в случае неудачи –1.

 

29) Системные вызовы pthread_join kill

#include <pthread.h>

int pthread_join(pthread_t th, void **thr_return)

Вызывается для ожидания завершения потока. Возвращает код возврата потока, переданный через вызов функции pthread_exit.

 

#include <pthread.h>

int pthread_kill(pthread_t thread, int signo);

Посылает сигнал, заданный аргументом signo, в поток, идентификатор которого задан аргументом thread. Передающий и принимающий потоки должны находиться в одном процессе.

 

Взаимоисключающие блокировки. Пример тупиковой ситуации.

Взаимоисключающие блокировки (mutex – сокращение от MUTual EXclusion) позволяют организовать выполнение потоков так, что когда несколько потоков пытаются установить такую блокировку, успеха достигает только один из них, который и продолжает выполняться. Остальные потоки блокируются до тех пор, пока эта блокировка не будет снята потоком владельцем. При снятии взаимоисключающие блокировки невозможно предсказать, какой ожидающий поток будет следующим владельцем блокировки.

При использовании взаимоисключающих блокировок следует избегать создания тупиковых ситуаций. Такая ситуация может возникнуть, когда процесс использует несколько взаимоисключающих блокировок и два или более потоков в этом процессе пытаются стать владельцами этих блокировок в произвольной последовательности. Пусть, например, в процессе инициализированы две взаимоисключающие блокировки (А и В), которыми пользуются два потока (X и Y). Допустим, поток X установил блокировку А, а поток Y - блокировку В. Когда X пытается стать владельцем блокировки В, он блокируется, потому что эта блокировка установлена потоком Y. Если затем поток Y пытается стать владельцем блокировки А, он тоже блокируется. Таким образом создается тупиковая ситуация: блокируются оба потока, потому что каждый из них пытается установить блокировку, принадлежащую другому потоку. Ни один из этих потоков не будет выполняться, потому что владелец блокировки заблокирован и не может снять ее.

На первый взгляд использование функции pthread_trywait вместо pthread_wait исключает тупиковую ситуацию, но это не так. В самом деле простая замена pthread_wait на pthread_trywait, приводит к тому, что логика обоих потоков выполняться все равно не будет, хотя потоки и не заблокированы. Это ситуация даже хуже, чем deadlock, т.к. потоки впустую растрачивают ресурсы процессора. Общая рекомендация в подобных случаях – избегать вложения взаимоисключающих блокировок друг в друга.

 

 

31)mutex_init

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

Инициализация взаимоисключающей блокировки. Переменная mutex типа pthread_mutex_t определяется вызывающим потоком. Аргумент mutexattr типа pthread_mutexattr_t – это указатель на объект, содержащий атрибуты для новой блокировки, если эта блокировка должна иметь атрибуты по умолчанию, то значение аргумента mutexattr равно нулю. Функции семейства pthread_mutexattr_init предназначены для создания, удаления, установки и запроса атрибутов мьютексов.

Вместо функции pthread_mutex_init можно использовать статические инициализаторы:

static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;

static pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

static pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

Указанные макросы fast (быстрый), recursive (рекурсивный) и error check (контроль ошибок) изменяют поведение мьютексовых функций в случае deadlock – тупиковых ситуаций. В справочной системе (man – страницах) имеется подробное описание макросов recursive и error check. В большинстве случаев используются «быстрые» мьютексы, их поведение рассматривается далее.

 

32)mutex_lock mutex_destroy

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

Захват взаимоисключающей блокировки. Если мьютекс уже захвачен, то поток блокируется до его освобождения.

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Удаление объекта – взаимоисключающая блокировка.

Все перечисленные функции возвращают 0 в качестве успешного завершения, либо не нулевой код ошибки.

 

33)mutex_trylock mutex_unlock

#include <pthread.h>

int pthread_mutex_trylock(pthread_mutex_t *mutex);

Не блокирующий захват взаимоисключающей блокировки. Если мьютекс уже захвачен, то функция pthread_mutex_trylock возвращает код ошибки EBUSY, не блокируя поток.

 

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

Освобождение взаимоисключающей блокировки.

 

Условные переменные

Условные переменные используются для того, чтобы заблокировать потоки до выполнения определенных условий. Условные переменные обычно применяются в сочетании с взаимоисключающими блокировками, чтобы несколько потоков могли ожидать момента выполнения одного условия. Это можно сделать следующим образом. Сначала поток устанавливает взаимоисключающую блокировку, но и сам блокируется до момента выполнения определенного условия. На то время, пока поток заблокирован, установленная им блокировка автоматически снимается. Когда другой поток выполняет поставленное условие, он дает условной переменной сигнал о разблокировании первого потока. После разблокирования потока взаимоисключающая блокировка автоматически вновь устанавливается, и первый поток повторно проверяет условие. Если оно не выполняется, поток опять блокируется условной переменной. Если же условие выполняется, поток снимает взаимоисключающую блокировку и выполняется дальше.

 

 

35) Системные вызовы pthread_cond_init, pthread_cond_signal и pthread_cond_timedwait. Их предназначение и параметры.

int pthread_cond_init(pthread_cond_t *cond,

pthread_condattr_t *cond_attr);

Инициализирует условную переменную.

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

Блокирует поток на условной переменной, на определенное время.

int pthread_cond_signal(pthread_cond_t *cond);

Разблокирует поток, ожидающий условную переменную.

36) Системные вызовы pthread_cond_broadcast, pthread_cond_wait и pthread_cond_destroy. Их предназначение и параметры.

int pthread_cond_wait(pthread_cond_t *cond,

pthread_mutex_t *mutex);

Блокирует поток на условной переменной.

int pthread_cond_broadcast(pthread_cond_t *cond);

Разблокирует все потоки, ожидающие условную переменную.

 

int pthread_cond_destroy(pthread_cond_t *cond);

Удаление объекта – условная переменная.

 

37) Семафоры POSIX.1b отличаются следующим:

• Семафоры POSIX.1b обозначаются путевым UNUX - именем (если создаются с помощью sem_open), либо остаются безымянными (но с присвоением начального виртуального адреса, если они создаются посредством вызова функции sem_init).

• В POSIX.1b с каждым вызовом sem_open и sem_init создается один семафор, а не набор семафоров.

• Значение семафора POSIX.1b увеличивается и уменьшается на единицу с каждым вызовом sem_post и sem_wait соответственно.

 

38)Системный вызов sem_open. Его предназначение и параметры.

sem_t sem_open(char*name, int flags, mode_t mode,

unsigned int value);

Функция sem_open создает семафор, имя которого задано аргументом name. Значение name должно быть строкой символов, похожей на путевое UNUX – имя, и всегда начинаться с символа «/». Кроме того, не следует ожидать, что будет создан файл с таким именем. Значение аргумента flags может быть равно нулю, если функции известно, что указанный семафор уже существует. Флаг O_CREAT показывает, что следует создать семафор с заданным именем. Вместе с этим флагом можно указывать флаг O_EXCL, который заставляет функцию возвращать код неудачного завершения, если семафор с заданным именем уже существует. Аргументы mode и init_value используются при создании нового семафора. Значение аргумента mode - это права доступа на чтение и запись для владельца, группы и прочих пользователей, которые должны быть установлены для нового семафора. Аргумент init_value задает значение, которое должно быть присвоено семафору. Тип этого аргумента - целое без знака.

В случае успешного выполнения рассматриваемая функция возвращает указатель на структуру типа sem_t, а в случае неудачи -1.

 

39) Системные вызовы sem_init. Его предназначение и параметры.

int sem_init(sem_t*addr,int pshared, unsigned int value);

Функция sem_init - альтернатива функции sem_open. Процесс, использующий sem_init, сначала выделяет область памяти для создаваемого семафора. Эта область памяти может быть разделяемой, если к семафору должны обращаться другие процессы. Адрес этой области передается как значение в аргумент addr функции sem_init. Аргумент pshared имеет значение 1, если семафор должен использоваться несколькими процессами совместно. В противном случае значение этого аргумента - 0. Аргумент init_value содержит целое значение, которое должно быть присвоено семафору как начальное. Это значение не должно быть отрицательным числом.

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

• EINVAL превышено максимально возможное количество семафоров в системе;

• ENOSYS аргумент pshared не равен нулю.

 

40) Системные вызовы sem_getvalue, sem_wait и sem_close. Их предназначение и параметры.

int sem_getvalue(sem_t*idp, int*valuep);

Функция sem_gelvalue позволяет определить текущее значение семафора, указанного аргументом idp. Это значение передается с помощью аргумента valuep. Функция всегда возвращает 0.

 

int sem_wait(sem_t* idp);

Функция sem_wait уменьшает значение семафора на единицу. Семафор обозначается аргументом idp. Если его значение уже равно нулю, функция sem_wait блокирует вызывающий процесс до тех пор, пока не будут созданы условия для его успешного продолжения. Функция всегда возвращает 0.

int sem_close(sem_t*idp);

Функция sem_close отсоединяет семафор, созданный функцией sem_open. В случае успешного выполнения функция возвращает 0, а в случае неудачи -1.

 

41) Системные вызовы sem_trywait и sem_unlink. Их предназначение и параметры.

int sem_trywait(sem_t* idp);

Функция sem_trywait похожа на sem_wait, но она неблокирующая и возвращает EAGAIN (если не может уменьшить указанное значение семафора).

int sem_unlink(char* name);

Функция sem_unlink удаляет семафор, созданный функцией sem_open из системы. В случае успешного выполнения функция возвращает 0, а в случае неудачи -1.

 

42) Системные вызовы sem_post и sem_destroy. Их предназначение и параметры.

int sem_post(sem_t* idp); Функция sem_post увеличивает значение семафора на единицу. Семафор обозначается аргументом idp. В случае успешного выполнения функция возвращает 0, а если увеличение значения семафора приводит к переполнению типа, то возвращается ERANGE.

int sem_destroy (sem_t* idp);

Функция sem_destroy используется с семафорами, созданными функцией sem_init. Она удаляет семафор из системы. В случае успешного выполнения функция возвращает 0, если же на этом семафоре есть заблокированные процессы то возвращается EBUSY.

 



Поделиться:


Последнее изменение этой страницы: 2017-02-06; просмотров: 303; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.137.202.103 (0.065 с.)