Анимация
JavaScript
|
Главная Библионтека гарантии, что операции чтения не будут блокироваться. Если блокировка ввода устанавливается одновременно с тем, как другой процесс заблокируется в ожидании вывода, возникает состояние взаимной блокировки (deadlock). Процессы входят в клинч, пока кто-нибудь не убьет их или не перезагрузит компьютер. Если вы можете управлять буферизацией другого процесса (потому что вы сами написали программу и знаете, как она работает), возможно, вам поможет модуль 1РС::0реп2. Первые два аргумента функции ореп2, экспортируемой 1РС::0реп2 в ваше пространство имен, представляют собой файловые манипуляторы. Либо передавайте ссылки на typeglobs, как это сделано в решении, либо создайте собственные объекты IO::Handle и передайте их: use IPC;:0реп2; use 10::Handle; ($reader, Swrlter) = (10: •Handle->new, 10.:Handle->new): open2($reader, $writer, Sprogram); Чтобы передать объекты, необходимо предварительно создать их (например, функцией 10: :Handle->new). Если передаваемые неременные не содержат файловых манипуляторов, функция ореп2 не создаст их за вас. Другой вариант - передать аргументы вида X&OTHERFILEHANDLE" или >&OTHERFILEHANDLE", определяющие существующие файловые манипуляторы для порожденных процессов. Эти файловые манипуляторы не обязаны находиться под контролем вашей программы; они могут быть подключены к другим программам, файлам или сокетам. Программа может задаваться в виде списка (где первый элемент определяет имя программы, а остальные элементы - аргументы программы) или в виде отдельной строки (передаваемой интерпретатору в качестве команды запуска программы). Если вы также хотите управлять потоком STDERR программы, воспользуйтесь модулем 1РС::0репЗ (см. следующий рецепт). Если произойдет ошибка, возврат из ореп2 и орепЗ не происходит. Вместо этого вызывается die с сообщением об ошибке, которое начинается с "ореп2" или "орепЗ". Для проверки ошибок следует использовать конструкцию eval БЛОК: eval { open2($readme, $wrlteme, @program and arguments); If ($@) { If ($@ =" /"open2/) { warn "open2 failed: $!\n$@\n"; return; die; # Заново инициировать непредвиденное исключение > Смотри также- Документация по стандартным модулям 1РС:;0реп2 и 1РС::0репЗ; рецепт 10.12; описание функции eval вperlfunc(i); описание переменной $@ в разделе «Special Global Variables* perlvar{\). 16.9. Управление потоками ввода, вывода и ошибок другой программы Проблема Вы хотите полностью управлять потоками ввода, вывода и ошибок запускаемой команды. Решение Аккуратно воспользуйтесь стандартным модулем 1РС::0репЗ, возможно - в сочетании с модулем IO::Selcct (появившимся в версии 5.004). Комментарий Если вас интересует лишь один из потоков STDIN, STDOUT или STDOUT программы, задача решается просто. Но если потребуется управлять двумя и более потоками, сложность резко возрастает. Мультиплексирова1П1е нескольких потоков ввода/вывода всегда выглядело довольно уродливо. Существует простое обходное решение: @iall,= (Scffld I sed -e s stdout: / ) 2>&1; for (gall) { push @{ s/stdout: Xeoutlines ; xeerrlines }, $ } print "STDOUT:\n". ©outlines, "\n": print STDERR:\n", gerrlines, An", Если утилита sed не установлена в вашей системе, то в простых случаях вроде показанного можно обойтись командой perl -ре, которая работает практически так же. Однако то, что здесь происходит, в действительности нельзя считать параллельными вычислениями. Мы всего лишь помечаем строки STDOUT префиксом "stdout;" и затем удаляем их после чтения всего содержимого STDOUT и STDERR, сгенерированного программой. Кроме того, можно воспользоваться стандартным модулем 1РС::0репЗ. Как ни странно, аргументы функции 1РС::0репЗ следуют в другом порядке, нежели в 1РС::0реп2. open3(.WRITEHANDLE, .READHANDLE, .ERRHANDLE, "ЗАПУСКАЕМАЯ ПРОГРАММА"); Открываются широкие потенциальные возможности для создания хаоса - еще более широкие, чем при использоваиии ореп2. Если попытаться прочитать STDERR программы, когда она пытается записать несколько буферов в STDOUT, процесс записи будет заблокирован из-за заполнения буферов, а чтение заблоки-руется из-за отсутствия данных. Чтобы избежать взаимной блокировки, можно имитировать орепЗ с помощью fork, open и exec; сделать все файловые манипуляторы небуферизованными и использовать sysread, syswrite и select, чтобы решить, из какого доступного для чтения манипулятора следует прочитать байт. Однако ваша программа становится медленной и громоздкой, к тому же при этом не решается классическая пробле- # при больших объемах print STDOUT:\n", outlines, "\n"; print "STDERR:\n", @errlines, "\n": Кроме того (как будто одной взаимной блокировки недостаточно), такое решение чревато нетривиальными ошибками. Существуют но крайней мере три неприятных ситуации: первая - когда и родитель и потомок пытаются читать одновременно, вызывая взаимную блокировку. Вторая - когда заполнение буферов заставляет потомка блокироваться при попытке записи в STDERR, тогда как родитель блокируется при попытке чтения из STDOUT потомка. Третья - когда заполнение буферов заставляет родителя блокировать запись в STDIN потомка, а потомок блокируется при записи в STDOUT или STDERR. Первая проблема в общем случае не решается, хотя ее можно обойти, создавая таймеры функцией alarm и предотвращая перезапуск блокирующих операций при получении сигнала SIGALRM. Мы используем модуль IO:;Select, чтобы узнать, из каких файловых манипуляторов можно прочитать данные (для этой цели можно использовать встроенную функцию select). Это решает вторую, но не третью проблему. Для решения третьей проблемы также потребуются alarm и SIGALRM. Если вы хотите отправить программе входные данные, прочитать ее вывод и затем либо прочитать, либо проигнорировать ошибки, работы заметно прибавится (см. пример 16.2). Пример 16.2. cmdSsel #!/usr/bin/perl # cmd3sel - управление всеми тремя потоками порожденного процесса й (ввод, вывод и ошибки), use IPC::0реп3: use 10::Select; $cmd = "grep vt33 /none/such - /etc/termcap"; Spid = open3(.CMD IN, .CMD OUT, .CMD ERR, $cmd); $SIG{CHLD} = sub { print "REAPER: status $? on $pid\n" if waitpid($pid, 0) > 0 print CMD IN "This line has a vt33 lurking in it\n"; close(CMD IN); $selector = 10;:Select->new(); ма взаимной блокировки ореп2, при которой каждая программа ждет поступления данных от другой стороны; use IPC;;ОрепЗ; $pid = open3(.HIS lN, .HIS OUT, .HIS ERR, Scmd); close(HIS IN); # Передать порожденному процессу EOF или данные outlines = <HIS OUT>; # Читать до конца файла eerrlines = <HIS ERR>; # XXX: Возможная блокировка 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 |