Кунг-фу стиля Linux: делаем все и сразу

Kate

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


Все переводы серии

























Поддержка Bash​


На удивление легко запустить несколько процессов под Bash. Прежде всего, процессы на обеих сторонах конвейера работают вместе, и это, вероятно, самый распространённый способ сценариев с использованием мультипрограммирования. Другими словами, подумайте, что произойдёт, если вы напишете ls | more.

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

Это легко, потому что программы синхронизируются по входному и выходному каналу. Однако, так же легко запустить несколько программ независимо друг от друга. Ключ использует "& amp;" для разделения программ или завершения строки сценария.

Достаточно легко преобразовать сценарий вроде:

find /mnt1/usr -name '*.so' >/tmp/libs1
find /mnt2/usr -name '*.so' > /tmp/libs2

# to

find /mnt1/usr -name '*.so' >/tmp/libs1 &
find /mnt2/usr -name '*.so' > /tmp/libs2 &


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

Проблема​


Есть только одна проблема. Хотя вы можете выделить несколько программ для совместной работы, эти программы редко не нуждаются в согласовании друг с другом.

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

alw@Enterprise:~$ banner hello & banner goodbye
[1] 173


# # ###### # # ####
#### #### #### ##### ##### # # ######
# # # # # # #
# # # # # # # # # # # # #
###### ##### # # # #
# # # # # # # ##### # #####
# # # # # # #
# ### # # # # # # # # # #
# # # # # # #
# # # # # # # # # # # #
# # ###### ###### ###### ####
#### #### #### ##### ##### # ######


[1]+ Done banner hello

Не то, что вы ожидали, правда? В зависимости от вашей системы первая программа может (или не может) получить весь свой вывод за один раз. Перемежение вывода — это не совсем то, что вам нужно.

Control​


Есть и другие проблемы. Возможно, вы захотите узнать PID нового процесса. Вы можете использовать bash’s job control. Однако на самом деле вам не нужно заходить так далеко. Команда jobs покажет вам, что вы исполняете в фоновом режиме из текущей оболочки. Если вы добавите -l или -p, вы также можете узнать PID.

Более простой способ узнать PID команды — использовать $!. Конечно, если вы работаете из командной строки оболочки, она также сообщает вам PID последней команды запуска. Например:

ls / & echo PID=$!
[2] 178
PID=178

Ваш номер PID почти наверняка будет другим.

Что дальше?​


Вооружили PID, что с ним делать? Есть две вещи, которые вы найдёте наиболее полезными. Сперва, вы можете использовать wait, чтобы понять, когда процесс (или процессы) завершены.

Ещё вы можете использовать kill для отправки сигналов фоновой программе. Это выходит за рамки этой статьи, но вы можете использовать сигналы для создания сложной связи между программами или для вызова поведения по умолчанию, такого как завершение.

Пример​


Рассмотрим случай, когда у вас есть куча файлов jpeg, и вы хотите преобразовать их в файлы png для веб-сайта с помощью ImageMagick. Вы можете попробовать этот простой скрипт:

#!/bin/bash
for I in *.jpg
do
convert "$I" "png/$(basename "$I" .jpg).png"
done
echo Copying files now...
ls png

Это упростит работу. Предположительно, в последней строке будет следующая команда копирования файла, например sftp, однако список каталогов использовался только для примера.

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

#!/bin/bash
for I in *.jpg
do
convert "$I" "png/$(basename "$I" .jpg).png" &
done
wait
echo Copying files now...
ls png


Всё ещё есть проблема​


Команда ожидания будет ждать любых подпроцессов, активных в оболочке. Возможно, это не то, что вам нужно, хотя в этом случае это, вероятно, нормально. Давайте поправим это:

#!/bin/bash
PIDs=""
for I in *.jpg
do
convert "$I" "png/$(basename "$I" .jpg).png" &
PIDs+="$! "
done
wait $PIDs
echo Copying files...
ls png


Если вы запустите отсчёт времени с множеством файлов, вы увидите большую разницу. На моём ноутбуке с несколькими картинками прямая версия заняла около 40 секунд. С финальной версией на это ушло чуть больше 10 секунд.

В заключении​


Довольно легко забыть, что вы можете легко делать больше, чем одну вещь за раз. Конечно, это тоже открывает целый ряд проблем. Если вам нужно защитить свои программы друг от друга, ознакомьтесь с предыдущей публикацией о критических разделах. Не все думают, что bash — отличный язык программирования, но он на удивление способен на многое, и, хотя он может быть хорош не для всего, он отлично подходит для некоторых задач.

 
Сверху