Марк Митчелл - Программирование для Linux. Профессиональный подход
- Название:Программирование для Linux. Профессиональный подход
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2002
- Город:Москва
- ISBN:5-8459-0243-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марк Митчелл - Программирование для Linux. Профессиональный подход краткое содержание
Данная книга в основном посвящена программированию в среде GNU/Linux. Авторы применяют обучающий подход, последовательно излагая самые важные концепции и методики использования расширенных возможностей системы GNU/Linux в прикладных программах. Читатели научатся писать программы, к интерфейсу которых привыкли пользователи Linux; освоят такие технологии, как многозадачность, многопотоковое программирование, межзадачное взаимодействие и взаимодействие с аппаратными устройствами; смогут улучшить свои программы, сделав их быстрее, надежнее и безопаснее; поймут особенности системы GNU/Linux, ее ограничения, дополнительные возможности и специфические соглашения.
Книга предназначена для программистов, уже знакомых с языком С и имеющих базовый опыт работы в GNU/Linux.
Программирование для Linux. Профессиональный подход - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Функция munlосkall()
разблокирует всю память текущего процесса.
Контролировать использование памяти удобнее всего с помощью команды top
. В колонке SIZE
ее выходных данных показывается размер виртуального адресного пространства каждой программы (общий размер сегментов кода, данных и стека с учетом выгруженных страниц). В колонке RSS
приводится объем резидентной части программы. Сумма значений в столбце RSS
не может превышать имеющийся объем ОЗУ, а суммарный показатель по столбцу SIZE
не может быть больше 2 Гбайт (в 32-разрядных версиях Linux).
Функции семейства mlock()
объявлены в файле .
8.9. Функция mprotect(): задание прав доступа к памяти
В разделе 5.3, "Отображение файлов в памяти", рассказывалось о том, как осуществляется отображение файла в памяти. Вспомните, что третьим аргументом функции mmap()
является битовое объединение флагов доступа: флаги PROT_READ
, PROT_WRITE
и PROT_EXEC
задают права чтения, записи и выполнения файла, а флаг PROT_NONE
означает запрет доступа. Если программа пытается выполнить над отображаемым файлом недопустимую операцию, ей посылается сигнал SIGSEGV
(нарушение сегментации), который приводит к завершению программы.
После того как файл был отображен в памяти, изменить права доступа к нему позволяет функция mprotect()
. Ее аргументами является адрес области памяти, размер области и новый набор флагов доступа. Область должна состоять из целых страниц, т.е. начинаться и заканчиваться на границе между страницами.
Учтите, что память, выделяемая функцией malloc()
, обычно не выравнивается по границе страниц, даже если размер области кратен размеру страницы. Если требуется защищать память, выделяемую функцией malloc()
, нужно запросить более крупный блок, а затем найти в нем участок, выровненный по границе страниц.
Кроме того, с помощью функции mmap()
можно обойти функцию malloc()
и запрашивать память непосредственно у ядра Linux.
Предположим, к примеру, что программа выделяет страницу, отображая в памяти файл /dev/zero
. Память инициализируется как для чтения, так и для записи:
int fd = open("/dev/zero", O_RDONLY);
char* memory =
mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
close(fd);
Далее программа запрещает запись в эту область памяти, вызывая функцию mprotect()
:
mprotect(memory, page_size, PROT_READ);
Существует оригинальная методика контроля памяти: можно защитить область памяти с помощью функций mmap()
и mprotect()
, а затем обрабатывать сигнал SIGSEGV
, посылаемый при попытке доступа к этой памяти. Эта методика иллюстрируется в листинге 8.7.
mprotect()
#include
#include
#include
#include
#include
#include
#include
#include
static int alloc_size;
static char* memory;
void segv_handler(int signal_number) {
printf("memory accessed!\n");
mprotect(memory, alloc_size, PROT_READ | PROT_WRITE);
}
int main() {
int fd;
struct sigaction sa;
/* Назначение функции segv_handler() обработчиком сигнала
SIGSEGV. */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &segv_handler;
sigaction(SIGSEGV, &sa, NULL);
/* Выделение одной страницы путем отображения в памяти файла
/dev/zero. Сначала память доступна только для записи. */
alloc_size = getpagesize();
fd = open("/dev/zero", O_RDONLY);
memory =
mmap(NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
/* Запись на страницу для получения ее копии в частное
использование. */
memory[0] = 0;
/* Запрет на запись в память. */
mprotect(memory, alloc_size, PROT_NONE);
/* Попытка записи в память. */
memory[0] = 1;
/* Удаление памяти. */
printf("all done\n");
munmap(memory, alloc_size);
return 0;
}
Программа работает по следующей схеме.
1. Задается обработчик сигнала SIGSEGV
.
2. Файл /dev/zero
отображается в памяти, из которой выделяется одна страница. В эту страницу записывается инициализирующее значение, благодаря чему программе предоставляется частная копия страницы.
3. Программа защищает память, вызывая функцию mprotect()
с флагом PROT_NONE
.
4. Когда программа впоследствии обращается к памяти, Linux посылает ей сигнал SIGSEGV
, который обрабатывается в функции segv_handler()
. Обработчик сигнала отменяет защиту памяти, разрешая выполнить операцию записи.
5. Программа удаляет область память с помощью функции munmap()
.
8.10. Функция nanosleep(): высокоточная пауза
Функция nanosleep()
является более точной версией стандартной функции sleep()
, принимая указатель на структуру типа timespec
, где время задается с точностью до наносекунды, а не секунды. Правда, особенности работы ОС Linux таковы, что реальная точность оказывается равной 10 мс, но это все равно выше, чем в функции sleep()
. Функцию nanosleep()
можно использовать в приложениях, где требуется запускать различные операции с короткими интервалами между ними.
В структуре timespec
имеются два поля:
■ tv_sес
— целое число секунд;
■ tv_nsec
— дополнительное число миллисекунд (должно быть меньше, чем 10 9).
Работа функции nanosleep()
, как и функции sleep()
, прерывается при получении сигнала. При этом функция возвращает значение -1, а в переменную errno записывается код EINTR
. Но у функции nanosleep()
есть важное преимущество. Она принимает дополнительный аргумент — еще один указатель на структуру timespec
, в которую (если указатель не равен NULL
) заносится величина оставшегося интервала времени (т.е. разница между запрашиваемым и прошедшим промежутками времени). Благодаря этому можно легко возобновлять прерванные операции ожидания.
В листинге 8.8 показана альтернативная реализация функции sleep()
. В отличие от стандартного системного вызова эта функция может принимать дробное число секунд и возобновлять операцию ожидания в случае прерывания по сигналу.
sleep()
#include
#include
int better_sleep(double sleep_time) {
struct timespec tv;
/* Заполнение структуры timespec на основании указанного числа
секунд. */
tv.tv_sec = (time_t)sleep_time;
/* добавление неучтенных выше наносекунд. */
tv.tv_nsec = (long)((sleep_time - tv.tv_sec) * 1e+9);
while (1) {
/* Пауза, длительность которой указана в переменной tv.
В случае прерывания по сигналу величина оставшегося
промежутка времени заносится обратно в переменную tv. */
Интервал:
Закладка: