Что происходит в BASH, когда вы нажимаете Ctrl-C (подсказка, это не просто отправка SIGINT) - PullRequest
9 голосов
/ 08 июля 2011

Сначала немного предыстории - когда я 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, каждые 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 ]

5 голосов
/ 08 июля 2011

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

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

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

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

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

EDIT

После прочтения вопроса сноваЯ подозреваю, что сигнал не отправлен на 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.

4 голосов
/ 05 августа 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
0 голосов
/ 08 июля 2011

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

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

, что верно.Позвольте мне объяснить.

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

Итак, при отправке Ctrl C в ожидании выполнения apt своей работы этот сигнал отправляется на apt.

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

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

sudo do-something
echo $!
pidof do-something

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

...