Назад | Перейти на главную страницу

Есть ли способ поставить в очередь долго выполняющиеся задачи?

Есть ли способ сделать в терминале unix следующее:

  1. Начать длительный процесс
  2. Добавьте еще один длительный процесс, который будет запускаться, когда будет выполнено предыдущее
  3. Повторяйте шаг 2, пока я не поставлю в очередь процессы, которые мне нужно запустить.

Причина, по которой я спрашиваю, заключается в том, что у меня есть кое-какие долгосрочные дела, которые мне нужно сделать. Я мог бы поместить все команды в простой сценарий bash и просто выполнить его, но проблема в том, что я не всегда точно знаю, что мне нужно запустить. Поэтому, если я запускаю сценарий, а затем запоминаю еще один, который мне нужно запустить, мне нужно несколько раз нажать ^ C, пока все процессы не будут убиты, отредактировать сценарий и добавить новый процесс, а затем снова запустить все это.

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

Надеюсь, это имело смысл ... Возможно ли что-нибудь подобное?

Оболочка для этого идеально подходит.

  1. Запускаем первую программу.
  2. Нажмите Ctrl-z приостановить программу.
  3. Запускаем следующую программу.
  4. Повторите шаги 2 и 3, чтобы добавить другие программы в очередь. Все программы будут в приостановленном режиме.
  5. Теперь запустите очередь так:

    while fg; do :; done
    

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

Если вам нужно будет выйти из системы (например, программы будут работать много дней), рассмотрите возможность выполнения вышеуказанных шагов в screen.

Ответ на ваш общий вопрос: Абсолютно. На заре компьютерных технологий пакетная обработка была единственным способом сделать что-либо, и даже когда были изобретены многопользовательские интерактивные системы, возможность пакетной обработки была нормой для больших заданий. И сегодня это обычно делается в средних и крупных средах с использованием таких систем, как Sun Grid Engine или Крутящий момент.

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

  1. Создайте структуру каталогов, соответствующую вашим целевым дискам:

    ~/copysystem/drive1/ ~/copysystem/drive2/ ~/copysystem/drive3/

  2. Установить Incron.

  3. Настройте запись incrontab для каждого из этих каталогов, которая автоматически запускает ваш сценарий копирования на IN_MOVED_TO.

  4. Сделайте свой сценарий либо а) убить все предыдущие экземпляры того же скрипта при его запуске или б) использовать mkdirфайл блокировки и блокировать, пока блокировка не будет снята.

Затем все, что вам нужно сделать, это переместить файлы в разные ~/copysystem/drive# каталоги, и все они волшебным образом копируются в место назначения.

В частности, в случае 4a вы, вероятно, захотите использовать rsync -aP для копирования файлов, чтобы можно было перезапустить частичную передачу с середины. (Возможно в сочетании с --remove-sent-files, если вы хотите избавиться от оригиналов.)

Если вы хотите избежать осложнений, связанных с использованием incron, вы все равно можете воспользоваться блокировкой ваших скриптов для файла блокировки. Это работает примерно так:

#!/bin/bash
LOCKDIR="/var/run/copysystem/copysystem.lock"

while ! mkdir $LOCKDIR ; do
  echo "waiting for lock"
  sleep 5
done
trap rmdir $LOCKDIR EXIT

rsync commands go here....

Это работает, потому что mkdir является атомарной операцией - если она завершится успешно, вы знаете, что каталог не существует. Это важно, потому что если вы используете что-то вроде ! -f && touch, есть состояние гонки. (То же самое со сканированием таблицы процессов для команд rsync и т.п.)

Не очень элегантно, но быстро и грязно:

process1 &
while kill -0 $! ; do sleep 1; done && process2 &
while kill -0 $! ; do sleep 1; done && process3 &

Вам нужно будет заменить фактические PID на $! если вы выполняли промежуточные фоновые задания.

Вам нужна неинтерактивная очередь команд. Хорошие новости! Я написал для тебя.

enqueue_cmd:

#!/usr/bin/perl -w

# Enqueue a job

use strict;

use Fcntl qw(:flock);

my $JOB_QUEUE_FILE = '/var/tmp/job_queue';
my $LOCK_TRIES = 5;
my $LOCK_SLEEP = 1;

my $jq;
open $jq, ">> $JOB_QUEUE_FILE" or die "!open $JOB_QUEUE_FILE: $!";

my $locked = 0;

LOCK_ATTEMPT: for (my $lock_tries = 0; $lock_tries < $LOCK_TRIES;
    $lock_tries++)
{
    if (flock $jq, LOCK_EX) {
            $locked = 1;
            last LOCK_ATTEMPT;
    }

    sleep $LOCK_SLEEP;
}

$locked or die "could not lock $JOB_QUEUE_FILE";

for (@ARGV) {
    print $jq "$_\n";
}

close $jq;

dequeue_cmd:

#!/usr/bin/perl -w

# Dequeue a jobs and run them

use strict;

use Fcntl qw(:seek :flock);
use FileHandle;

my $QUEUE_FILE = '/var/tmp/job_queue';
my $OUTPUT_FILE = '/var/tmp/job_out';
my $LOCK_TRIES = 5;
my $LOCK_SLEEP = 1;
my $JOB_SLEEP = 1;

my $locked;

my $jo;
open $jo, ">> $OUTPUT_FILE" or die "!open $OUTPUT_FILE: $!";
$jo->autoflush(1);

my $jq;
open $jq, "+< $QUEUE_FILE" or die "!open $QUEUE_FILE: $!";

my @jobs = ( );
my $job;

JOB: while (1) {

    if (-s $QUEUE_FILE == 0) {
            sleep $JOB_SLEEP;
            next JOB;
    }

    $locked = 0    

    LOCK_ATTEMPT: for (my $lock_tries = 0; $lock_tries < $LOCK_TRIES;
            $lock_tries++)
    {
            if (flock $jq, LOCK_EX) {
                    $locked = 1;
                    last LOCK_ATTEMPT;
            }

            sleep $LOCK_SLEEP;
    }
    $locked or die "could not lock $QUEUE_FILE";

    seek $jq, 0, SEEK_SET or die "could not seek to start of file";

    push @jobs, <$jq>;

    truncate $jq, 0 or die "could not truncate $QUEUE_FILE";
    seek $jq, 0, SEEK_SET or die "could not seek to start of $QUEUE_FILE";

    flock $jq, LOCK_UN;

    for $job (@jobs) {
            chomp $job;
            print $jo "## executing $job\n";
            print $jo `$job`;
    }

    sleep $JOB_SLEEP;
}

Первый забег nohup ./dequeue_cmd &, Затем добавьте свои команды так:

./enqueue_cmd "echo 'hello world'" "sleep 5" "date"
./enqueue_cmd "ls /var/tmp"

Результат появляется в /var/tmp/job_out:

tail -F /var/tmp/job_out
## executing echo 'hello world'
hello world
## executing sleep 5
## executing date
Fri Dec 10 16:35:43 PST 2010
## executing ls /var/tmp
ff
job_out
job_queue
ss

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

Мне нравится элегантность решения Александра - и оно затрагивает все вопросы, которые вы изначально подняли, - но я вижу, что у него есть ограничения.

Ранее я использовал BSD lpd в качестве метода для постановки заданий в очередь - «драйвер принтера» - это просто сценарий оболочки, поэтому его легко адаптировать к различным задачам (в моем случае это означало управление 4 модемами для опроса данных, отправки факсов, SMS и прочее).