Что происходит в 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. Sudo ловит сигнал и отправляет какой-то другой сигнал в apt-get. Я пытался отправить apt-get все возможные сигналы! Он по-прежнему не возобновляет загрузку ни для одного из них. Он возобновляет загрузку только тогда, когда я нажимаю Ctrl-C, чтобы убить его.

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


person Anupam Jain    schedule 08.07.2011    source источник
comment
Можно предположить, что apt-get устанавливает обработчик SIGINT, который просто обрабатывает свой сигнал по-разному, в зависимости от того, интерактивная оболочка, в которой он работает, или нет...   -  person Linus Kleen    schedule 08.07.2011
comment
Ну, это не может быть просто так, потому что тогда запуск apt-get в одной оболочке и его уничтожение в другой должны по-прежнему позволять ему захватывать SIGINT и делать все, что ему нужно, чтобы сохранить состояние загрузки.   -  person Anupam Jain    schedule 08.07.2011


Ответы (3)


Подсказка, это не просто отправка 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.

person cnicutar    schedule 08.07.2011
comment
Тогда почему загрузка возобновляется при нажатии стрелки вверх и вводе для использования истории bash? - person Thaddee Tyl; 08.07.2011
comment
@Thaddee Tyl Потому что он запускает новый процесс, выбирая новый исходный порт, новое TCP-соединение, новое время запуска дросселя. Прочитайте мой ответ еще раз :-) - person cnicutar; 08.07.2011
comment
Я имею в виду, хранит ли apt-get где-нибудь кеш того, что загружает? Он нормально возобновляет загрузки? Не знаю, я не использую его на регулярной основе. Мне интересно, почему он не возобновляет загрузку при запуске с другого терминала. - person Thaddee Tyl; 08.07.2011
comment
@Thaddee Tyl Я не знаю, как apt-get возобновляет загрузку (и мне все равно, если честно), но я подозреваю, что причиной всего этого является новое TCP-соединение. - person cnicutar; 08.07.2011
comment
Ну вопрос не в том как работает троттлинг (мне правда все равно), а в чем разница между Ctrl-C и kill -2 pid. - person Anupam Jain; 08.07.2011
comment
@Anupam Jain Тогда ты не задал правильный вопрос. Правильным был бы вопрос: что делает apt-get при получении этих сигналов? - person cnicutar; 08.07.2011
comment
@cnicutar, я спросил именно то, что хотел спросить. Пожалуйста, прочитайте вопрос еще раз, особенно последнюю строку вопроса. - person Anupam Jain; 08.07.2011
comment
@Anupam Jain Извините, мои глаза почувствовали, что у меня есть определенные вещи, и я перестал читать. Только название вводит в заблуждение, я вам это даю. Тем не менее, я не понимаю отрицательного ответа :-/ Возможно, вам следует еще раз прочитать заголовок вашего вопроса? - person cnicutar; 08.07.2011
comment
Я не уверен, что ты имеешь в виду. Название вопроса не могло быть яснее - в чем разница между Ctrl-C и SIGINT (т.е. kill -2 pid). И ваш ответ даже не пытается ответить на вопрос, отсюда и отрицательный голос. - person Anupam Jain; 08.07.2011
comment
@cnicutar позвольте нам продолжить это обсуждение в чате - person Anupam Jain; 08.07.2011
comment
Хорошо, это было хорошее предложение. Однако это не объясняет поведения. Пожалуйста, смотрите редактирование моего вопроса. - person Anupam Jain; 08.07.2011

Хорошо, тайна раскрыта! Спасибо полезным людям из Индийской группы пользователей 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
person Anupam Jain    schedule 05.08.2011

Хорошо, как сказал cnicutar, он просто отправляет сигнал SIGINT. Теперь ключ к тому, что было сказано, находится здесь:

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

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

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

Таким образом, при отправке CtrlC во время ожидания выполнения своей работы apt этот сигнал отправляется на apt.

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

Проверьте себя:

sudo do-something
echo $!
pidof do-something

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

person c00kiemon5ter    schedule 08.07.2011
comment
То, что вы говорите, могло быть фактором. Вот почему я провел эксперимент, упомянутый в моем вопросе. Даже когда я получаю PID apt-get (не sudo) и уничтожаю его с другого терминала, он все равно не позволяет apt-get сохранять состояние. Явно происходит что-то еще. - person Anupam Jain; 08.07.2011