Perl для системного администрирования

       

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



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

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

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

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

}

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



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