Программирование для Linux. Профессиональный подход
Шрифт:
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
— дополнительное число миллисекунд (должно быть меньше, чем 109). Работа функции
nanosleep
, как и функции sleep
, прерывается при получении сигнала. При этом функция возвращает значение -1, а в переменную errno записывается код EINTR
. Но у функции nanosleep
есть важное преимущество. Она принимает дополнительный аргумент — еще один указатель на структуру timespec
, в которую (если указатель не равен NULL
) заносится величина оставшегося интервала времени (т.е. разница между запрашиваемым и прошедшим промежутками времени). Благодаря этому можно легко возобновлять прерванные операции ожидания. В листинге 8.8 показана альтернативная реализация функции
sleep
. В отличие от стандартного системного вызова эта функция может принимать дробное число секунд и возобновлять операцию ожидания в случае прерывания по сигналу. Листинг 8.8. (better_sleep.c) Высокоточная реализация функции
sleep
#include <errno.h>
#include <time.h>
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. */
int rval = nanosleep(&tv, &tv);
if (rval == 0)
/* пауза успешно окончена. */
return 0;
else if (errno == EINTR)
/* Прерывание по сигналу. Повторная попытка. */
continue;
else
/* Какая-то другая ошибка. */
return rval;
}
return 0;
}
8.11. Функция readlink: чтение символических ссылок
Функция
readlink
определяет адресата символической ссылки. Она принимает три аргумента: путь к символической ссылке, буфер для записи адресата и длина буфера. Как ни странно, путевое имя, помещаемое в буфер, не завершается нулевым символом. Но поскольку в третьем аргументе возвращается длина буфера, добавить этот символ несложно. Если первый аргумент не является символической ссылкой, функция
readlink
возвращает -1, а в переменную errno записывается константа EINVAL
. Программа, представленная в листинге 8.9, показывает адресата символической ссылки, заданной в командной строке.
Листинг 8.9. (print-symlink.с) Отображение адресата символической ссылки
#include «errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
char target_path[256];
char* link_path = argv[1];
/* Попытка чтения адресата символической ссылки. */
int len =
readlink(link_path, target_path, sizeof(target_path));
if (len == -1) {
/* Функция завершилась ошибкой. */
if (errno == EINVAL)
/* Это не символическая ссылка. */
fprintf(stderr, "%s is not a symbolic link\n", link_path);
else
/* Произошла какая-то другая ошибка. */
perror("readlink");
return 1;
} else {
/* Завершаем путевое имя нулевым символом. */
target_path[len] = '\0';
/* Выводим результат. */
printf("%s\n", target_path);
Поделиться с друзьями: