Паралельное выполнение в BASH (Казалось бы должно работать...)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

(+ssvda)
Сообщения: 31
ОС: Debian GNU/Linux Etch (only)

Паралельное выполнение в BASH

Сообщение (+ssvda) »

Может меня нужно послать в раздел с X'ами. Но что-то мне подсказывает, что проблема програмная:)

Итак, в ~/.xinit есть следующие строки:

Код: Выделить всё

WMS_DEATH_LIST=""

function wms_start_service()
{
    local SERVICE=$1
    echo "Starting $SERVICE..." >&2
    $SERVICE &
    PID=$!
    echo "Starting $SERVICE... done" >&2
    WMS_DEATH_NOTE="$(echo $WMS_DEATH_LIST)$(echo)$PID"
}

function wms_stop_service()
{
    kill -s SIGQUIT $1
    wait $1
}

function wms_killall_services()
{
    local I
    for I in $WMS_DEATH_LIST
    do
        wms_stop_service $I
    done
}

WM_PID=$(wms_start_service $WM)
WM_PANEL_PID=$(wms_start_service $PANEL)
WM_KLIPPER_PID=$(wms_start_service klipper)

wait $WM_PID

wms_killall_services


WM и WM_PANEL --- это приложения менеджера окон и панели соответственно.

Казалось бы все просто... wms_start_service должна запускать процесс параллельно с исполняющей оболочкой и продолжать выполнение. именно так и происходит, если просто открыть терминал с bash ввести (методом копипаста) туда эти функции, а потом пользоваться ими как показано.

Но при использовании startx все несколько странно... На экране появляется:

Код: Выделить всё

Starting awesome...
Starting awesome... done


После этого он работает. После выхода из него так же запускается панель. И снова ждет, пока ее не прибьют. Потом запускается клиппер. И тоже ждет пока его не убьют.

Объясните мне бестолковому, что я делаю не так? Наверняка тут какая-то дурость есть, но я ее не вижу о_О.

Upd.: Могу даже уточнить формулировку. Какого черта оболочка не покидает функцию wms_start_service до тех пор, пока запущенное в ней приложение не будет завершено? При том только при вызове из startx. Во всех остальных случаях все работает. Более того, если в том же скрипте просто запускать процессы (без использования функций), то все выполняется как надо. Так же этот скрипт работает, если заменить wm и панель на какие-нибудь приложения и просто запустить. Я уже ничего не понимаю.
Спасибо сказали:
korey4ik
Сообщения: 6

Re: Паралельное выполнение в BASH

Сообщение korey4ik »

Тут дело в том, что шелл не может перейти от

Код: Выделить всё

WM_PID=$(wms_start_service $WM)

к

Код: Выделить всё

WM_PANEL_PID=$(wms_start_service $PANEL)

пока переменная WM_PID не получит корректное значение.
А это в свою очередь произойдёт, только когда функция wms_start_service завершится. А она завершится, когда приложение $WM закончит своё выполнение. :crazy:

Моим решением было бы - вместо того, чтобы ждать возвращаемого функцией значения, пусть она(wms_start_service) изменяет какую-нибудь глобальную переменную, скажем WMS_LAST_PID. А уже после вызова, если требуется, можно эту переменную соответствующим образом обработать.
Например так:

Код: Выделить всё

WMS_DEATH_LIST=""
WMS_LAST_PID=""

function wms_start_service()
{
    local SERVICE=$1
    echo "Starting $SERVICE..." >&2
    $SERVICE &

    WMS_LAST_PID="$!"

    echo "Starting $SERVICE... done" >&2
    WMS_DEATH_NOTE="$(echo $WMS_DEATH_LIST)$(echo)$PID"
}

function wms_stop_service()
{
    kill -s SIGQUIT $1
    wait $1
}

function wms_killall_services()
{
    local I
    for I in $WMS_DEATH_LIST
    do
        wms_stop_service $I
    done
}

wms_start_service $WM
WM_PID="$WMS_LAST_PID"

wms_start_service $PANEL
WM_PANEL_PID="$WMS_LAST_PID"

wms_start_service klipper
WM_KLIPPER_PID="$WMS_LAST_PID"

wait $WM_PID

wms_killall_services
Спасибо сказали:
Аватара пользователя
Luinnar
Сообщения: 246
ОС: Solaris, Debian, Ubuntu

Re: Паралельное выполнение в BASH

Сообщение Luinnar »

korey4ik правильно проблему описал, просто запущенный в бэкграунде процесс может выводить что-либо в стандартный поток, и всё это должно быть сохранено в переменной WM_PID, следовательно нужно ждать завершения программы - поэтому и "висим". Только одно но: зачем ещё одну переменную модифицировать, когда список PID запущенных процессов и так составляется? Я бы вот такое решение предложил:

Код: Выделить всё

WMS_DEATH_LIST=""

function wms_start_service()
{
    local SERVICE=$@
    echo -n "Starting $SERVICE..." >&2
    $SERVICE &
    PID=$!
    echo "done." >&2
    if [[ $WMS_DEATH_LIST == "" ]]
      then WMS_DEATH_LIST="$PID"
      else WMS_DEATH_LIST="$WMS_DEATH_LIST $PID"
    fi
}

function wms_stop_service()
{
    echo -n "Stopping $1..." >&2
    kill -s SIGQUIT $1
    wait $1
    echo "done."  >&2
}

function wms_killall_services()
{
    local I
    for I in $WMS_DEATH_LIST
    do
        wms_stop_service $I
    done
}

WM="sleep 5"
PANEL="sleep 7"
klipper="sleep 10"

wms_start_service $WM
wms_start_service $PANEL
wms_start_service $klipper

wms_killall_services


Пара замечаний:
1. Использование $WMS_DEATH_NOTE вместо $WMS_DEATH_LIST намеренно или это опечатка?
2. С инициализацией $WMS_DEATH_NOTE явно перебор, зачем дважды echo вызывать, когда можно просто пробел вставить.
3. Ну и в конце ждать нужно, наверное, все три процесса, а не только первый не так ли?
Спасибо сказали:
Аватара пользователя
Luinnar
Сообщения: 246
ОС: Solaris, Debian, Ubuntu

Re: Паралельное выполнение в BASH

Сообщение Luinnar »

А ещё остановку сервисов можно тоже параллельной сделать:

Код: Выделить всё

WMS_DEATH_LIST=""

function wms_start_service()
{
    local SERVICE=$@
    echo -n "Starting $SERVICE..." >&2
    $SERVICE &
    PID=$!
    echo "done." >&2
    if [[ $WMS_DEATH_LIST == "" ]]
      then WMS_DEATH_LIST="$PID"
      else WMS_DEATH_LIST="$WMS_DEATH_LIST $PID"
    fi
}

function wms_stop_service()
{
    echo "Stopping $1..." >&2
    kill -s SIGQUIT $1
}

function wms_killall_services()
{
    local I
    for I in $WMS_DEATH_LIST
    do
        wms_stop_service $I
    done
    wait
    echo 'Services stopped.' >&2
}

WM="sleep 5"
PANEL="sleep 7"
klipper="sleep 10"

wms_start_service $WM
wms_start_service $PANEL
wms_start_service $klipper

wms_killall_services
Спасибо сказали: