Программирование для Linux. Профессиональный подход - страница 19

стр.

>

>   path, strerror(error_code));

>  /* He завершаем программу; можно предоставить пользователю

>     шанс открыть другой файл. */

>  break;

> case ЕFAULT:

>  /* Переменная PATH содержит неправильный адрес. Это, скорее

>     всего, ошибка программы. */

>  abort();

> case ENOMEM:

>  /* Ядро столкнулось с нехваткой памяти. */

>  fprintf(stderr, "%s\n", strerror(error_code));

>  exit(1);

> default:

>  /* Произошла какая-то другая, непредвиденная ошибка. Мы

>     пытались обработать все возможные коды ошибок. Если

>     что-то пропущено, то это ошибка программы! */

>  abort();

> };

>}

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

>rval = chown(path, user_id, -1);

>assert(rval == 0);

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

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

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

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

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

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

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

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

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

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

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

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

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

>#include

>#include

>#include

>#include

>#include


>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() нет необходимости удалять буферы и закрывать файлы. Но некоторые другие совместно используемые ресурсы приходится все же освобождать вручную. В частности, это относится к временным файлам и совместным буферам памяти: они способны "пережить" программу.

2.3. Создание и использование библиотек

Практически со всеми программами компонуется одна или несколько библиотек. К любой программе, использующей функции языка С (например, >printf() или >malloc()), подключается библиотека времени выполнения. Если у программы есть графический интерфейс, вместе с ней компонуются библиотеки функций работы с окнами. Когда программа обращается к СУБД, она делает это посредством функции библиотеки, предоставленной разработчиком данной СУБД.