$ ldd myprog | grep libc
libc.so.6 => /lib/tls/libc.so.6 (0x4004b000)
Есть два средства, с помощью которых прикладная программа может определить версию библиотеки GNU C в системе: тестирование констант или вызов библиотечной функции. Начиная с версии 2.0, в glibc определяются две константы, __GLIBC__ и __GLIBC_MINOR__, которые могут быть протестированы в ходе компиляции (в инструкциях #if). В системе с установленной glibc 2.12 эти константы будут иметь значения 2 и 12. Но использование этих констант не принесет пользы в программе, скомпилированной в одной системе, но запущенной в другой системе с отличающейся по версии библиотекой glibc. Учитывая вышесказанное, можно воспользоваться функцией gnu_get_libc_version() для определения версии glibc доступной во время исполнения программы.
#include <gnu/libc-version.h>
const char *gnu_get_libc_version(void);
Возвращает указатель на заканчивающуюся нулевым байтом статически размещенную строку, содержащую номер версии библиотеки GNU C
Функция gnu_get_libc_version() возвращает указатель на строку вида 2.12.
Информацию о версии можно также получить, воспользовавшись функцией confstr() для извлечения значения конфигурационной переменной (относящегося к конкретной glibc-библиотеке) _CS_GNU_LIBC_VERSION. В результате вызова функции будет возвращена строка вида glibc 2.12.
Почти каждый системный вызов и вызов библиотечной функции завершаются возвратом какого-либо значения, показывающего, чем все завершилось — успехом или неудачей. Надо всегда проверять код завершения, чтобы можно было убедиться в успешности вызова. Если успех вызову не сопутствовал, нужно предпринимать соответствующее действие — как минимум программа должна вывести на экран сообщение об ошибке, свидетельствующее о том, что случилось нечто неожиданное.
Несмотря на стремление сэкономить на наборе текста путем исключения подобных проверок (особенно после просмотра примеров программ, написанных под UNIX и Linux, где коды завершения не проверяются), это «экономия на спичках». Из-за отсутствия проверки кода, возвращенного системным вызовом или вызовом библиотечной функции, которая «в принципе не должна дать сбой», можно впустую потратить многие часы на отладку.
Есть несколько системных вызовов, никогда не дающих сбоев. Например, getpid() всегда успешно возвращает идентификатор процесса, а _exit() всегда прекращает процесс. Проверять значения, возвращаемые такими системными вызовами, нет никакого смысла.
Обработка ошибок системных вызовов
Возможные возвращаемые вызовом значения документируются на странице руководства по каждому системному вызову, и там показывается значение (или значения), свидетельствующее об ошибке. Обычно ошибка выявляется возвращением значения –1. Следовательно, системный вызов может быть проверен с помощью такого кода:
fd = open(pathname, flags, mode); /* Системный вызов для открытия файла */
if (fd == -1) {
/* Код для обработки ошибки */
}
…
if (close(fd) == -1) {
/* Код для обработки ошибки */
}
При неудачном завершении системного вызова для глобальной целочисленной переменной errno устанавливается положительное значение, позволяющее идентифицировать конкретную ошибку. Объявление errno, а также набора констант для различных номеров ошибок предоставляется за счет включения заголовочного файла <errno.h>. Все относящиеся к ошибкам символьные имена начинаются с E. Список возможных значений errno, которые могут быть возвращены каждым системным вызовом, предоставляется на каждой странице руководства в разделе заголовочного файла ERRORS. Простой пример использования errno для обнаружения ошибки системного вызова имеет следующий вид:
cnt = read(fd, buf, numbytes);
if (cnt == -1) {
if (errno == EINTR)
fprintf(stderr, "read was interrupted by a signal\n");
// чтение было прервано сигналом
else {
/* Произошла какая-то другая ошибка */
}
}
Успешно завершенные системные вызовы и вызовы библиотечных функций никогда не сбрасывают errno в 0, следовательно, эта переменная может иметь ненулевое значение из-за ошибки предыдущего вызова. Более того, SUSv3 разрешает успешно завершающей свою работу функции устанавливать для errno ненулевое значение (хотя это делают всего несколько функций). Поэтому при проверке на ошибку нужно всегда сперва проверить, не вернула ли функция значение, свидетельствующее о возникновении ошибки, и только потом исследовать errno для определения причины ошибки.