Главная страница

Околения компьютеров


Скачать 1.67 Mb.
НазваниеОколения компьютеров
Дата18.04.2022
Размер1.67 Mb.
Формат файлаpdf
Имя файлаOS_Graur.pdf
ТипДокументы
#481658
страница12 из 20
1   ...   8   9   10   11   12   13   14   15   ...   20
ftok():
#include
#include key_t ftok(char *filename, char proj);
Эта функция генерирует значение ключа по некоторой строке символов и добавочному символу, передаваемым в качестве параметров. Гарантируется, что полученное таким образом значение будет отличаться от всех других значений, сгенерированных функцией ftok() с другими значениями параметров, и в то же время, при повторном запуске ftok() с теми же параметрами, будет получено то же самое значение ключа.
Смысл второго аргумента функции ftok() – добавочного символа – в том, что он позволяет генерировать разные значения ключа по одному и тому же значению первого параметра – строки. Это позволяет программисту поддерживать несколько версий своей программы, которые будут использовать одну и ту же строку, но разные добавочные символы для генерации ключа, и тем самым получат возможность в рамках одной системы работать с разными разделяемыми ресурсами.

Следует заметить, что функция ftok() не является системным вызовом, а предоставляется библиотекой.
Общие принципы работы с разделяемыми ресурсами.
Рассмотрим некоторые моменты, общие для работы со всеми разделяемыми ресурсами IPC. Как уже говорилось, общим для всех ресурсов является механизм именования. Кроме того, для каждого IPC-ресурса поддерживается идентификатор его владельца и структура, описывающая права доступа к нему. Подобно файлам, права доступа задаются отдельно для владельца, его группы и всех остальных пользователей; однако, в отличие от файлов, для разделяемых ресурсов поддерживается только две категории доступа: по чтению и записи. Априори считается, что возможность изменять свойства ресурса и удалять его имеется только у процесса, эффективный идентификатор пользователя которого совпадает с идентификатором владельца ресурса. Владельцем ресурса назначается пользователь, от имени которого выполнялся процесс, создавший ресурс, однако создатель может передать права владельца другому пользователю. В заголовочном файле определен тип struct ipc_perm, который описывает права доступа к любому IPC-ресурсу. Поля в этой структуре содержат информацию о создателе и владельце ресурса и их группах, правах доступа к ресурсу и его ключе.
Для создания разделяемого ресурса с заданным ключом, либо подключения к уже существующему ресурсу с таким ключом используются ряд системных вызовов, имеющих общий суффикс get. Общими параметрами для всех этих вызовов являются ключ и флаги. В качестве значения ключа при создании любого IPC- объекта может быть указано значение IPC_PRIVATE. При этом создается ресурс, который будет доступен только породившему его процессу. Такие ресурсы обычно порождаются родительским процессом, который затем сохраняет полученный дескриптор в некоторой переменной и порождает своих потомков. Так как потомкам доступен уже готовый дескриптор созданного объекта, они могут непосредственно работать с ним, не обращаясь предварительно к «get»-методу.
Таким образом, созданный ресурс может совместно использоваться родительским и порожденными процессами. Однако, важно понимать, что если один из этих процессов повторно вызовет «get»-метод с ключом IPC_PRIVATE, в результате будет получен другой, совершенно новый разделяемый ресурс, так как при обращении к «get»-методу с ключом IPC_PRIVATE всякий раз создается новый объект нужного типа.
Отметим, что даже если ни один процесс не подключен к разделяемому ресурсу, система не удаляет его автоматически. Удаление объектов IPC является обязанностью одного из работающих с ним процессов и для этого определена специальная функция. Для этого системой предоставляются соответствующие функции по управлению объектами System V IPC.

