14.8. Уведомление о смене каталога

14.8. Уведомление о смене каталога

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

Системный вызов fcntl() используется для регистрации уведомлений об обновлениях каталога. В главе 11 уже говорилось о том, что этот системный вызов принимает три аргумента. Первый аргумент — это интересующий файловый дескриптор, второй — это команда, которую необходимо выполнить fcntl(), а последний — это целое число, специфическое для этой команды. Для уведомлений каталогов первый аргумент является файловым дескриптором, относящимся к интересующему каталогу. Это единственный случай, при котором каталог следует открывать с помощью нормального системного вызова open() вместо opendir(). Командой регистрации уведомлений является F_NOTIFY, а последний аргумент определяет, какие типы событий вызывают отправку сигнала. Это должен быть один или несколько перечисленных ниже флагов, объединенных по логическому "ИЛИ".

DN_ACCESS Файл в каталоге, который читается.
DN_ATTRIB Права владения или доступа к файлу в каталоге были изменены.
DN_CREATE В каталоге создан новый файл (включая новые жесткие ссылки на уже существующие файлы).
DN_DELETE Файл удален из каталога.
DN_MODIFY Файл в каталоге был модифицирован (тип модификации — усечение).
DN_RENAME Файл в каталоге был переименован.

Для отмены уведомления о событии вызовите fcntl() с командой F_NOTIFY и последним аргументом, равным нулю.

Обычно уведомление каталога автоматически отменяется после передачи одного сигнала. Для эффективного уведомления каталога окончательный аргумент для fcntl() должен быть объединен операцией "ИЛИ" с DN_MULTISHOT, что вызывает отправку сигналов для всех подходящих событий до отмены уведомления.

По умолчанию для уведомления каталога передается SIGIO. Если приложение желает использовать для этого другой сигнал (например, для разных каталогов могут понадобиться разные сигналы), можно применить команду F_SETSIG в fcntl(), а в качестве последнего аргумента определить нужный сигнал. Если используется F_SETSIG (даже если установлен сигнал SIGIO), ядро также помещает файловый дескриптор на каталог в элементе si_fd аргумента обработчика сигналов siginfo_t[103], позволяя приложению узнать, какие из контролируемых каталогов обновились[104].

Если контролируется несколько каталогов и для всех каталогов выбран один сигнал, крайне необходимо использовать сигнал реального времени, чтобы убедиться, что ни одно из событий не затерялось.

Ниже приведена программа, использующая уведомление о смене каталога для вывода сообщений об удалении либо добавлении файлов в любые контролируемые ею каталоги (их количество указывается в командной строке). Она отказывается принять SIGRTMIN при смене каталога и использует si_fd, чтобы обнаружить, какой именно каталог был изменен. С целью предотвращения условий состязаний программа использует сигналы с очередизацией и блокирование сигналов. Сигнал может быть доставлен только один раз — при вызове sigsuspend() в строке 203. Это обеспечивает повторное сканирование каталога в случае внесения изменений в каталог во время его сканирования; иначе эти изменения останутся незамеченными. Использование сигналов с очередизацией разрешает любые изменения каталога во время работы программы; эти сигналы доставляется при каждом новом вызове sigsuspend(), гарантируя, что ничего не пропущено.

  1: /* dirchange.с */

  2:

  3: #define _GNU_SOURCE

  4: #include <dirent.h>

  5: #include <errno.h>

  6: #include <fcntl.h>

  7: #include <signal.h>

  8: #include <stdio.h>

  9: #include <stdlib.h>

 10: #include <string.h>

 11: #include <unistd.h>

 12:

 13: /* Для сохранения имен файлов из каталога используется связный

 14:    список. Поле exists служит для хранения служебной информации

 15:    при проверке изменений. */

 16: struct fileInfo {

 17:  char * name;

 18:  struct fileInfo * next;

 19:  int exists;

 20: };

 21:

 22: /* Это глобальный массив. Он отображает файловые дескрипторы на пути

 23:    каталогов, сохраняет список файлов в каталоге и предоставляет

 24:    обработчику сигналов место для отображения того факта, что каталог

 25:    должен сканироваться повторно. Последний элемент имеет path,

 26:    равный NULL, обозначающий конец массива. */

 27:

 28: struct directoryInfo {

 29:  char * path;

 30:  int fd;

 31:  int changed;

 32:  struct fileInfo * contents;

 33: } * directoryList;

 34:

 35: /* Это никогда не возвращает пустой список; любой каталог содержит,

 36:    по крайней мере, "." и ".." */

 37: int buildDirectoryList(char * path, struct fileInfo ** listPtr) {

 38:  DIR * dir;

 39:  struct dirent * ent;

 40:  struct fileInfo * list = NULL;

 41:

 42:  if (!(dir = opendir(path))) {

 43:   perror("opendir");

 44:   return 1;

 45:  }

 46:

 47:  while ((ent = readdir(dir))) {

 48:   if (!list) {

 49:    list = malloc(sizeof(*list));

 50:    list->next = NULL;

 51:    *listPtr = list;

 52:   } else {

 53:    list->next = malloc(sizeof(*list));

 54:    list = list->next;

 55:   }

 56:

 57:   list->name = strdup(ent->d_name);

 58:  }

 59:

 60:  if (errno) {

 61:   perror("readdir");

 62:   closedir(dir);

 63:   return 1;

 64:  }

 65:

 66:  closedir(dir);

 67:

 68:  return 0;

 69: }

 70:

 71: /* Сканирует путь каталога в поисках изменений предыдущего

 72:    содержимого, как указано *listPtr. Связанный список

 73:    обновляется новым содержимым, и выводятся сообщения,

 74:    описывающие произошедшие изменения. */

 75: int updateDirectoryList(char * path, struct fileInfo ** listPtr) {

 76:  DIR * dir;

 77:  struct dirent * ent;

 78:  struct fileInfo * list = *listPtr;

 79:  struct fileInfo * file, * prev;

 80:

 81:  if (!(dir = opendir(path))) {

 82:   perror("opendir");

 83:   return 1;

 84:  }

 85:

 86:  for (file = list; file; file = file->next)

 87:   file->exists = 0;

 88:

 89:  while ((ent = readdir(dir))) {

 90:   file = list;

 91:   while (file && strcmp(file->name, ent->d_name))

 92:    file = file->next;

 93:

 94:   if (!file) {

 95:    /* новый файл, добавить его имя в список */

 96:    printf("%s создан в %s ", ent->d_name, path);

 97:    file = malloc(sizeof(*file));

 98:    file->name = strdup(ent->d_name);

 99:    file->next = list;

100:    file->exists = 1;

101:    list = file;

102:   } else {

103:    file->exists = 1;

104:   }

105:  }

106:

107:  closedir(dir);

108:

109:  file = list;

110:  prev = NULL;

111:  while (file) {

112:   if (!file->exists) {

113:    printf("%s удален из %s ", file->name, path);

114:    free(file->name);

115:

116:    if (!prev) {

117:     /* удалить головной узел */

118:     list = file->next;

119:     free(file);

120:     file = list;

121:    } else {

122:     prev->next = file->next;

123:     free(file);

124:     file = prev->next;

125:    }

126:   } else {

127:    prev = file;

128:    file = file->next;

129:   }

130:  }

131:

132:  *listPtr = list;

133:

134:  return 0;

135: }

