Прогон программ с использованием разделяемой памяти
Для иллюстрации использования разделяемой памяти давайте рассмотрим две взаимодействующие программы.
/* Программа 1 (06-1а.с) для иллюстрации работы с разделяемой памятью */
/* Мы организуем разделяемую память для массива из трех целых чисел.
Первый элемент массива является счетчиком числа запусков программы 1,
т. е. данной программы, второй элемент массива – счетчиком числа запусков
программы 2, третий элемент массива – счетчиком числа запусков обеих
программ */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <errno.h>
int main()
{
int *array; /* Указатель на разделяемую память */
int shmid; /* IPC дескриптор для области разделяемой памяти */
int new = 1; /* Флаг необходимости инициализации элементов массива */
char pathname[] = "06-1a.c"; /* Имя файла,
используемое для генерации ключа. Файл с таким
именем должен существовать в текущей директории */
key_t key; /* IPC ключ */
/* Генерируем IPC ключ из имени файла 06-1a.c в
текущей директории и номера экземпляра области
разделяемой памяти 0 */
if((key = ftok(pathname,0)) < 0){
printf("Can\'t generate key\n");
exit(-1);
}
/* Пытаемся эксклюзивно создать разделяемую память для
сгенерированного ключа, т.е. если для этого ключа она
уже существует, системный вызов вернет отрицательное
значение. Размер памяти определяем как размер массива
из трех целых переменных, права доступа 0666 – чтение
и запись разрешены для всех */
if((shmid = shmget(key, 3*sizeof(int),
0666|IPC_CREAT|IPC_EXCL)) < 0){
/* В случае ошибки пытаемся определить: возникла ли она
из-за того, что сегмент разделяемой памяти уже существует
или по другой причине */
if(errno != EEXIST){
/* Если по другой причине – прекращаем работу */
printf("Can\'t create shared memory\n");
exit(-1);
} else {
/* Если из-за того, что разделяемая память уже
существует, то пытаемся получить ее IPC
дескриптор и, в случае удачи, сбрасываем флаг
необходимости инициализации элементов массива */
if((shmid = shmget(key, 3*sizeof(int), 0)) < 0){
printf("Can\'t find shared memory\n");
exit(-1);
}
new = 0;
}
}
/* Пытаемся отобразить разделяемую память в адресное
пространство текущего процесса. Обратите внимание на то,
что для правильного сравнения мы явно преобразовываем
значение -1 к указателю на целое.*/
if((array = (int *)shmat(shmid, NULL, 0)) == (int *)(-1)){
printf("Can't attach shared memory\n");
exit(-1);
}
/* В зависимости от значения флага new либо
инициализируем массив, либо увеличиваем
соответствующие счетчики */
if(new){
array[0] = 1;
array[1] = 0;
array[2] = 1;
} else {
array[0] += 1;
array[2] += 1;
}
/* Печатаем новые значения счетчиков, удаляем
разделяемую память из адресного пространства
текущего процесса и завершаем работу */
printf("Program 1 was spawn %d times,
program 2 - %d times, total - %d times\n",
array[0], array[1], array[2]);
if(shmdt(array) < 0){
printf("Can't detach shared memory\n");
exit(-1);
}
return 0;
}
Листинг 6.1a. Программа 1 (06-1а.с) для иллюстрации работы с разделяемой памятью
/* Программа 2 (06-1b.с) для иллюстрации работы с
разделяемой памятью */
/* Мы организуем разделяемую память для массива из
трех целых чисел. Первый элемент массива является
счетчиком числа запусков программы 1, т. е. данной
программы, второй элемент массива – счетчиком числа
запусков программы 2, третий элемент массива –
счетчиком числа запусков обеих программ */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <errno.h>
int main()
{
int *array; /* Указатель на разделяемую память */
int shmid; /* IPC дескриптор для области разделяемой памяти */
int new = 1; /* Флаг необходимости инициализации элементов массива */
char pathname[] = "06-1a.c"; /* Имя файла,
используемое для генерации ключа. Файл с таким
именем должен существовать в текущей директории */
key_t key; /* IPC ключ */
/* Генерируем IPC ключ из имени файла 06-1a.c в
текущей директории и номера экземпляра области
разделяемой памяти 0 */
if((key = ftok(pathname,0)) < 0){
printf("Can\'t generate key\n");
exit(-1);
}
/* Пытаемся эксклюзивно создать разделяемую память
для сгенерированного ключа, т.е. если для этого
ключа она уже существует, системный вызов вернет
отрицательное значение. Размер памяти определяем
как размер массива из трех целых переменных, права
доступа 0666 – чтение и запись разрешены для всех */
if((shmid = shmget(key, 3*sizeof(int),
0666|IPC_CREAT|IPC_EXCL)) < 0){
/* В случае возникновения ошибки пытаемся определить:
возникла ли она из-за того, что сегмент разделяемой
памяти уже существует или по другой причине */
if(errno != EEXIST){
/* Если по другой причине – прекращаем работу */
printf("Can\'t create shared memory\n");
exit(-1);
} else {
/* Если из-за того, что разделяемая память уже
существует, то пытаемся получить ее IPC дескриптор
и, в случае удачи, сбрасываем флаг необходимости
инициализации элементов массива */
if((shmid = shmget(key, 3*sizeof(int), 0)) < 0){
printf("Can\'t find shared memory\n");
exit(-1);
}
new = 0;
}
}
/* Пытаемся отобразить разделяемую память в адресное
пространство текущего процесса. Обратите внимание на то,
что для правильного сравнения мы явно преобразовываем
значение -1 к указателю на целое.*/
if((array = (int *)shmat(shmid, NULL, 0)) ==
(int *)(-1)){
printf("Can't attach shared memory\n");
exit(-1);
}
/* В зависимости от значения флага new либо
инициализируем массив, либо увеличиваем
соответствующие счетчики */
if(new){
array[0] = 0;
array[1] = 1;
array[2] = 1;
} else {
array[1] += 1;
array[2] += 1;
}
/* Печатаем новые значения счетчиков, удаляем разделяемую
память из адресного пространства текущего процесса и
завершаем работу */
printf("Program 1 was spawn %d times,
program 2 - %d times, total - %d times\n",
array[0], array[1], array[2]);
if(shmdt(array) < 0){
printf("Can't detach shared memory\n");
exit(-1);
}
return 0;
}
Листинг 6.1b. Программа 2 (06-1b.с) для иллюстрации работы с разделяемой памятью
Эти программы очень похожи друг на друга и используют разделяемую память для хранения числа запусков каждой из программ и их суммы. В разделяемой памяти размещается массив из трех целых чисел. Первый элемент массива используется как счетчик для программы 1, второй элемент – для программы 2, третий элемент – для обеих программ суммарно. Дополнительный нюанс в программах возникает из-за необходимости инициализации элементов массива при создании разделяемой памяти. Для этого нам нужно, чтобы программы могли различать случай, когда они создали ее, и случай, когда она уже существовала. Мы добиваемся различия, используя вначалесистемный вызов shmget() с флагами IPC_CREAT и IPC_EXCL . Если вызов завершается нормально, то мы создали разделяемую память. Если вызов завершается с констатацией ошибки и значение переменной errnoравняется EEXIST, то, значит, разделяемая память уже существует, и мы можем получить ее IPC дескриптор, применяя тот же самый вызов с нулевым значением флагов. Наберите программы, сохраните под именами 06-1а.си 06-1b.c cоответственно, откомпилируйте их и запустите несколько раз. Проанализируйте полученные результаты.
Команды ipcs и ipcrm
Как мы видели из предыдущего примера, созданная область разделяемой памяти сохраняется в операционной системе даже тогда, когда нет ни одного процесса, включающего ее в свое адресное пространство. С одной стороны, это имеет определенные преимущества, поскольку не требует одновременного существования взаимодействующих процессов, с другой стороны, может причинять существенные неудобства. Допустим, что предыдущие программы мы хотим использовать таким образом, чтобы подсчитывать количество запусков в течение одного, текущего, сеанса работы в системе. Однако в созданном сегменте разделяемой памяти остается информация от предыдущего сеанса, и программы будут выдавать общее количество запусков за все время работы с момента загрузки операционной системы. Можно было бы создавать для нового сеанса новый сегмент разделяемой памяти, но количество ресурсов в системе не безгранично. Нас спасает то, что существуют способы удалять неиспользуемые ресурсы System V IPC как с помощью команд операционной системы, так и с помощью системных вызовов. Все средства System V IPC требуют определенных действий для освобождения занимаемых ресурсов после окончания взаимодействия процессов. Для того чтобы удалять ресурсы System V IPC из командной строки, нам понадобятся две команды, ipcs и ipcrm .
Команда ipcs выдает информацию обо всех средствах System V IPC, существующих в системе, для которых пользователь обладает правами на чтение: областях разделяемой памяти, семафорах и очередях сообщений.
Команда ipcs Синтаксис команды ipcs [-asmq] [-tclup] ipcs [-smq] -i id ipcs -h Описание команды Команда ipcs предназначена для получения информации о средствах System V IPC, к которым пользователь имеет право доступа на чтение. Опция -i позволяет указать идентификатор ресурсов. Будет выдаваться только информация для ресурсов, имеющих этот идентификатор. Виды IPC ресурсов могут быть заданы с помощью следующих опций: · –s для семафоров; · -m для сегментов разделяемой памяти; · -q для очередей сообщений; · -a для всех ресурсов (по умолчанию). Опции [-tclup] используются для изменения состава выходной информации. По умолчанию для каждого средства выводятся его ключ, идентификатор IPC, идентификатор владельца, права доступа и ряд других характеристик. Применение опций позволяет вывести: · -t времена совершения последних операций над средствами IPC; · -p идентификаторы процесса, создавшего ресурс, и процесса, совершившего над ним последнюю операцию; · -c идентификаторы пользователя и группы для создателя ресурса и его собственника; · -l системные ограничения для средств System V IPC; · -u общее состояние IPC ресурсов в системе. Опция -h используется для получения краткой справочной информации. |
Из всего многообразия выводимой информации нас будут интересовать только IPC идентификаторы для средств, созданных вами. Эти идентификаторы будут использоваться в команде ipcrm , позволяющей удалить необходимый ресурс из системы. Для удаления сегмента разделяемой памяти эта команда имеет вид:
ipcrm shm <IPC идентификатор>
Удалите созданный вами сегмент разделяемой памяти из операционной системы, используя эти команды.
Команда ipcrm Синтаксис команды ipcrm [shm | msg | sem] id Описание команды Команда ipcrm предназначена для удаления ресурса System V IPC из операционной системы. Параметр id задает IPC идентификатор для удаляемого ресурса, параметрshm используется для сегментов разделяемой памяти, параметр msg – для очередей сообщений, параметр sem – для семафоров. |
Если поведение программ, использующих средства System V IPC, базируется на предположении, что эти средства были созданы при их работе, не забывайте перед их запуском удалять уже существующие ресурсы.