| netlib.narod.ru | < Назад | Оглавление | Далее > |
Код ядра демонстрирует ряд выдающихся идиом, часть из которых обсуждаются в этом разделе. Во время чтения кода ядра важно не столько понимание собственно идиом, а только тот факт, что они существуют и непротиворечиво применяются. Во время написания кода для ядра необходимо знать, какие идиомы используются ядром, и применять их в собственном коде.
Я дал идиомам имена, поэтому мне теперь проще о них говорить. Однако имена эти мои, и даны они только в интересах обсуждения. На практике мало кто явно ссылается на идиомы. Они — суть способ, в соответствии с которым ядро выполняет свою работу.
Одна общая идиома — это та, которую я называю идиома накопления ресурсов (resource acquisition idiom). В соответствии с этой идиомой, некоторая функция должна получать последовательность ресурсов — память, блокировки и т.д. Функция перейдет к получению следующего ресурса только после того, как будет успешно получен текущий ресурс. В конечном итоге, функция должна освободить все накопленные ресурсы и не предпринимать попытки освобождать те, при получении которых возникшли ошибки.
Идиома накопления ресурсов была показана в композиции с идиомой переменной ошибки (error variable idiom), которая предполагает использование временной переменной для записи возвращаемого из функции значения. Разумеется, множество функций делают это — тем-то и известна идиома переменной ошибки, когда некая переменная ошибки приеняется для того, чтобы справиться с управлением потоком, усложненным ради достижения высокой скорости. Значение переменной ошибки — либо 0 (успешное выполнение), либо отрицательная величина (ошибка). Рассмотренные две идиомы переходят из рук в руки, естественно приводя к коду, шаблон которого показан ниже:
int f(void)
{
int err;
resource * r1, * r2;
err = -ERR1; /* Предположить ошибку */
r1 = acquire_resource1();
if (!r1) /* Ресурс не получен */
goto out; /* Вернуть -ERR1 */
/* Получили ресурс r1; запрашиваем r2 */
err = -ERR2; /* Предположить ошибку */
if (!r2) /* Ресурс не получен */
goto out1; /* Вернуть -ERR2 */
/* Получили ресурсы r1 и r2 */
err = 0; /* Ошибок нет */
/* . . . Используем r1 и r2 . . . */
out2:
release_resource(r2);
out1:
release_resource(r1);
out:
return err;
}
Следует заметить, что уже само по себе существование переменной err указывает на присутствие идиомы переменной ошибки. Аналогично, имена меток out, out1, out2 непосредственно связаны с идиомой накопления ресурсов.
Всякий раз, когда достигается метка out2, r1 и r2 уже получены и должны быть освобождены. Всякий раз когда достигается метка out1 (либо строка за строкой, либо через goto), r2 — недоступен (возможно, r2 освобожден), но r1 — занят и должен быть освобожден. Аналогично, при достижении метки out, r1 и r2 освобождены, а err содержит код ошибки либо успешного завершения.
В рассмотренном примере не все присваивания err являются необходимыми. Реальный код следует приведенному шаблону, однако с некоторыми изменениями. Как показывают исследования, многие строки кода возвращают одно и то же значение ошибки, поэтому гораздо проще установить это значение один раз. В показанном выше примере преследовались чисто иллюстративные цели, реальный же пример кода подобного рода можно найти в функции sys_shmctl (строка 21654) в главе 9.
| netlib.narod.ru | < Назад | Оглавление | Далее > |