Анимация
JavaScript
|
Главная Библионтека $SIG{CHLD} = \&REAPER; while (Shisaddr = accept(CLIENT, SERVER)) { next If Spid = fork; # Родитель die "fork: S" unless defined Spid; # Неудача # otherwise child close(SERVER); # He нужно для потомка й ... Сделать что-то exit; # Выход из потомка } continue { close(CLIENT); # Не нужно для родителя Комментарий Подобный подход очень часто используется в потоковых (SOCK STREAM) серверах на базе сокетов Интернета и UNIX. Каждое входящее подключение получает собственный дубликат сервера. Общая модель выглядит так; 1. Принять потоковое подключение. 2. Ответвить дубликат для обмена данными с этим потоком. 3. Вернуться к п. 1. Такая методика не используется с датафаммными сокетами (SOCK DGRAM) из-за особенностей обмена данными в них. Из-за времени, затраченного на разветвление, эта модель непрактична для UDP-серверов. Вместо продолжительных соединений, обладающих определенным состоянием, серверы SOCK DGRAM работают с непредсказуемым набором датаграмм, обычно без определенного состояния. В этом варианте наща модель принимает следующий вид: 1. Принять датаграмму. 2. Обработать датаграмму. 3. Вернуться к п. 1. Новое соединение обрабатывается порожденным процессом. Поскольку сокет SERVER никогда не будет использоваться этим процессом, мы немедленно закрываем его. Отчасти это делается из стремления к порядку, но в основном - для того, чтобы серверный сокет закрывался при заверщении родительского (серверного) процесса. Если потомки не будут закрывать сокет SERVER, операционная система будет считать его открытым даже после заверщения родителя. За подробностями обращайтесь к рецепту 17.9. %SIG обеспечивает чистку таблицы процессов после заверщения потомков (см. главу 16). t> Смотри также- Описание функций fork и accept вperlfunc{i); рецепты 16.15; 16.19; 17.12-17.13. 17.12. Серверы с предварительным ветвлением 627 17.12. Серверы с предварительным ветвлением Проблема Вы хотите написать сервер, параллельно обслуживающий нескольких клиентов (как и в предыдущем разделе), однако подключения поступают так быстро, что ветвление слишком сильно замедлит работу сервера. Решение Организуйте пул заранее разветвленных потомков, как показано в примере 17.5. Пример 17.5. preforker #/usr/bin/perl # preforker - сервер с предварительным ветвлением use 10::Socket: use Symbol; use POSIX; # Создать сокет SERVER, вызвать bind и прослушивать порт. Sserver = IO;;Socket INET->new(LocalPort => 6969, or die making socket $@\n n Глобальные переменные SPREFORK = 5; $MAX CLIENTS PER CHILD = 5; %children = 0; $children = 0; sub REAPER { $SIG{CHLD} = \&REAPER; my $pid = wait; $children --; delete $children{$pid}; Type Proto Reuse Listen => SOCK STREAM, => tcp, => 1, => 10 ) # Количество поддерживаемых потомков # Количество клиентов, обрабатываемых # каждым потомком. # Ключами являются текущие # идентификаторы процессов-потомков # Текущее число потомков # Чистка мертвых потомков sub HUNTSMAN { local($SIG{CHLD}) = -IGNORE; kill -INT => keys %children, exit; # Обработчик сигнала SIGINT # Убиваем своих потомков # Корректно завершиться Пример 17.5 (продолжение) п Создать потомков, for (1 SPREFORK) { make new child(); # Установить обработчики сигналов. $SIG{CHLD} = \&REAPER; $SIG{INT} = \&HUNTSMAN, # Поддерживать численность процессов while (1) { sleep; # Ждать сигнала (например, # смерти потомка), for ($1 = Schildren; $i < $PREFORK, $i++) { make new child(), # Заполнить пул потомков sub fflake new child { my $pid; my $sigset; tt Блокировать сигнал для fork. Ssigset = POSIX::SigSet->new(SIGINT); sigprocmask(SIG BLOCK, Ssigset) or die "Cant block SIGINT for fork: S!\n; die fork: S" unless defined (Spid = fork), if (Spid) { # Родитель запоминает рождение потомка и возвращается. sigprocmask(SIG UNBLOCK, Ssigset) or die "Cant unblock SIGINT for fork: S!\n"; $children{Spid} = 1, Schildren++; return; } else { # Потомок *не может* выйти из этой подпрограммы $SIG{INT} = DEFAULT; # Пусть SIGINT убивает процесс, tt как это было раньше. # Разблокировать сигналы sigprocmask(SIG UNBLOCK, Ssigset) or die "Cant unblock SIGINT for fork: S!\n"; tt Обрабатывать подключения, пока их число не достигнет # $MAX CLIENTS PER CHILD. for ($1=0, Si < SMAX CLIENTS PER CHILD; $i++) { Sclient = Sserver->accept() or last, # Сделать что-то с соединением 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 [ 203 ] 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |