91
Структура процессов ОС Linux следует современным тенденциям построения операционных систем, в плане использования ими основной памяти ЭВМ:
•они полностью используют виртуальную адресацию памяти;
•ядро ОС фиксирует основные характеристики всех запущенных процессов.
Внастоящее время, ядро ОС использует 32-битную виртуальную адресацию пространства процесса, которая составляет размер в 4 ГБ, как показано на рисунке 2.17:
•первые 3 ГБ соответствуют пространству пользователя (User space);
•последний 1 ГБ находится в распоряжении ядра ОС (Kernel space).
Рисунок 2.17 — Виртуальная адресация процесса ОС Linux
В ядре ОС, для каждого процесса отведена директория страниц, что подразумевает возможность доступа к 1КВ таблицам страниц, которые указывают на 1МВ 4- х килобайтных страниц. Это позволяет адресовать 4 ГБ памяти.
Каждый пользовательский процесс имеет свою локальную таблицу дескриптора, которая адресует различные сегменты кода и сегменты данные-стек.
92
Замечание
В пользовательском пространстве, линейные адреса и логические адреса - идентичны.
Процесс получает свои таблицы страниц от родителя, при выполнении последним системного вызова fork(), со входами, помеченными как READ-ONLY или замещаемые.
Если процесс пытается писать в некоторую область памяти и страница является COPY-ON-WRITE страницей, то она копируется и помечается как READ-WRITE.
В любом случае, «Cистема управления памятью» Linux осуществляет подкачку страниц по обращению, в соответствии со стратегией COPY-ON-WRITE, которая основана на механизме подкачки и поддерживается процессором i386.
Современные ядра ОС Linux псевдоустройство proc, с файловой системой типа proc, которая монтируется к директории /proc корневой ФС.
Эта файловая система содержит много информации о каждом запущенном процес-
се:• директория /proc/<номер процесса> - содержит файлы (псевдофайлы) о разных характеристиках процесса с конкретным номером PID;
•директория /proc/self — является уникальной ссылкой для процесса, который обращается к информации о себе и который, как любой дочерний процесс, не знает свой PID.
Вкачестве демострации указанных возможностей, рассмотрим строку данных файла /proc/self/statm, содержащую семь цифровых слов, семантика которых представлена в таблице 2.1.
Таблица 2.1 — Семантика слов файла /proc/<pid>/statm
Номер слова
Семантика слова
1 Общий размер программы
2
Размер резидентной части памяти
3 Число разделяемых страниц
4
Число страниц 'code'
5 Число страниц data/stack
6
Число страниц library
7 Число страниц dirty («грязных»)
Замечание
Все строки псевдофайлов заканчиваются символом перевода строки (0x0A).
Другой пример, файл /proc/self/maps, который содержит адреса и другие параметры всех ресурсов, привязанных к текущему процессу.
93
На листинге 2.3, представлен текст программы, которая читает и выводит на терминал информацию из файлов /proc/self/statm и /proc/self/maps.
Листинг 2.3 - Пример вывода файлов /proc/self/stats и /proc/self/maps
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h>
int main(void) {
puts("Проект lab8.3 ..."); |
|
|
|||||||
char |
|
|
buf[BUFSIZ]; |
|
|
||||
|
|
|
|
||||||
int i, j, fd; |
|
|
|||||||
ssize_t n; |
|
|
|||||||
char *pw[7]; |
|
|
|||||||
fd = open("/proc/self/statm", O_RDONLY); |
|
||||||||
if(fd < 0){ |
|
|
|||||||
|
|
perror("open /proc/self/statm"); |
|
||||||
} |
|
exit(EXIT_FAILURE); |
|
|
|||||
|
|
|
|
|
|
|
|
|
|
puts("Содержимое файла /proc/self/statm:"); |
|
||||||||
n = read(fd, |
buf, BUFSIZ); |
|
|
||||||
if(fd < 0){ |
|
|
|
|
|||||
|
|
perror("read /proc/self/statm:"); |
|
||||||
} |
|
exit(EXIT_FAILURE); |
|
|
|||||
|
|
|
|
|
|
|
|
|
|
close(fd); |
|
|
|||||||
buf[n - 1] = 0; |
|
|
|||||||
puts |
(buf); |
|
|
||||||
i = 0; |
|
|
|||||||
j = 0; |
|
|
|||||||
pw[0] = |
buf; |
|
|
||||||
while |
(buf[i] != 0) { |
|
|
||||||
|
|
if |
(buf[i] == 0x20) { |
// Прочитали слово |
|
||||
|
|
|
|
|
buf[i] = 0; |
|
|
||
} |
|
|
pw[++j]= |
&buf[i + 1]; |
|
||||
|
|
|
|
|
|
|
|||
} |
|
i++; |
|
|
|||||
|
|
|
|
|
|
|
|
|
|
printf("%10s - Общий размер программы (в страницах)\n", |
pw[0]); |
||||||||
printf("%10s - Размер резидентной части памяти\n", |
pw[1]); |
||||||||
printf("%10s - Число разделяемых страниц\n", |
pw[2]); |
||||||||
printf("%10s - Число страниц 'code'\n", |
pw[3]); |
||||||||
printf("%10s - Число страниц data/stack\n", |
pw[4]); |
||||||||
printf("%10s - Число страниц library\n", |
pw[5]); |
||||||||
printf("%10s - Число страниц |
dirty («грязных»)\n", |
pw[6]); |
|||||||
puts("\nСодержимое файла /proc/self/maps:"); read_maps("/proc/self/maps", buf);
puts("Завершение проекта lab8.3 ..."); return EXIT_SUCCESS;
}
int read_maps(const char *fn, char *buf){ int fd;
ssize_t n;
fd = open(fn, O_RDONLY);
94
if(fd < 0){ perror(fn);
return EXIT_FAILURE;
}
while ((n = read(fd, buf, BUFSIZ - 1)) > 0){ buf[n] = 0;
puts(buf);
}
close(fd);
return EXIT_SUCCESS;
}
Задание 2.3
В среде разработки Eclipse, необходимо создать проект с именем lab8.3, руководствуясь текстом листинга 2.3.
Отладить и провести исследование работы полученной программы. Отразить краткое описание работы данного примера в личном отчете.
На листинге 2.4, представлен второй вариант программы, которая читает и выводит на терминал информацию из файлов /proc/self/statm и /proc/self/maps.
В этом примере, программа, относительно себя, выводит только содержимое файла /proc/self/statm, а затем создает дочерний процесс для вызова программы проекта lab8.3.
Листинг 2.4 — Второй пример вывода файлов /proc/self/stats и /proc/self/maps
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h>
int main(void) {
puts("Проект lab8.4 ...");
char buf[BUFSIZ]; int i, j, fd; ssize_t n;
char *pw[7];
fd = open("/proc/self/statm", O_RDONLY); if(fd < 0){
perror("open /proc/self/statm"); exit(EXIT_FAILURE);
}
puts("Содержимое файла /proc/self/statm:"); n = read(fd, buf, BUFSIZ);
if(fd < 0){
perror("read /proc/self/statm:"); exit(EXIT_FAILURE);
}
close(fd); buf[n - 1] = 0; puts(buf);
i = 0; j = 0;
pw[0] = buf;
|
95 |
|
while (buf[i] != 0) { |
|
|
if (buf[i] == 0x20) { |
// Прочитали слово |
|
buf[i] = 0; |
|
|
pw[++j]= &buf[i + 1]; |
|
|
} |
|
|
i++; |
|
|
} |
|
|
printf("%10s - Общий размер программы (в страницах)\n", |
pw[0]); |
|
printf("%10s - Размер резидентной части памяти\n", pw[1]); |
||
printf("%10s - Число разделяемых страниц\n", |
pw[2]); |
|
printf("%10s - Число страниц 'code'\n", |
pw[3]); |
|
printf("%10s - Число страниц data/stack\n", |
pw[4]); |
|
printf("%10s - Число страниц library\n", |
pw[5]); |
|
printf("%10s - Число страниц |
dirty («грязных»)\n", |
pw[6]); |
puts("\nЗапускаю дочерний процесс:"); system("lab8.3");
puts("\nДочерний процесс завершил работу ...");
puts("Завершение проекта lab8.4 ..."); return EXIT_SUCCESS;
}
Задание 2.4
Скопировать файл ~/lab8/lab8.3/Debug/lab8.3 в директорию ~/bin.
Всреде разработки Eclipse, создать проект с именем lab8.4, руководствуясь текстом листинга 2.4.
Отладить и провести исследование работы полученной программы. Сравнить системные характеристики программ lab8.3 и lab8.4. Отразить краткое описание работы данного примера в личном отчете.
Впредыдущих подразделах были рассмотрены случаи выделения памяти, которые осуществляются ядром ОС.
Вданном подразделе, мы изучим системные вызовы ОС, обеспечивающие выделение памяти для нужд программиста. Для этих целей существует ряд функций, позволяющих выделять и освобождать память ОЗУ динамически, во время выполнения программы. Эти функции имеют следующее определение:
#include <stdlib.h>
void *malloc(size_t size); void free(void *ptr);
void *calloc(size_t nmemb, size_t size); void *realloc(void *ptr, size_t size);
Семантика этих функций - следующая:
•malloc(...) - выделяет область неинициализированной памяти, размер которой указан в байтах, в качестве аргумента, и возвращает значение указателя на эту область; если значение аргумента равно нулю, то возвращается значение