Что происходит в BASH, когда вы нажимаете Ctrl-C (подсказка, это не просто отправка SIGINT)

Сначала немного предыстории - Когда я делаю apt-get install загрузка из Интернета моей компании обеспечивает высокую скорость (400-500 КБ / с) в течение первых 10 секунд или около того, а затем снижается до одной десятой (40-50 КБ / с), а затем через несколько минут до действительно несчастный (4-5КБ / с). Это заставляет меня думать, что системный администратор реализовал какую-то схему регулирования сети.

Теперь я знаю, что сеть не просто ошибочна, потому что если я начну apt-get install foo, Ctrl-C это через 10 секунд и сразу запустить apt-get install foo снова (выполнив стрелку вверх и введя, чтобы использовать историю bash), а затем продолжайте повторять этот процесс в течение нескольких минут, пока все пакеты не будут загружены, я могу очень быстро загружать даже большие пакеты. В частности, даже после прекращения загрузки с помощью Ctrl-C, apt-get может возобновить загрузку при следующем вызове.

Конечно, смотреть на экран, нажимая Ctrl-C Up Enter каждые 10 секунд, становится очень скучно, поэтому я написал сценарий оболочки -

#!/bin/sh
for i in `seq 1 100` ; do
    sudo apt-get install foo -y &
    sleep 10
    sudo kill -2 $!
done

Это похоже на работу. Он порождает apt-get, запускает его в течение 10 секунд, а затем убивает (отправляя SIGINT) и запускает его снова. Однако на самом деле это не работает, потому что теперь apt-get не возобновляет загрузку при последующих вызовах!

Я провел эксперимент sudo apt-get install foo из одного терминала, а затем побежал kill -2 <PID of apt-get> из другого терминала. И даже в том случае, когда я перезагружаю apt-get, загрузка не возобновляется.

Очевидно, что Ctrl-C не эквивалентен SIGINT. И еще что-то происходит, когда я выполняю Ctrl-C вручную, что дает apt-get возможность сохранить состояние загрузки. Вопрос - что это?

редактировать

Это предложения, которые я получил до сих пор, но не сигары. Тайна углубляется! -

  1. На sudo kill -2 $! сигнал может идти sudo вместо apt-get, Это не причина, потому что, как упоминалось выше, я также пытался отправить SIGINT специально для PID apt-get, и даже это не позволило apt-get сохранить его состояние.

  2. Судо ловит сигнал и отправляет другой сигнал apt-get. Я попытался отправить apt-get все сигналы, которые я могу придумать! Это все еще не возобновляет загрузку ни для одного из них. Загрузка возобновляется только тогда, когда я нажимаю Ctrl-C, чтобы убить его.

  3. Apt-get обрабатывает SIGINT по-разному, если он из сценария, а не из интерактивной оболочки. Опять же, "эксперимент" выше доказывает, что это не так.

3 ответа

Решение

Хорошо, загадка раскрыта! Благодаря полезным людям из индийской группы пользователей Linux.

Ответ здесь двоякий -

Во-первых, apt-get вызывает другую программу под названием http для загрузки данных.

[~] ➔ file /usr/lib/apt/methods/http

/usr/lib/apt/methods/http: ELF 32-bit LSB executable, Intel 80386, version 1
(SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15,
stripped

Обратите внимание, что это исполняемый файл, даже не скрипт, вероятно, для поддержки загрузки файлов во время установки системы, когда еще нет доступных perl/python/ruby ​​и т. Д.

Во-вторых, когда вы нажимаете Ctrl-C после запуска apt-getSIGINT отправляется httpи не apt-get, когда http получает SIGINT, сохраняет состояние загрузки перед выключением.

Вот обновленный скрипт, который отлично работает -

#!/bin/sh
for i in `seq 1 100` ; do
    sudo apt-get install foo -y &
    sleep 10
    sudo kill -2 `ps -ae | grep " http" | awk '{print $1}'`
done

Подсказка, это не просто отправка SIGINT).

Да, это просто отправка SIGINT:-) Дросселирование - это то, что происходит, вы правильно поняли. Вот что я подозреваю, что происходит:

  • Что-то ограничивает пропускную способность соединений. Для отслеживания подключений он также включает в себя порт источника (что является плохой идеей IMO) среди других параметров

  • Когда вы убьете apt-get и перезапустите его, он, естественно, получит новый исходный порт TCP, и злой объект, подавляющий вас, подумает: "О, это новое соединение", что на самом деле

Так как ты ускоряешь вещи? Ну, реальным решением было бы использовать несколько параллельных загрузок. Я никогда не использовал его сам, но я слышал об инструменте под названием "apt-fast" (на самом деле сам скрипт bash), который делает что-то подобное.

РЕДАКТИРОВАТЬ

После прочтения вопроса я подозреваю, что сигнал не отправляетсяapt-get,

sudo apt-get install foo -y &
sudo kill -2 $! # sends signal to sudo, which sends whatever it wants to `apt-get`

Так что я верю sudo ловит сигнал и отправляет что-то еще (sigterm? sighup?) apt-get,

Хорошо, как сказал Cnicutar, это просто отправка SIGINT. Теперь ключ к тому, что было сказано, здесь:

Я подозреваю, что сигнал не отправляется в apt-get

что является правдой. Позволь мне объяснить.

Бег sudo foo начинает sudo процесс, который затем (то есть после вставки вашего passwd) вызывает foo; это аргумент. однажды sudo принимает пароль и вызывает foo вы в foo"космос". Что бы вы ни делали в ожидании foo закончить сделано на foo и не судо.

Итак, отправка CtrlC в ожидании apt чтобы сделать свою работу, этот сигнал отправляется apt,

Теперь, если вы начнете sudoего пид хранится в $! вар. Отправка kill сигнал к этому пид / вар посылает kill сигнал к sudoне apt который позже был начат sudo. Если вы хотите отправить kill сигнал к apt вы, вероятно, захотите использовать pidof полезность.

Проверьте сами:

sudo do-something
echo $!
pidof do-something

на выходе должны быть два разных pid.
Я надеюсь, что это немного помогает.

Другие вопросы по тегам