организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации сигналы sigusr1 и sigusr2. |
Организация файловой системы в UNIX. Работа с файлами и директориями. Понятие о memory mapped файлах
Понятие о файлах, отображаемых в память (memory mapped файлах). Системные вызовы mmap(), munmap()
Как уже говорилось, с помощью системного вызова open() операционная система отображает файл из пространства имен в дисковое пространство файловой системы, подготавливая почву для осуществления других операций. С появлением концепции виртуальной памяти, которая рассматривалась в лекции 9, когда физические размеры памяти перестали играть роль сдерживающего фактора в развитии вычислительных систем, стало возможным отображать файлы непосредственно в адресное пространство процессов. Иными словами, появилась возможность работать с файлами как с обычной памятью, заменив выполнение базовых операций над ними с помощью системных вызовов на использование операций обычных языков программирования. Файлы, чье содержимое отображается непосредственно в адресное пространство процессов, получили название файлов, отображаемых в память, или, по-английски, memory mapped файлов (см. лекцию 10). Надо отметить, что такое отображение может быть осуществлено не только для всего файла в целом, но и для его части.
С точки зрения программиста работа с такими файлами выглядит следующим образом:
- Отображение файла из пространства имен в адресное пространство процесса происходит в два этапа: сначала выполняется отображение в дисковое пространство, а уже затем из дискового пространства в адресное. Поэтому вначале файл необходимо открыть, используя обычный системный вызов open() .
- Вторым этапом является отображение файла целиком или частично из дискового пространства в адресное пространство процесса. Для этого используется системный вызов mmap() . Файл после этого можно и закрыть, выполнив системный вызов close() , так как необходимую информацию о расположении файла на диске мы уже сохранили в других структурах данных при вызове mmap() .
- После этого с содержимым файла можно работать, как с содержимым обычной области памяти.
- По окончании работы с содержимым файла, необходимо освободить дополнительно выделенную процессу область памяти, предварительно синхронизировав содержимое файла на диске с содержимым этой области (если, конечно, необходимо). Эти действия выполняет системный вызов munmap() .
Анализ, компиляция и прогон программы для создания memory mapped файла и записи его содержимого
Для закрепления материала, изложенного в предыдущем разделе, рассмотрим пример программы.
/* Программа 11-1.с для иллюстрации работы с memory mapped файлом */ int main(void) { int fd; /* Файловый дескриптор для файла, в котором будет храниться наша информация*/ size_t length; /* Длина отображаемой части файла */ int i; /* Ниже следует описание типа структуры, которым мы забьем файл, и двух указателей на подобный тип. Указатель ptr будет использоваться в качестве начального адреса выделенной области памяти, а указатель tmpptr – для перемещения внутри этой области. */ struct A { double f; double f2; } *ptr, tmpptr; /* Открываем файл или сначала создаем его (если такого файла не было). Права доступа к файлу при создании определяем как read-write для всех категорий пользователей (0666). Из-за ошибки в Linux мы будем вынуждены ниже в системном вызове mmap() разрешить в отображении файла и чтение, и запись, хотя реально нам нужна только запись. Поэтому и при открытии файла мы вынуждены задавать O_RDWR. */ fd = open("mapped.dat", O_RDWR | O_CREAT, 0666); if( fd == -1){ /* Если файл открыть не удалось, выдаем сообщение об ошибке и завершаем работу */ printf("File open failed!\n"); exit(1); } /* Вычисляем будущую длину файла (мы собираемся записать в него 100000 структур) */ length = 100000*sizeof(struct A); /* Вновь созданный файл имеет длину 0. Если мы его отобразим в память с такой длиной, то любая попытка записи в выделенную память приведет к ошибке. Увеличиваем длину файла с помощью вызова ftruncate(). */ ftruncate(fd,length); /* Отображаем файл в память. Разрешенные операции над отображением указываем как PROT_WRITE | PROT_READ по уже названным причинам. Значение флагов ставим в MAP_SHARED, так как мы хотим с охранить информацию, которую занесем в отображение, на диске. Файл отображаем с его начала (offset = 0) и до конца (length = длине файла). */ ptr = (struct A )mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); /* Файловый дескриптор нам более не нужен, и мы его закрываем */ close(fd); if( ptr == MAP_FAILED ){ /* Если отобразить файл не удалось, сообщаем об ошибке и завершаем работу */ printf("Mapping failed!\n"); exit(2); } /* В цикле заполняем образ файла числами от 1 до 100000 и их квадратами. Для перемещения по области памяти используем указатель tmpptr, так как указатель ptr на начало образа файла нам понадобится для прекращения иотображения вызовом munmap(). */ tmpptr = ptr; for(i = 1; i <=100000; i++){ tmpptr->f = i; tmpptr->f2 = tmpptr->f*tmpptr->f; tmpptr++; } /* Прекращаем отображать файл в память, записываем содержимое отображения на диск и освобождаем память. */ munmap((void *)ptr, length); return 0; }Листинг 11.1. Программа 11-1.с для иллюстрации работы с memory mapped файлом.
Эта программа создает файл, отображает его в адресное пространство процесса и заносит в него информацию с помощью обычных операций языка С.
Обратите внимание на необходимость увеличения размера файла перед его отображением. Созданный файл имеет нулевой размер, и если его с этим размером отобразить в память, то мы сможем записать в него или прочитать из него не более 0 байт, т.е. ничего. Для увеличения размера файла использован системный вызов ftruncate() , хотя это можно было бы сделать и любым другим способом.
При отображении файла мы вынуждены разрешить в нем и запись, и чтение, хотя реально совершаем только запись. Это сделано для того, чтобы избежать ошибки в операционной системе Linux, связанной с использованием 486-х и 586-х процессоров. Такой список разрешенных операций однозначно требует, чтобы при открытии файла системным вызовом open() файл открывался и на запись, и на чтение. Поскольку информацию мы желаем сохранить на диске, при отображении использовано значение флагов MAP_SHARED. Откомпилируйте эту программу и запустите ее.
Изменение предыдущей программы для чтения из файла, используя его отображение в память
Модифицируйте программу из предыдущего раздела так, чтобы она отображала файл, записанный программой из раздела "Анализ, компиляция и прогон программы для создания memory mapped файла и записи его содержимого", в память и считала сумму квадратов чисел от 1 до 100000, которые уже находятся в этом файле.
Задача повышенной сложности: напишите две программы, использующие memory mapped файл для обмена информацией при одновременной работе, подобно тому, как они могли бы использовать разделяемую память