Кольцевой буфер
Кольцевой буфер
Мы только что рассмотрели традиционный способ ротации журналов для контроля за пространством, занимаемым постоянно растущими журналами. Позвольте представить вам более необычный подход, который вы можете добавить в свою копилку.
Вот обычный сценарий: выполняется отладка сервера, который выводит целый поток данных в журнал. Нас интересует только малая часть всех этих данных, вероятно, только те строки, которые выводятся сервером после выполнения определенных тестов на определенном клиенте. Если сохранять в журнале весь вывод, как обычно, это быстро заполнит жесткий диск. Ротация журналов с нужной частотой при таком количестве выводимых данных замедлит работу сервера. Что же делать?
Я написал программу bigbuffy для решения этой головоломки, применив совершенно незамысловатый подход, bigbuffy считывает построчно поступающие на вход данные. Эти строки сохраняются в кольцевом буфере определенного размера. Когда буфер заполняется, он начинает вновь заполняться с вершины. Этот процесс чтения-записи продолжается до тех пор, пока bigbuffy не получит сигнал от пользователя. Получив сигнал, программа сбрасывает текущее содержимое буфера в файл и возвращается в свой нормальный цикл. На диске же остается лишь «окошко» в потоке данных из журнала, в котором показаны только те данные, которые нужны.
bigbuffy
можно использовать в паре с программой наблюдения за службой (подобной тем, которые можно найти в главе 5 «Службы имен TCP/IP»). Как только наблюдающая программа (монитор) замечает проблему, она может послать сигнал bigbuffy сбросить содержимое буфера на диск. Теперь у нас есть выдержка из журнала, относящаяся как раз к нужной проблеме (считаем, что буфер достаточно велик и монитор вовремя ее заметил).Вот упрощенная версия bigbuffy. Этот код длиннее примеров из предыдущей главы, но он не очень сложный. Мы будем его использовать в качестве трамплина для разрешения некоторых важных вопросов, таких как блокировка ввода и безопасность:
Souffsize = 200;
размер кольцевого буфера по умолчанию (строчках) use Getopt::Long;
анализируем параметры GetOptions("buffsize=i" => \$Duffsize, "dumpfile=s" => \$dumpfile);
устанавливаем обработчик сигнала и инициализируем счетчик &setup;простой цикл прочитать строку - сохранить строку
while (<>){
и помещаем строку в структуру данных. Заметьте, мы делаем 8 это сначала, даже если получаем сигнал. Лучше записать 8 лишнюю строчку, чем потерять строку данных, если в 8 процессе сброса данных что-то пойдет не так.
$buffer[$whatline] = $_;
8 куда деть следующую строку? (Swhatline %= $buffsize)++;
8 если получаем сигнал, сбрасываем текущий буфер if ($dumpnow) {
&dodump(); } }
sub setup {
die "ИСПОЛЬЗОВАНИЕ: $0 [--buffsize=<lines>] --dump-ile=<filename>" unless (length($dumpfile));
$SIG{ 'USR1'} = \&di;mpnow; n устанавливаем обработчик
Swhatline = 1; и начальная строка кольцевого буфера
простой обработчик сигнала, который просто устанавливает фла-8 исключения, см. perlipc(l) sub dumpnow {
Sdumpnow = 1;
}
флаг, существует ли уже файл my(@firststat,@secondstat); п для хранения вывода Istats
Sdumpnow = 0; № сбрасываем флаг и обработчик сигнала $SIG{ 'USR1'} = \&dumpnow;
if (-e Sdumpfile and (! -f Sdumpfile or -1 Sdurripfile)) {
warn "ПРЕДУПРЕЖДЕНИЕ: файл для сброса данных существует и не является, обычным текстовым файлом, пропускаем сброс данных.\п";
return undef; }
# необходимо принять специальные меры предосторожности при
# дописывании. Следующий набор операторов "if" выполняет
# несколько проверок при открытии файла для дописывания if (-e Sdumpfile) {
Sexists = 1;
unless(@firststat = Istat $dumpfile){
warn "Невозможно выяснить состояние Sdumpfile, пропускаем сброс данных.\n";
return undef; } if ($firststat[3] != 1) {
warn "Sdumpfile - жесткая ссылка, пропускаем сброс данных.\n";
return undef; } }
unless (open(DUMPFILE, "$dumpfile")){
warn "Невозможно открыть Sdumpfile для дописывания,
пропускаем сброс данных.\п"; return undef; > if (Sexists) {
unless (@secondstat = Istat DUMPFILE){
warn "Невозможно выяснить состояние открытого файла Sdumpfile,
пропускаем сброс данных.\п"; return undef; }
if ($firststat[0] != $secondstat[0] or
# проверяем номер устройства $firststat[1] != $secondstat[1] or
проверяем mode $firststat[7] != $secondstat[7]) tt проверяем размеры {
warn "ПРОБЛЕМА БЕЗОПАСНОСТИ: Istats не совпадают,
пропускаем сброс данных,\п"; return undef:
}
Sline = Swhatline;
print DUMPFILE "-".scalar(Iocaltime). C'-"x50)."\n";
do < Проблемы с пространством на диске 357
И если буфер не полный
last unless (defined $buffer[$line]) print DUMPFILE $buffer[$line];
Sline = (Sline == Sbuffsize) 9 1 : $iine+l; } while (Siine '= Swhatline);
close(DUMPFILE):
П проталкиваем активный буфер, чтобы не повторяв данные
# при последующем сбросе их в файл $whatline = 1; Sbuffer = ();
return 1;
}
Подобная программа может найти несколько интересных применений.