Что происходит в 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 возможность сохранить состояние загрузки. Вопрос - что это?
редактировать
Это предложения, которые я получил до сих пор, но не сигары. Тайна углубляется! -
На
sudo kill -2 $!
сигнал может идтиsudo
вместоapt-get
, Это не причина, потому что, как упоминалось выше, я также пытался отправить SIGINT специально для PID apt-get, и даже это не позволило apt-get сохранить его состояние.Судо ловит сигнал и отправляет другой сигнал apt-get. Я попытался отправить apt-get все сигналы, которые я могу придумать! Это все еще не возобновляет загрузку ни для одного из них. Загрузка возобновляется только тогда, когда я нажимаю Ctrl-C, чтобы убить его.
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-get
SIGINT отправляется 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.
Я надеюсь, что это немного помогает.