/* Запрет на запись в память. */
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()
. В отличие от стандартного системного вызова эта функция может принимать дробное число секунд и возобновлять операцию ожидания в случае прерывания по сигналу.
Листинг 8.8. ( better_sleep.c ) Высокоточная реализация функции 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. */
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
#include
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)
/* Это не символическая ссылка. */
Читать дальше