БИЛЕТ 33
Очередь сообщений.
Итак, одним из типов объектов System V IPC являются очереди сообщений.
Очередь сообщений представляет собой некое хранилище типизированных сообщений, организованное по принципу FIFO. Любой процесс может помещать новые сообщения в очередь и извлекать из очереди имеющиеся там сообщения.
Каждое сообщение имеет тип, представляющий собой некоторое целое число.
Благодаря наличию типов сообщений, очередь можно интерпретировать двояко — рассматривать ее либо как сквозную очередь неразличимых по типу сообщений, либо как некоторое объединение подочередей, каждая из которых содержит элементы определенного типа. Извлечение сообщений из очереди происходит согласно принципу FIFO – в порядке их записи, однако процесс-получатель может указать, из какой подочереди он хочет извлечь сообщение, или, иначе говоря, сообщение какого типа он желает получить – в этом случае из очереди будет извлечено самое «старое» сообщение нужного типа (см. Рис. 10).
А
А
В
А
В
А
В
Рис. 10 Типизированные очереди сообщений
Рассмотрим набор системных вызовов, поддерживающий работу с очередями сообщений.
Доступ к очереди сообщений.
Для создания новой или для доступа к существующей используется системный вызов:
#INCLUDE
#INCLUDE
#INCLUDE
INT MSGGET (KEY_T KEY, INT MSGFLAG)
В случае успеха вызов возвращает положительный дескриптор очереди, который может в дальнейшем использоваться для операций с ней, в случае неудачи -1.
Первым аргументом вызова является ключ, вторым – флаги, управляющие поведением вызова. Подробнее детали процесса создания/подключения к ресурсу описаны выше.
Отправка сообщения.
Для отправки сообщения используется функция msgsnd():
#INCLUDE
#INCLUDE
#INCLUDE
INT MSGSND (INT MSQID, CONST VOID *MSGP, SIZE_T MSGSZ,
INT MSGFLG)

Ее первый аргумент — идентификатор очереди, полученный в результате вызова
msgget(). Второй аргумент — указатель на буфер, содержащий реальные данные и тип сообщения, подлежащего посылке в очередь, в третьем аргументе указывается размер буфера.
В качестве буфера необходимо указывать структуру, содержащую следующие поля
(в указанном порядке):
long msgtype — тип сообщения
char msgtext[ ]
— данные (тело сообщения)
В заголовочном файле определена константа MSGMAX, описывающая максимальный размер тела сообщения. При попытке отправить сообщение, у которого число элементов в массиве msgtext превышает это значение, системный вызов вернет –1.
Четвертый аргумент данного вызова может принимать значения 0 или
IPC_NOWAIT. В случае отсутствия флага IPC_NOWAIT вызывающий процесс будет блокирован (т.е. приостановит работу), если для посылки сообщения недостаточно системных ресурсов, т.е. если полная длина сообщений в очереди будет больше максимально допустимого. Если же флаг IPC_NOWAIT будет установлен, то в такой ситуации выход из вызова произойдет немедленно, и возвращаемое значение будет равно –1.
В случае удачной записи возвращаемое значение вызова равно 0.
Получение сообщения.
Для получения сообщения имеется функция msgrcv():
#INCLUDE
#INCLUDE
#INCLUDE
INT MSGRCV (INT MSQID, VOID *MSGP, SIZE_T MSGSZ, LONG
MSGTYP, INT MSGFLG)
Первые три аргумента аналогичны аргументам предыдущего вызова: это дескриптор очереди, указатель на буфер, куда следует поместить данные, и максимальный размер (в байтах) тела сообщения, которое можно туда поместить.
Буфер, используемый для приема сообщения, должен иметь структуру, описанную выше.
Четвертый аргумент указывает тип сообщения, которое процесс желает получить.
Если значение этого аргумента есть 0, то будет получено сообщение любого типа.
Если значение аргумента msgtyp больше 0, из очереди будет извлечено сообщение указанного типа. Если же значение аргумента msgtyp отрицательно, то тип принимаемого сообщения определяется как наименьшее значение среди типов, которые меньше модуля msgtyp. В любом случае, как уже говорилось, из подочереди с заданным типом (или из общей очереди, если тип не задан) будет выбрано самое старое сообщение.
Последним аргументом является комбинация (побитовое сложение) флагов. Если среди флагов не указан IPC_NOWAIT, и в очереди не найдено ни одного сообщения, удовлетворяющего критериям выбора, процесс будет заблокирован до появления такого сообщения. (Однако, если такое сообщение существует, но его длина превышает указанную в аргументе msgsz, то процесс заблокирован не
будет, и вызов сразу вернет –1. Сообщение при этом останется в очереди). Если же флаг IPC_NOWAIT указан, то вызов сразу вернет –1.
Процесс может также указать флаг MSG_NOERROR – в этом случае он может прочитать сообщение, даже если его длина превышает указанную емкость буфера.
В этом случае в буфер будет записано первые msgsz байт из тела сообщения, а остальные данные отбрасываются.
В случае удачного чтения возвращаемое значение вызова равно фактической длине тела полученного сообщения в байтах.
Управление очередью сообщений.
Функция управления очередью сообщений выглядит следующим образом:
#INCLUDE
#INCLUDE
#INCLUDE
INT MSGCTL(INT MSQID, INT CMD, STRUCT MSGID_DS *BUF)
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с очередью и уничтожения очереди. Ее аргументы — идентификатор ресурса, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры очереди. Тип msgid_ds описан в заголовочном файле , и представляет собой структуру, в полях которой хранятся права доступа к очереди, статистика обращений к очереди, ее размер и т.п.
Возможные значения аргумента cmd:
IPC_STAT – скопировать структуру, описывающую управляющие параметры очереди по адресу, указанному в параметре buf
IPC_SETзаменить структуру, описывающую управляющие параметры очереди, на структуру, находящуюся по адресу, указанному в параметре buf
IPC_RMID – удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
10. Использование очереди сообщений.
Пример программы, где основной процесс читает некоторую текстовую строку из стандартного ввода, и в случае, если строка начинается с буквы 'a', эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В, затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.
Основной процесс.
#include
#include
#include
#include
#include
#include struct {
long mtype;
/* тип сообщения */ char
Data[256];
/* сообщение */
} Message; int main(int argc, char **argv)
{ key_t key; int msgid; char str[256]; key = ftok("/usr/mash",'s');
/*получаем уникальный ключ, однозначно определяющий доступ к ресурсу */ msgid=msgget(key, 0666 | IPC_CREAT);
/*создаем очередь сообщений , 0666 определяет права доступа */ for(;;)
{
/* запускаем вечный цикл */ gets(str); /* читаем из стандартного ввода строку */ strcpy(Message.Data, str);
/* и копируем ее в буфер сообщения */ switch(str[0]){ case
'a': case
'A':
Message.mtype
=
1;
/* устанавливаем тип */ msgsnd(msgid, (struct msgbuf*)
(&Message), strlen(str) + 1, 0);
/* посылаем сообщение в очередь */ break; case
'b': case
'B':
Message.mtype
=
2; msgsnd(msgid, (struct msgbuf*)
(&Message), strlen(str) + 1, 0); break; case
'q': case
'Q':
Message.mtype
=
1; msgsnd(msgid, (struct msgbuf*)
(&Message), strlen(str) + 1, 0);
Message.mtype
=
2; msgsnd(msgid, (struct msgbuf*)
(&Message), strlen(str) + 1, 0); sleep(10);
/* ждем получения сообщений процессами А и В */ msgctl(msgid, IPC_RMID, NULL);
/* уничтожаем очередь*/
return
0; default: break;
}
}
}
Процесс-приемник А
/* процесс В аналогичен с точностью до четвертого параметра в msgrcv */
#include
#include
#include
#include struct { long mtype; char
Data[256];
} Message; int main(int argc, char **argv)
{ key_t key; int msgid; key = ftok("/usr/mash",'s');
/* получаем ключ по тем же параметрам */ msgid = msgget(key, 0666 | IPC_CREAT);
/*подключаемся к очереди сообщений */ for(;;)
{
/* запускаем вечный цикл */ msgrcv(msgid, (struct msgbuf*) (&Message),
256, 1, 0);
/* читаем сообщение с типом 1*/ if (Message.Data[0]=='q' ||
Message.Data[0]=='Q') break; printf("\nПроцесс-приемник А: %s",
Message.Data);
} return
0;
}
Благодаря наличию типизации сообщений, очередь сообщений предоставляет возможность мультиплексировать сообщения от различных процессов, при этом каждая пара взаимодействующих через очередь процессов может использовать свой тип сообщений, и таким образом, их данные не будут смешиваться.
В качестве иллюстрации приведем следующий стандартный пример взаимодействия. Рассмотрим еще один пример - пусть существует процесс-сервер и несколько процессов-клиентов. Все они могут обмениваться данными,
используя одну очередь сообщений. Для этого сообщениям, направляемым от клиента к серверу, присваиваем значение типа 1. При этом процесс, отправивший сообщение, в его теле передает некоторую информацию, позволяющую его однозначно идентифицировать. Тогда сервер, отправляя сообщение конкретному процессу, в качестве его типа указывает эту информацию (например, PID процесса). Таким образом, сервер будет читать из очереди только сообщения типа
1, а клиенты — сообщения с типами, равными идентификаторам их процессов.
11. Очередь сообщений. Модель «клиент-сервер»
server
#include
#include
#include
#include int main(int argc, char **argv)
{ struct { long mestype; char mes [100];
} messageto; struct { long mestype; long mes;
} messagefrom; key_t key; int mesid; key = ftok("example",'r'); mesid = msgget (key, 0666 | IPC_CREAT); while(1)
{ if (msgrcv(mesid, &messagefrom, sizeof(messagefrom), 1, 0) <= 0) continue; messageto.mestype = messagefrom.mes; strcpy( messageto.mes, "Message for client");
msgsnd (mesid, &messageto, sizeof(messageto),
0);
} msgctl (mesid, IPC_RMID, 0); return 0;
}
client
#include
#include
#include
#include
#include int main(int argc, char **argv)
{ struct { long mestype; /*описание структуры сообщения*/ long mes;
} messageto; struct { long mestype; /*описание структуры сообшения*/ char mes[100];
} messagefrom; key_t key; int mesid; long pid = getpid(); key = ftok("example", 'r'); mesid = msgget(key, 0); /*присоединение к очереди сообщений*/ messageto.mestype = 1; messageto.mes = pid; msgsnd (mesid, &messageto, sizeof(messageto), 0);
/* отправка */
while ( msgrcv (mesid, &messagefrom, sizeof(messagefrom), pid, 0) <= 0);
/*прием сообщения */ printf("%s\n", messagefrom.mes); return 0;
}

