Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

Linux программирование в примерах - Арнольд Роббинс

Читать онлайн Linux программирование в примерах - Арнольд Роббинс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 64 65 66 67 68 69 70 71 72 ... 165
Перейти на страницу:

Если ни одно из этих условий не отмечается, размер вычисляется либо в соответствии с размером в struct stat, либо в соответствии с числом блоков диска (строки 73–75) Это решение основывается на переменной apparent_size, которая установлена при использовании опции --apparent-size.

78 if (first_call)

79 {

80  n_alloc = info->level + 10; /* Allocate arrays */

81  sum_ent = XCALLOC(uintmax_t, n_alloc); /* to hold sums */

82  sum_subdir = XCALLOC(uintmax_t, n_alloc);

83 }

84 else

85 {

86  /* FIXME: Стыдно, что нам нужно приводить к типу size_t для избежания

87     предупреждений gcc о 'сравнении между знаковым и беззнаковым'.

88     Возможно, неизбежно, при условии, что члены структуры FTW

89     имеют тип 'int' (исторически), так как мне нужно, чтобы переменные

90     вроде n_alloc и prev_level имели осмысленные типы. */

91  if (n_alloc <= (size_t)info->level)

92  {

93   n_alloc = info->level * 2; /* Удвоить сумму */

94   sum_ent = XREALLOC(sum_ent, uintmax_t, realloc); /* И выделить повторно */

95   sum_subdir = XREALLOC(sum_subdir, uintmax_t, n_alloc);

96  }

97 }

98

99 size_to_print = size;

Строки 78–97 управляют динамической памятью, используемой для хранения статистики о размере файла, first_call является статической переменной (строка 12), которая равна true при первом вызове process_file(). В этом случае вызывается calloc() (через упаковывающий макрос в строках 81–82; это обсуждалось в разделе 3.2.1.8 «Пример чтение строк произвольной длины»). Остальную часть времени first_call равно false, и используется realloc() (снова через упаковывающий макрос, строки 91–96).

Строка 99 заносит значение size в size_to_print; эта переменная может обновляться в зависимости от того, должна ли она включать размеры дочерних элементов. Хотя size могла бы использоваться повторно, отдельная переменная упрощает чтение кода.

101 if (!first_call)

102 {

103  if ((size_t)info->level == prev_level)

104  {

105   /* Обычно самый частый случай. Ничего не делать. */

106  }

107  else if ((size_t)info->level > prev_level)

108  {

109   /* Нисхождение по иерархии.

110      Очистить аккумуляторы для *всех* уровней между prev_level

111      и текущим. Глубина может значительно меняться,

112      например, от 1 до 10. */

113   int i;

114   for (i = prev_level +1; i <= info->level; i++)

115    sum_ent[i] = sum_subdir[i] = 0;

116  }

117  else /* info->level < prev_level */

118  {

119   /* Восхождение по иерархии.

120      nftw обрабатывает каталог лишь после всех элементов,

121      в которых был обработан каталог. Когда глубина уменьшается,

122      передать суммы от детей (prev_level) родителям.

123      Здесь текущий уровень всегда меньше, чем

124      предыдущий. */

125   assert (<size_t) info->level == prev_level - 1);

126   size_to_print += sum_ent[prev_level];

127   if (!opt_separate_dirs)

128    size_to_print += sum_subdir[prev_level];

129   sum_subdir[info->level] += (sum_ent[prev_level]

130    + sum_subdir[prev_level]);

131  }

132 }

Строки 101–132 сравнивают текущий уровень с предыдущим. Возможны три случая.

Уровни те же самые

В этом случае нет необходимости беспокоиться о статистике дочерних элементов. (Строки 103–106.)

Текущий уровень выше предыдущего

В этом случае мы спустились по иерархии, и статистику нужно восстановить (строки 107–116). Термин «аккумулятор» в комментарии подходящий: каждый элемент аккумулирует общее дисковое пространство, использованное на этом уровне. (На заре вычислительной техники регистры центрального процессора часто назывались «аккумуляторами».)

Текущий уровень ниже предыдущего

В этом случае мы завершили обработку всех дочерних элементов каталога и только что вернулись обратно в родительский каталог (строки 117–131). Код обновляет суммы, включая size_to_print.

134 prev_level = info->level; /* Установить статические переменные */

135 first_call = 0;

136

137 /* Включить элемент каталога в общую сумму для содержащего

138    каталога, если не указана --separate-dirs (-S). */