136:

137: void handler(int sig, siginfo_t * siginfo, void * context) {

138:  int i;

139:

140:  for (i = 0; directoryList[i].path; i++) {

141:   if (directoryList[i].fd == siginfo->si_fd) {

142:    directoryList[i].changed = 1;

143:    return;

144:   }

145:  }

146: }

147:

148: int main(int argc, char ** argv) {

149:  struct sigaction act;

150:  sigset_t mask, sigio;

151:  int i;

152:

153:  /* Блокировать SIGRTMIN. Мы не хотим получать его нигде,

154:     кроме как внутри системного вызова sigsuspend(). */

155:  sigemptyset(&sigio);

156:  sigaddset(&sigio, SIGRTMIN);

157:  sigprocmask(SIG_BLOCK, &sigio, &mask);

158:

159:  act.sa_sigaction = handler;

160:  act.sa_flags = SA_SIGINFO;

161:  sigemptyset(&act.sa_mask);

162:  sigaction(SIGRTMIN, &act, NULL);

163:

164:  if (!argv[1]) {

165:   /* ни одного аргумента не передано, привести argc/argv

166:      к виду ".", как будто передается единственный аргумент */

167:   argv[1] = ".";

168:   argc++;

169:  }

170:

171:  /* каждый аргумент представляет собой отслеживаемый каталог */

172:  directoryList = malloc(sizeof(*directoryList) * argc);

173:  directoryList[argc - 1].path = NULL;

174:

175:  for (i = 0; i < (argc - 1); i++) {

176:   directoryList[i].path = argv[i + 1];

177:   if ((directoryList[i].fd =

178:    open(directoryList[i].path, O_RDONLY)) < 0) {

179:    fprintf(stderr, "ошибка при открытии %s: %s ",

180:    directoryList[i].path, strerror(errno));

181:    return 1;

182:   }

183:

184:   /* Отслеживание каталога перед первым сканированием;

185:      это гарантирует, что мы захватим файлы, созданные кем-то

186:      во время сканирования каталога. Если кто-то изменит его,

187:      будет сгенерирован сигнал (и заблокирован, пока

188:      мы не будем готовы принять его) */

189:   if (fcntl(directoryList[i].fd, F_NOTIFY, DN_DELETE |

190:    DN_CREATE | DN_RENAME | DN_MULTISHOT) ) {

191:    perror("fcntl F_NOTIFY");

192:    return 1;

193:   }

194:

195:   fcntl(directoryList[i].fd, F_SETSIG, SIGRTMIN);

196:

197:   if (build DirectoryList(directoryList[i].path,

198:    &directoryList[i].contents))

199:    return 1;

200:  }

201:

202:  while (1) {

203:   sigsuspend(&mask);

204:

205:   for (i = 0; directoryList[i].path; i++)

206:    if (directoryList[i].changed)

207:     if (updateDirectoryList(directoryList[i].path,

208:      &directoryList[i].contents))

209:      return 1;

210:  }

211:

212:  return 0;

213: }

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Пример: уведомление сигналом