БИЛЕТ 34
Разделяемая память
Виртуальное адресное
пр-во процесса
Виртуальное адресное
пр-во процесса
Физическая память
Рис. 11 Разделяемая память
Механизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальной памяти на общую область физической памяти. Благодаря этому, данные, находящиеся в этой области памяти, будут доступны для чтения и модификации всем процессам, подключившимся к данной области памяти.
Процесс, подключившийся к разделяемой памяти, может затем получить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемой памяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к malloc()), однако, как уже говорилось, сама по себе разделяемая область памяти не уничтожается автоматически даже после того, как процесс, создавший или использовавший ее, перестанет с ней работать.
Рассмотрим набор системных вызовов для работы с разделяемой памятью.
Создание общей памяти.
#include
#include
#include int shmget (key_t key, int size, int shmemflg)
Аргументы этого вызова: key - ключ для доступа к разделяемой памяти; size задает размер области памяти, к которой процесс желает получить доступ. Если в результате вызова shmget() будет создана новая область разделяемой памяти, то ее размер будет соответствовать значению size. Если же процесс подключается к существующей области разделяемой памяти, то значение size должно быть не более ее размера, иначе вызов вернет –1. Заметим, что если процесс при подключении к существующей области разделяемой памяти указал в аргументе
size значение, меньшее ее фактического размера, то впоследствии он сможет получить доступ только к первым size байтам этой области.

Отметим, что в заголовочном файле определены константы
SHMMIN и SHMMAX, задающий минимально возможный и максимально возможный размер области разделяемой памяти. Если процесс пытается создать область разделяемой памяти, размер которой не удовлетворяет этим границам, системный вызов
1   ...   8   9   10   11   12   13   14   15   ...   20


написать администратору сайта