LINUX.ORG.RU

Проблема: чтение TCP-сокета на BASH приводит к overload'у

 , ,


0

2

Делаю так:

exec 6<>/dev/tcp/localhost/12345
while read -r l <&6; do
 ...do something with $l...
done
В результате при том, что «do something» делает одно и то же, ничего outside не меняется - процесс чтения сокета на BASH начинает со временем отжирать всё больше процессора и памяти, так что доходит до 90% загрузки одного ядра и более 80% расхода памяти.

Внимание вопрос: WTF, что это может быть?!!

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 1)

Ответ на: комментарий от Spoofing

Огромное спасибо!
Заодно ещё перекомпилировал BASH, обновив до версии 4.2 - вроде теперь полегче стало. Но нужно последить за скриптом: он сначала всегда жрал не очень много процессора и памяти, а через неделю его ядро убивало OOM Killer'ом...

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от Spoofing

Всё равно растёт потребление памяти и процессора. Странно, может, он теряет память куда-то...

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от Spoofing

У меня постоянно используется ассоциативный массив, но в нём одни и те же ключи, они просто перезаписываются в каждой итерации.
Интересно, что после использования внутреннего extended pattern matching'а (shopt -s extglob) нагрузка на процессор по сравнению с пайпом/sed'ом даже возросла. Как такое может быть...

DRVTiny ★★★★★
() автор топика

а не исключить ли нам конкретный шелл, спросишь ты меня
конечно, давай исключим, отвечу тебе я

попробуй какой-нить ksh/zsh для эксперимента

zolden ★★★★★
()

перепиши на перле/руби/whatever

DELIRIUM ☆☆☆☆☆
()

я смотрю ты какой-то сервак на баше пишешь о_О ? то тебе инифайлы потребовалось парсить, то вот теперь по сети чего-то. может стоит задуматся о смене инструмента для проекта? я яп имею в виду.

nanoolinux ★★★★
()

За несколько часов потребление памяти возросло с 0.3% до 3% Это при том, что скрипт память вообще не использует, он просто берёт по одной строке из сокета и кладёт их в определённый файл (вся хитрость только в том, что каждые 5 минут это уже новый файл со свлим именем, а файл, формирующийся в данный момент, имеет в имени «тильду»). Потребление CPU возросло «всего» на 6%.
В принципе, у меня есть основной код на Perl для замены этого скрипта (дописать чуть и пустить в дело), но мне принципиально хочется разобраться в том, что, собственно, происходит.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Насчёт CPU всё-таки не совсем верно: загрузка скачет, сейчас снизилась. Но потребление памяти растёт. Явно этот скрипт умирал именно из-за того, что его прибивало OOM Killer'ом

DRVTiny ★★★★★
() автор топика

А если попробовать на принципиально ином дистрибутиве? И что если попробовать тело цикла сделать echo $I > /dev/null? Может, проблема вовсе не в сокетах? Во время исполнения ничего не форкается? И да, может эта запись и одинакова по смыслу, но, все-же, я всегда делаю это как:
cat someting | (
while read i ;do
тело цикла
done
)

ktulhu666 ☆☆☆
()
Ответ на: комментарий от ktulhu666

Ох... Ладно, вот он, секрет полишинеля:

while read -r -u 6 -t 2 LINE; do
    if [[ $LINE =~ ^[[:space:]]*$ ]]; then
     STATE='wait 4 data stream norm-n'
     (( countBlankLine++ ))
     info_ "Got blank line, $countBlankLine occurence"
     if (( countBlankLine>nMaxBlankLines )); then
      error_ 'Limit of blank lines in block exceeded, we have to trigger a problem and wait some time'
      touch $trigNoData
      sleep 60
###SSH connection to the Vaisala server seems to be down. We are forced to restart overall data collector system now'
###      $BIN_DIR/GLDWD.sh &
     fi
     sleep 1
     continue
    elif (( countBlankLine )); then
     [[ -f $trigNoData ]] && rm -f $trigNoData
     countBlankLine=0
     STATE=$ST_READ_IN_CONT_LOOP
    fi
    
    oldDATE="${TS[DATE]:-$(findPrvDate)}"    
        
    if (( flGetDateFromFile )); then
     REC=( $LINE )
    else
     REC=( 0 $(date +"%Y %m %d %H %M") )
    fi
        
    TS=([YEAR]=${REC[1]} [MONTH]=${REC[2]} [DAY]=${REC[3]} [HOUR]=${REC[4]} [MINUTE]=${REC[5]})
    
    for v in MONTH DAY HOUR; do
     [[ ${TS[$v]} =~ ^[1-9]$ ]] && TS[$v]="0${TS[$v]}"
    done

    TS[DATE]="${TS[YEAR]}/${TS[MONTH]}/${TS[DAY]}"
    [[ ${TS[MINUTE]} =~ ^0([0-9])$ ]] && TS[MINUTE]=${BASH_REMATCH[1]}
    
    TS[INUM]=$(( TS[MINUTE]/INTERVAL + 1 ))
    (( TS[INUM] < 10 )) && TS[INUM]="0${TS[INUM]}"
    
    DATE_PATH="$ROOT/${TS[DATE]}"           
    [[ -d ${DATE_PATH}/${TS[HOUR]} ]] || mkdir -p "${DATE_PATH}/${TS[HOUR]}"
    if (( flFirstIter )) && [[ $oldDATE ]]; then
     oldCSV=$(tail -1 "$ROOT/$oldDATE/$CSV_LST")
    else            
     oldCSV=$CSV
    fi

    CSV="${TS[HOUR]}/~${CSV_FORMAT//%I%/${TS[INUM]}}" 
    if [[ "$oldDATE/$oldCSV" != "${TS[DATE]}/$CSV" ]]; then
     debug_ "We have finished control time period"
     if [[ $oldCSV && $oldCSV =~ '~' ]]; then
      readyCSV="${oldCSV/\~/}"
      debug_ "Rename temp file $oldCSV to $readyCSV"
      mv "$ROOT"/$oldDATE/{$oldCSV,$readyCSV}
      debug_ "Replace temp file name in $ROOT/$oldDATE/$CSV_LST"
      sed -i "s%$oldCSV%$readyCSV%" "$ROOT"/$oldDATE/$CSV_LST
     fi
     echo "$CSV" >> "$DATE_PATH"/$CSV_LST
    fi
    echo "${LINE//+([[:space:]])/;}" >>  "$DATE_PATH"/$CSV
done

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от nanoolinux

я смотрю ты какой-то сервак на баше пишешь о_О ? то тебе инифайлы потребовалось парсить, то вот теперь по сети чего-то. может стоит задуматся о смене инструмента для проекта? я яп имею в виду.

+1, если у него такая страсть к скриптам, то почему бы не воспользоваться более простым средством типа питона?

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от mky

Я сам догадался до unset'а, но проблему это, к сожалению, не решило. После зачистки кода и сокращения его объёма до минимума, у меня появилось нехорошее предчувствие, что дело в собственно чтении из сокета. Память явно теряет read, больше там попросту нечему течь.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

А мой вариант организации read уже пробовал? И top точно указывает, что это bash память отжирает?

ktulhu666 ☆☆☆
()
Ответ на: комментарий от DRVTiny

Если память течёт в read, то, наверное, можно написать тестовый скрипт в несколько строчек, без массивов. Просто чтение из сокета, в который пишет, допустим, nc. Тогда есть вероятность, что ЛОРовцы позапускают его на разных версиях bash'а и можно будет оформлять баг-репорт.

mky ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.