ТОР 5 статей: Методические подходы к анализу финансового состояния предприятия Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века Характеристика шлифовальных кругов и ее маркировка Служебные части речи. Предлог. Союз. Частицы КАТЕГОРИИ:
|
Необходимость синхронизации процессов и нитей исполнения, использующих общую память.http://cs.mipt.ru/docs/courses/osstud/05/ch5.htm - l0502Все рассмотренные в лабораторной работе примеры являются не совсем корректными. В большинстве случаев они работают правильно, однако возможны ситуации, когда совместная деятельность этих процессов или нитей исполнения приводит к неверным и неожиданным результатом. Это связано с тем, что любые неатомарные операции, связанные с изменением содержимого разделяемой памяти, представляют собой критическую секцию процесса или нити исполнения. При одновременном существовании 2-х процессов в операционной системе может возникнуть следующая последовательность выполнения операций во времени: ... Процесс 1: array[0] += 1; Процесс 2: array[1] += 1; Процесс 1: array[2] += 1; Процесс 1: printf("Program 1 was spawn %d times, program 2 - %d times, total - %d times\n", array[0], array[1], array[2]); ... Тогда печать будет давать неправильные результаты. Естественно, что воспроизвести подобную последовательность действий практически нереально. Мы не сможем подобрать необходимые времена старта процессов и степень загруженности вычислительной системы. Это проделано в программах04-4 и 04-5. http://cs.mipt.ru/docs/courses/osstud/05/ch5.htm - l0503Как видим, для написания корректно работающих программ необходимо обеспечивать взаимоисключение при работе с разделяемой памятью и, может быть, взаимную очередность доступа к ней Примеры программ Программа 04-1 /* Программа 04-1 для иллюстрации работы с разделяемой памятью*/ /* Организуем разделяемую память для массива из 3-х целых чисел. Первый элемент массива является счетчиком числа запусков программы 04-1, т.е. данной программы, второй элемент массива - счетчиком числа запусковпрограммы 04-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); } /* Пытаемся эксклюзивно создать разделяемую память для сгенерированного ключа, т.е. если для этого ключа она уже существует системный вызов вернет отрицательное значение. Размер памяти определяем как размер массива из 3-х целых переменных, права доступа 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; }
Программа 04-2 /* Программа 2 для иллюстрации работы с разделяемой памятью*/ /* Мы организуем разделяемую память для массива из 3-х целых чисел. Первый элемент массива является счетчиком числа запусковпрограммы 1, второй элемент массива - счетчиком числа запусков программы 2, т. е. данной программы, третий элемент массива - счетчиком числа запусков обеих программ */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> intmain() { 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); } /* Пытаемся эксклюзивно создать разделяемую память для сгенерированного ключа, т.е. если для этого ключа она уже существует системный вызов вернет отрицательное значение. Размер памяти определяем как размер массива из 3-х целых переменных, права доступа 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 wasspawn %dtimes, program 2 - %dtimes, total - %dtimes\n", array[0], array[1], array[2]); if(shmdt(array) < 0){ printf("Can't detach shared memory\n"); exit(-1); } return 0; }
Программа 04-3 /* Программа для иллюстрации работы двух нитей исполненияКаждая нить исполнения просто увеличивает на 1 разделяемую переменную a. */ #include <pthread.h> #include <stdio.h> int a = 0; /*Переменная a является глобальной статической для всей программы, поэтому она будет разделяться обеими нитями исполнения.*/ /*Ниже следует текст функции, которая будет ассоциирована со 2-м thread'ом*/ void *mythread(void *dummy)/* Параметр dummy в нашей функции не используется и присутствует только для совместимости типов данных. По той же причине функция возвращает значение void *, хотя это никак не используется в программе.*/ { pthread_t mythid; /* Для идентификатора нити исполнения */ /*Заметим, что переменная mythidявляется динамической локальной переменной функцииmythread(), т.е. помещается в стеке и, следовательно, не разделяется нитями исполнения. */ /* Запрашиваемидентификаторthread'а */ mythid = pthread_self(); a = a+1; printf("Thread %d, Calculation result = %d\n", mythid, a); returnNULL; } /*Функцияmain() - онажеассоциированнаяфункцияглавногоthread'а*/ int main() { pthread_t thid, mythid; int result; /* Пытаемся создать новую нить исполнения, ассоциированную с функцией mythread().Передаем ей в качестве параметра значение NULL.В случае удачи в переменную thid занесется идентификатор нового thread'а. Есливозникнетошибка - прекратимработу. */ result = pthread_create(&thid, (pthread_attr_t *)NULL, mythread, NULL); if(result!= 0){ printf ("Error on thread create, return value = %d\n", result); exit(-1); } printf("Threadcreated, thid = %d\n", thid); /*Запрашиваемидентификаторглавногоthread'а */ mythid = pthread_self(); a = a+1; printf("Thread %d, Calculationresult = %d\n", mythid, a); /*Ожидаемзавершенияпорожденногоthread'a, неинтересуяськакоезначениеоннамвернет. Если не выполнить вызов этой функции, то возможна ситуация, когда мы завершим функцию main() до того, как выполнится порожденный thread, что автоматически повлечет его завершение,исказив результаты.*/ pthread_join(thid, (void **)NULL); return 0; }
Программа 04-4 /* Программа 04-4 для иллюстрации некорректности работы с разделяемой памятью*/ /* Организуем разделяемую память для массива из 3-х целых чисел. Первый элемент массива является счетчиком числа запусков программы 1, т. е. данной программы, второй элемент массива - счетчиком числа запусковпрограммы 2, третий элемент массива - счетчиком числа запусков обеих программ */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> int main() { int *array; /* Указательнаразделяемуюпамять */ intshmid; /* IPC дескриптор для области разделяемой памяти */ intnew = 1; /* Флаг необходимости инициализации элементов массива */ charpathname[] = "06-1a.c"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */ key_tkey; /* IPCключ */ longi; /* Генерируем IPC ключ из имени файла 06-1a.c в текущей директории и номера экземпляра области разделяемой памяти 0 */ if((key = ftok(pathname,0)) < 0){ printf("Can\'t generate key\n"); exit(-1); } /* Пытаемся эксклюзивно создать разделяемую память для сгенерированного ключа, т.е. если для этого ключа она уже существует, то системный вызов вернет отрицательное значение. Размер памяти определяем как размер массива из 3-х целых переменных, права доступа 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; for(i=0; i<1000000000L; i++); /* Предельное значение для i может меняться в зависимости от производительности компьютера */ 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; } Программа 04-5 /* Программа 04-5 для иллюстрации некорректности работы с разделяемой памятью */ /* Организуем разделяемую память для массива из 3-х целых чисел. Первый элемент массива является счетчиком числа запусковпрограммы 1, второй элемент массива - счетчиком числа запусков программы 2, т. е. данной программы, третий элемент массива - счетчиком числа запусков обеих программ */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> int main() { int *array; /* Указательнаразделяемуюпамять */ intshmid; /* IPC дескриптор для области разделяемой памяти */ intnew = 1; /* Флаг необходимости инициализации элементов массива */ charpathname[] = "06-1a.c"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */ key_t key; /* IPC ключ */ longi; /* Генерируем IPC ключ из имени файла 06-1a.c в текущей директории и номера экземпляра области разделяемой памяти 0 */ if((key = ftok(pathname,0)) < 0){ printf("Can\'t generate key\n"); exit(-1); } /* Пытаемся эксклюзивно создать разделяемую память для сгенерированного ключа, т.е. если для этого ключа она уже существует, то системный вызов вернет отрицательное значение. Размер памяти определяем как размер массива из 3-х целых переменных, права доступа 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; for(i=0; i<1000000000L; i++); /* Предельное значение для i может меняться в зависимости от производительности компьютера */ 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; } Не нашли, что искали? Воспользуйтесь поиском:
|