2.2.4. Ошибки выделения ресурсов

2.2.4. Ошибки выделения ресурсов

Обычно при неудачном выполнении системного вызова наиболее приемлемое решение — отменить текущую операцию, но не завершить программу, так как можно восстановить ее нормальную работу. Один из способов сделать это — выйти из текущей функции, передав через оператор return код ошибки вызывающему модулю.

В случае, когда выход осуществляется посреди функции, важно убедиться в том, что ресурсы, выделенные в функции ранее, освобождены. К таким ресурсам относятся буферы памяти, дескрипторы и указатели файлов, временные файлы, объекты синхронизации и т.д. В противном случае, если программа продолжит выполняться, ресурсы окажутся потерянными.

В качестве примера рассмотрим функцию, загружающую содержимое файла в буфер. Функция выполняет такую последовательность действий:

1. выделяет буфер;

2. открывает файл;

3. читает содержимое файла и записывает его в буфер;

4. закрывает файл;

5. возвращает буфер вызывающему модулю.

Если файл не существует, этап 2 закончится неудачей. Подходящая реакция в этом случае — вернуть из функции значение NULL. Но если буфер уже был выделен на этапе 1, существует опасность потери этого ресурса. Нужно не забыть освободить буфер где-то в программе. Если же неудачей завершится этап 3, требуется не только освободить буфер перед выходом из функции, но и закрыть файл.

В листинге 2.6 показан пример реализации такой функции.

Листинг 2.6. (readfile.c) Освобождение ресурсов при возникновении аварийных ситуаций

#include <fcntl.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

char* read_from_file(const char* filename, size_t length) {

 char* buffer;

 int fd;

 ssize_t bytes_read;

 /* Выделяем буфер. */

 buffer = (char*)malloc(length);

 if (buffer == NULL)

  return NULL;

 /* Открываем файл. */

 fd = open(filename, O_RDONLY);

 if (fd == 1) {

  /* Открыть файл не удалось. Освобождаем буфер

     перед выходом. */

  free(buffer);

  return NULL;

 }

 /* Чтение данных. */

 bytes_read = read(fd, buffer, length);

 if (bytes_read != length) {

  /* Чтение не удалось. Освобождаем буфер и закрываем файл

     перед выходом. */

  free(buffer);

  close(fd);

  return NULL;

 }

 /* Все прошло успешно. Закрываем файл и возвращаем буфер

    в программу. */

 close(fd);

 return buffer;

}

При завершении программы операционная система Linux освобождает выделенную память, ссылки на открытые файлы и большинство других ресурсов, поэтому перед вызовом функции exit() нет необходимости удалять буферы и закрывать файлы. Но некоторые другие совместно используемые ресурсы приходится все же освобождать вручную. В частности, это относится к временным файлам и совместным буферам памяти: они способны "пережить" программу.