Какое распределение вероятностей моделирует это состояние гонки?

10

Рассмотрим следующую команду: bash -c "echo x; cat 1" | tee 1.

Насколько я понимаю, он будет переходить в новую оболочку, писать xв stdout, писать file 1 not foundв stderr, выходить и возвращать управление родительскому процессу, а также писать xв stdout и to 1. Следовательно, я ожидаю, что конечный результат будет x, а файл 1содержит именно строку x.

Однако, это не так. На самом деле файл 1обычно содержит как минимум два экземпляра x, а иногда и тысячи строк xs. При пакетном тесте выполнения команды десять тысяч раз среднее число xs, записанных в файл, составило 52,3, а медиана - 1. Какой механик вызывает это? Какое распределение вероятностей моделирует это поведение? Я подозреваю, что он условно геометрический и в остальном однородный.

Уилл Шервуд
источник
3
Это связано со сроками исполнения левой и правой стороны конвейера. Оба запускаются одновременно или близко к нему. Если teeфайл открыли для записи, прежде чем catоткрыть его для чтения, xв файле может быть много ключей. В этом случае «цикл» завершается всякий раз, когда catчтение teeвыполняется быстрее, чем запись, достигая конца файла.
Кусалананда
Из ограниченного тестирования здесь, среднее число xs, записанных в файл, составило 4,35. Я думаю, это будет зависеть от загрузки машины.
Ренан

Ответы:

1

Это очень любопытно, поэтому я попытался исследовать это с помощью strace. Выполнить вашу команду в цикле 1000 раз:

mkdir {000..999}
for i in {000..999}; do
echo $i
(cd $i; strace -f -o trace.log bash -c 'bash -c "echo x; cat 1" | tee 1 >/dev/null'; )
done

Нашел файл с наибольшим количеством строк ( wc -l */1 | sort -nr | head -n2) и проверил соответствующий trace.log. Я, конечно, вижу много:

7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>
7568  read(3, "x\n", 131072)            = 2
7568  write(1, "x\n", 2)                = 2
7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>
7568  read(3, "x\n", 131072)            = 2
7568  write(1, "x\n", 2)                = 2
7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>

Где 7567 tee 1и 7568 cat 1. Они определенно чередуются, так что, да, как и предполагалось, все дело в сроках выполнения (и я представляю себе переключение контекста) двух команд.

chutz
источник