Кольцевой буфер



Кольцевой буфер

Мы только что рассмотрели традиционный способ ротации журналов для контроля за пространством, занимаемым постоянно растущими журналами. Позвольте представить вам более необычный подход, который вы можете добавить в свою копилку.

Вот обычный сценарий: выполняется отладка сервера, который выводит целый поток данных в журнал. Нас интересует только малая часть всех этих данных, вероятно, только те строки, которые выводятся сервером после выполнения определенных тестов на определенном клиенте. Если сохранять в журнале весь вывод, как обычно, это быстро заполнит жесткий диск. Ротация журналов с нужной частотой при таком количестве выводимых данных замедлит работу сервера. Что же делать?

Я написал программу 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;

}

Подобная программа может найти несколько интересных применений.



Содержание раздела