Из книги UNIX: взаимодействие процессов автора Стивенс Уильям Ричард

Пример: уведомление сигналом Одним из способов исключения вызова каких-либо функций из обработчика сигнала является установка этим обработчиком глобального флага, который проверяется программным потоком для получения информации о приходе сообщения. В листинге 5.9


Пример: уведомление сигналом с отключением блокировки

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

Пример: уведомление сигналом с отключением блокировки Исправить описанную выше ошибку можно, отключив блокировку операции считывания сообщений. Листинг 5.10 содержит измененную версию программы из листинга 5.9. Новая программа считывает сообщения в неблокируемом


Пример: уведомление с использованием sigwait вместо обработчика

Из книги Программирование на языке Ruby [Идеология языка, теория и практика применения] автора Фултон Хэл

Пример: уведомление с использованием sigwait вместо обработчика Хотя программа из предыдущего примера работает правильно, можно повысить ее эффективность. Программа использует sigsuspend для блокировки в ожидании прихода сообщения. При помещении сообщения в пустую очередь


16.19.1 Модель каталога

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.

16.19.1 Модель каталога Информационная база каталога (Directory Information Base) распределена среди группы баз данных, управляемых агентами обслуживания каталогов (Directory Service Agent — DSA). Пользователи обращаются к каталогам через пользовательский агент каталога (Directory User Agent — DUA). DUA


10.1.30. Обход каталога

Из книги C++. Сборник рецептов автора Диггинс Кристофер

10.1.30. Обход каталога Метод класса foreach — это итератор, который последовательно передает в блок каждый элемент каталога. Точно так же ведет себя метод экземпляра each.Dir.foreach("/tmp") { |entry| puts entry }dir = Dir.new("/tmp")dir.each { |entry| puts entry }Оба фрагмента печатают одно и то же (имена всех файлов и


10.1.31. Получение содержимого каталога

Из книги Яндекс для всех автора Абрамзон М. Г.

10.1.31. Получение содержимого каталога Метод класса Dir.entries возвращает массив, содержащий все элементы указанного каталога:list = Dir.entries("/tmp") # %w[. .. alpha.txt beta.doc]Как видите, включаются и элементы, соответствующие текущему и родительскому каталогу. Если они вам не нужны, придется


14.4. Чтение содержимого каталога

Из книги Linux программирование в примерах автора Роббинс Арнольд

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


10.10. Создание каталога

Из книги Введение в криптографию автора Циммерманн Филипп

10.10. Создание каталога ПроблемаТребуется создать каталог, причем эта операция должна быть переносимой, т.е. в ней не должен использоваться специфичный для конкретной ОС программный интерфейс.РешениеНа большинстве платформ вы сможете использовать системный вызов mkdir,


10.11. Удаление каталога

Из книги автора

10.11. Удаление каталога ПроблемаТребуется удалить каталог, причем эта операция должна быть переносимой, т.е. в ней не должен использоваться специфичный для конкретной ОС программный интерфейс.РешениеНа большинстве платформ вы сможете воспользоваться системным вызовом


12.3. Уведомление одного потока другим

Из книги автора

12.3. Уведомление одного потока другим ПроблемаИспользуется шаблон, в котором один поток (или группа потоков) выполняет какие-то действия, и требуется сделать так, чтобы об этом узнал другой поток (или группа потоков). Может использоваться главный поток, который передает


1.7.3. Структура каталога

Из книги автора

1.7.3. Структура каталога На сегодня в каталоге Яндекса 15 разделов первого уровня (рис. 1.16). Помимо привычных, встречающихся и в иных каталогах разделов — Отдых, Бизнес, Культура и иных, в этом каталоге есть уникальный раздел. Он предназначен для детей и называется "Интернет


Меню каталога

Из книги автора

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


Страница каталога

Из книги автора

Страница каталога На самой странице каталога пользователь обязательно должен видеть товары – конкретные предложения о покупке. Чаще всего владельцы интернет-магазинов спрашивают, сколько товаров нужно показывать на странице каталога.Однозначного ответа нет. Это


Уведомление об аннулировании сертификата

Из книги автора

Уведомление об аннулировании сертификата После аннулирования сертификата крайне важно оповестить всех потенциальных корреспондентов, что он более недействителен. Наиболее простой способ оповещения в среде PGP — это размещение аннулированного сертификата на