139 if (!(opt_separate_dirs && IS_FTW_DIR_TYPE(file_type)))

140  sum_ent[info->level] += size;

141

142 /* Даже если каталог нельзя прочесть или перейти в него,

143    включить его размер в общую сумму, ... */

144 tot_size += size;

145

146 /* ...но не выводить для него итог, поскольку без размера(-ов)

147    потенциальных элементов, это может сильно запутывать. */

148 if (file_type == FTW_DNR || file_type == FTW_DCH)

149  return 0;

150

151 /* Если мы не считаем элемент, например, потому что это прямая

152    ссылка на файл, который уже посчитан (и --count-links), не

153    выводить для него строку. */

154 if (!print)

155  return 0;

Строки 134–135 устанавливают статические переменные prev_level и first_call таким образом, что они содержат правильные значения для последующего вызова process_file(), гарантируя, что весь предыдущий код работает правильно.

Строки 137–144 выверяют статистику на основе опций и типа файла. Комментарии и код достаточно просты. Строки 146–155 сразу завершают функцию, если сведения не должны выводиться.

157  /* FIXME: Это выглядит подозрительно годным для упрощения. */

158  if ((IS_FTW_DIR_TYPE(file_type) &&

159   (info->level <= max_depth || info->level == 0))

160   || <(opt_all && info->level <= max_depth) || info->level == 0))

161  {

162   print_only_size(size_to_print);

163   fputc('t', stdout);

164   if (arg_length)

165   {

166    /* Вывести имя файла, но без суффикса каталога '.' или '/.'

167       который мы, возможно, добавили в main. */

168    /* Вывести все до добавленной нами части. */

169    fwrite(file, arg_length, 1, stdout);

170    /* Вывести все после добавленного нами. */

171    fputs(file + arg_length + suffix_length

172     + (file[arg_length + suffix_length] == '/'), stdout);

173   }

174   else

175   {

176    fputs(file, stdout);

177   }

178   fputc('n', stdout);

179   fflush(stdout);

180  }

181

182  return 0;

183 }

Условие в строках 158–160 сбивает с толку, и комментарий в строке 157 указывает на это. Условие утверждает: «Если (1a) файл является каталогом и (1b) уровень меньше максимального для вывода (переменные — -max-depth и max_depth) или нулевой, или (2a) должны быть выведены все файлы и уровень меньше, чем максимальный для вывода, или (2b) уровень нулевой», тогда вывести файл. (Версия du после 5.0 использует в этом случае несколько менее запутанное условие.)

Строки 162–179 осуществляют вывод. Строки 162–163 выводят размер и символ TAB Строки 164–173 обрабатывают специальный случай. Это объяснено далее в du.c, в строках файла 524–529:

524 /* При разыменовании лишь аргументов командной строки мы

525    используем флаг nftw FTW_PHYS, поэтому символическая ссылка

526    на каталог, указанная в командной строке, в норме не

527    разыменовывается. Для решения этого мы идем на издержки,

528    сначала добавляя '/.' (или '.'), а затем удаляем их каждый раз

529    при выводе имени производного файла или каталога. */

В этом случае arg_length равен true, поэтому строки 164–173 должны вывести первоначальное имя, а не измененное В противном случае строки 174–177 могут вывести имя как есть.

Фу! Куча кода. Мы находим, что это верхний уровень спектра сложности, по крайней мере, насколько это может быть просто представлено в книге данного содержания. Однако, он демонстрирует, что код из реальной жизни часто бывает сложным. Лучшим способом справиться с этой сложностью является ясное именование переменных и подробные комментарии du.с в этом отношении хорош; мы довольно легко смогли извлечь и изучить код без необходимости показывать все 735 строк программы!

8.6. Изменение корневого каталога: chroot()

Текущий рабочий каталог, установленный с помощью chdir() (см. раздел 8.4.1 «Изменение каталога — chdir() и fchdir()»), является атрибутом процесса, таким же, как набор открытых файлов. Он также наследуется новыми процессами.

Менее известным является то, что у каждого процесса есть также текущий корневой каталог. Это именно на этот каталог ссылается имя пути /. В большинстве случаев корневые каталоги процесса и системы идентичны. Однако, суперпользователь может изменить корневой каталог с помощью (как вы догадались) системного вызова chroot():

1 ... 64 65 66 67 68 69 70 71 72 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии