За последние 24 часа нас посетили 16493 программиста и 1568 роботов. Сейчас ищут 920 программистов ...

Помогите разобраться с функцией popen

Тема в разделе "PHP для новичков", создана пользователем demoniqus, 25 апр 2011.

  1. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    В моем учебнике приводится такой пример:
    PHP:
    1.  
    2. <?php ##Использование popen() .
    3. //Запускаем процесс (параллельно работе сценария)  режиме чтения.
    4. $fp = popen("usr/sbin/sendmail -t -i", "wb");
    5. //Передаем процессу тело письма в стандартный входной поток.
    6. ........
    7.  
    Далее идет коментарий:"...По команде popen() запускается указанная в первом параметре программа, причем выполняется она параллельно сценарию..."
    Вот с этим ПАРАЛЛЕЛЬНО у меня неувязочка и выходит... мне нужно запустить другой скрипт, который от команды sleep() вздремнет немного, а затем выполнится. Я так понимаю, что при параллельном выполнении основной скрипт, вызвавший команду popen(), не должен дожидаться окончания выполнения вызываемого скрипта и работать дальше... но у меня ждет выполения... Как заставить скрипт выполнится параллельно?
     
  2. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    опишите причину необходимости такого поведения
    и ваш случай можно реализовать, вопрос лишь в том, стоит ли овчинка выделки
     
  3. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Суть всего вот в чем:
    мне нужно организовать многопоточность работы с БД MySQL. Пока дело не доходит до редактирования записи, проблем не возникает. Проблема возникает, когда нужно отредактировать запись: скрипт генерирует пользователю страницу с исходными данными для редактирования, пользователь получает эту страницу, меняет данные и отправляет обратно на сервер для сохранения. Обычная блокировка таблицы LOCK TABLE не прокатывает, т.к. она автоматически завершается при завершении скрипта (в данном случае скрипта, генерирующего страницу с исходными данными). Я решил, что можно ставить метку (к примеру ID сессии и текущее время) в нужную строку - остальные скрипты, увидев эту метку должны будут ждать до окончания блокировки, пока получат право на редактирование данной записи. Ну а концом блокировки можно считать отправку и сохранение новых данных на сервере, либо переход по другой ссылке на странице. Вроде идея неплохая, но есть одно НО, а точнее два: а если пользователь просто грубо закроет (может даже убьет станицу через диспетчера ради эксперимента) или просто отвалится (отключили к примеру свет)? Тогда метку никто не снимет и никто не получит право на редактирование данной записи никогда! Выходом можно посчитать следующее:
    скрипт, генерирующий первоначальную страницу, также запускает разовую проверку времени последнего обновления - если оно превышает некий интервал, то значит, что пользователь по каким-либо причинам больше не работает с данной записью и можно удалить метку. Поскольку заранее нельзя сказать, сколько по времени займет у пользователя редактирование данных (может он откроет запись для редактирования и пойдет ублажать целый гарем), то приходится немного расширить скрипт: со страницы пользователя циклически отправляются команда на изменение времени последнего обновления записи, а также команда на очистку метки. Т.к. команды обе находятся на серваке и получает их сервер одновременно от пользователя (это можно гарантировать), то никакие события на стороне пользователя не приведут к глупой ситуации "вечно редактируемой записи".
    для этого мне надо сделать так, чтобы скрипт, выполняющий проверку последнего времени обновления записи и при необходимости убирающий метку, получал команду на выполнение, но не задерживал основной сценарий.
    Можно делать и не скриптом, а, скажем, EXE-шником, но проблема остается та же... все функции, которые я использовал, ждут выполнения дочернего процесса
     
  4. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    вы сами себе противоречите. если вы решили использовать в качестве метки id сессии, то и снятие лока должно выполняться по обновлении вашего материала ИЛИ по разрушению сессии (смотря что случится раньше). попросить БД залипнуть, пока юзер не передумает/не сделает свою работу - это худший их всех вариантов, который можно вообще придумать
     
  5. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    я не спец по работе с сессиями, а точнее вообще впервые их затронул... и то с целью использовать не совсем по назначению...
    Если я начинаю новую сессию в скрипте getData.php, то сколько эта сессия будет существовать? доживет ли она до скрипта saveData.php, если пользователь будет думать несколько часиков? Что с ней будет, если пользователь отвалится из-за ЧП или по своей милости (к примеру введет в адресную строку новый адрес и нажмет Enter) - ведь сессия сразу в этом случае скорее всего не угаснет, а будет прибита через значительное время автоматически, а запись должна освободиться максимально быстро для редактирования.
    И в чем я себе противоречу? Если пользователь все еще на странице редактирования данных, то эта страница автоматически подает сигнал об этом и редактируемая запись доступна лишь ему. Если же пользователь больше не подает "признаков жизни", то имеющаяся у сервера команда уберет метку. Если пользователь дал сам команду, то сервер также уберет метку. Единственное слабое место, которое я в этом случае вижу - если сервак грохнется между сохранением данных и очисткой метки... Других противоречий я тут не вижу...
     
  6. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    вам безразлично, когда конкретно должна быть оборвана сессия. пользователь открыл браузер, решил отредактировать статью - час думал, два думал, три думал... сессия отмерла, юзер отправляет результат. смотрим - сессии такой больше нет, метку стираем и видим, что пользователь хочет записать сообщение без предварительного резервирования, заворачиваем его с сообщением "вы не можете сейчас записать вашу статью, потому что превышен интервал ожидания. попробуйте еще раз". а со второй попытки у него получится, если больше никто статью не редактирует. во всех остальных случаях, метка будет обработана суперкорректно
    зы: если пользователь закрыл браузер, то сессия корректно завершилась
     
  7. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    А как тогда обеспечить недоступность записи для редактирования в этом случае остальным пользователям?
    И если пользователь некорректно завершил сессию, то сколько по времени она еще будет существовать, пока завершится сама и освободит запись для редактирования? Ведь нужно, чтобы она освободилась как можно быстрее (в пределах 2-5 минут)
     
  8. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    время жизни сессии - это настраиваемый параметр. есть некоторые тонкости с минимальным временем жизни. если интересует:
    http://habrahabr.ru/blogs/php/28418/
     
  9. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Не получается решить проблему вероятности перехвата права редактирования...
    Если идти по такой схеме:
    - внести идентификатор сессии в таблицу для обозначения занятой позиции, а потом получать другими пользователями этот идентификатор и проверять данную сессию на жизнеспособность - не существует, значит редактируем, а существует - ждем дальше
    то всю гармонию нарушает сборщик мусора - он через указанное время перебьет все сессии, хранящиеся во временном каталоге и у другого пользователя будет возможность перехватить право редактирования записи до того, как первый пользователь опять заявит свое право на внесение изменений.
    Если же сделать время жизни сессии большим, то в течение длительного времени после некорректного завершения работы запись останется недоступной...
     
  10. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
  11. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    VOLT(220)
    Гениально! Работает!
    А как через эту конструкцию передать параметры в скрипт?
     
  12. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
    Код (Text):
    1. start /B php {путь к файлу}\file.php "параметр 1" параметр_число "строковый параметр 2"
    Соответственно, параметров может быть сколько надо или не быть вовсе.
    Искать их в $_SERVER['argv']
     
  13. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Все, вроде пошло! Спасибо всем за помощь!
     
  14. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Мдя... рано радоваться... в одном скрипте получилось... попытался перенести в другой - облом по всей усатой морде...
    PHP:
    1.  
    2. <?php
    3. ......
    4. $file = fopen('../forms/TTH.txt', 'w+b');
    5. fwrite($file, "$Organization\n" . $TTH);
    6. fclose($file);
    7. //`start ../exe/TTH.exe`;
    8.  
    9. //$commandString = 'start /b Z:\\home\\sitrans.ru\\www\\exe\\TTH.exe -attachment "c:\\temp\file1.txt"';
    10. //$commandString = 'start /b Z:\\home\\sitrans.ru\\www\\exe\\TTH.exe -attachment';
    11. $commandString = 'start /b Z:/usr/local/php5/php.exe Z:/home/sitrans.ru/www/php/test.php "888parampampam888"';
    12. pclose(popen($commandString, 'r'));
    13.  
    14. //`Z:/usr/local/php5/php.exe Z:/home/sitrans.ru/www/php/test.php`;
    15.  
    16. echo "<div style='color: red; font-size: 50px; text-decoration: underline'>$hndl</div>\n";
    17. //popen ('TTH.exe -t -i', "wb");
    18. //require_once('../exe/run.php');
    19. //$file = popen('../exe/TTH.exe', 'r');
    20. echo $report . "\n\n\n" . "Файл для распечатки сохранен в  Мои документы/TTH.xls ";
    21. ?>
    22.  
    здесь без ожидания окончания выполнения запускается скрипт test.php - и действительно последняя отладочная команда echo выводит результат, не дожидаясь, пока проснется test.php

    В другой скрипт я перенес ту же команду без изменения, однако скрипт дожидается, пока test.php проснется и выполнится... Что я теперь не так ему сделал???
    PHP:
    1.  
    2. <?php
    3. require_once ('DateToNumber.php');
    4. require_once ('clear.php');
    5. $link = mysql_connect('localhost', 'root', '');
    6. mysql_select_db('SITRANS');
    7. //$lock = mysql_query ("lock tables drivers write");
    8. //echo "$lock<br /><br /><hr />";
    9. $num = $_POST['num'];
    10. $currenttime = time();
    11. $SID1 =SID;
    12. //$r1 = '';
    13. mysql_query('lock tables drivers write');
    14. $res = mysql_query("select * from drivers where ind=$num and session_id=''");
    15. //$r1 .= mysql_num_rows($res) . "<hr />";
    16. if (mysql_num_rows($res) == 0) {
    17.     $res = mysql_query("select session_id from drivers where ind=$num");
    18.     $res = mysql_fetch_array($res);
    19.     if ($res[0] != '') {
    20.         echo 'Данную позицию в настоящее время использует другой пользователь.';
    21.         exit();
    22.         }
    23.     }
    24. else {
    25.     mysql_query("update drivers set session_id='$SID1', time=$currenttime where ind=$num");
    26.     //$r1 .= ("update drivers set session_id='$SID1', time=$currenttime where ind=$num");
    27.     }
    28. mysql_query('UNLOCK TABLES');
    29. $res = mysql_fetch_array($res);
    30.  
    31. $result = "<div style='text-align: center;'>Изменение данных водителя<div style='text-align: center; font-size: 25px; font-weight: bold'>$res[4]</div></div><br /><br />";
    32. $result .= "<div align=center><table width=60% style='border-width: 1px; border-style: solid;'><tr><td width='50%' style='text-align: left; vertical-align: top;'>Водительское удостоверение</td><td style='text-align: left'><input type=text id='DriverLicense' value='$res[1]'";
    33. $result .= "onBlur='CheckFormatDriverLicense ();' size=50/><br /><br />";
    34. $result .= "</td></tr>";
    35.  
    36. $result .= "<tr><td style='text-align: left; vertical-align: top;'>Дата выдачи</td><td style='text-align: left'><input size=50 type=text id='DriverLicenseDate' value='$res[2]' ";
    37. $result .= "onChange='this.value = (this.value.replace(/[^\\d]+/g, \".\").replace(/\\.{2,}/g, \".\"))'/><br /><br /></td></tr>";
    38.  
    39. $result .= "<tr><td style='text-align: left; vertical-align: top;'>Кем выдан</td><td style='text-align: left'><input size=50 type=text id='GIBDD' value='$res[3]' /><br /><br /></td></tr>";
    40.  
    41. $result .= "<tr><td style='text-align: left; vertical-align: top;'>ФИО</td><td style='text-align: left'><input type=text id='NameDriver' size=50 value='$res[4]' /><br /><br /></td></tr>";
    42.  
    43. $result .= "<tr><td style='text-align: left; vertical-align: top;'>Телефон</td><td style='text-align: left'><input size=50 type=text id='TLPH'  value='$res[5]'/><br /><br /></td></tr>";
    44. $result .= "<tr><td colspan=2 style='text-align: center;'><input type=button value='Сохранить' onClick='ChangeDriver($num, \"" . SID . "\")' /></td></tr></table></div>";
    45. $commandString = 'start /b Z:/usr/local/php5/php.exe Z:/home/sitrans.ru/www/php/test.php "888parampampam888"';
    46. echo "<br />$commandString";
    47. pclose(popen($commandString, 'r'));
    48. echo $SID1 . "|" . $result;// . "<br />$r1";
    49. //ClearBlocking($SID1, $currenttime)
    50. //$commandString = "start /b Z:/usr/local/php5/php.exe Z:/home/sitrans.ru/www/php/clear.php '$SID1' '$currenttime'";
    51.  
    52. ?>
    53.  
     
  15. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    Я закоментил весь код во втором скрипте кроме вызова popen и все отработало, как надо. Вопрос теперь в том, какая часть кода заставляет выполняться скрипт с ожиданием?
     
  16. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Такие задачи решаются двумя способами.
    1. Приоритет по сохранению.
    Никаких локов не делается, но при выдаче клиенту формы редактирования в ней сохраняется таймстамп последней редактирования этой записи. В некоторых случаях используют md5 содержимого. При сохранении данных, используя этот таймстамп или md5 - проверяют, что данные не изменились - и только тогда сохраняют их. Если данные изменились (кто-то уже отредактировал их) - клиенту выдается соответствующее сообщение, выводятся как новые данные из базы, так и поля с набранным им ранее - а дальше уже он решает, что делать.

    2. Блокировка по началу редоктирования.
    В общем этот способ вы же и описали, только для чего вам эти все popen - не ясно. Вы ставите метку, кто редактирует, и время начала. Дается время жизни этой блокировки, о чем можно выдать клиенту сообщение... с обратным отсчетом. Теоретически, ему же, если много данных вводить, можно дать возможность продлять блокировку - ссылка, по нажатию на которую обновляется таймстамп начала блокировки. Второй клиент находит активную блокировку, выдает сообщение клиенту и, к примеру, сидит обновляется каждую минуту, проверяет - не пропала ли блокировка, не устарела ли она.

    На случай, если первый клиент попытается сохранить данные с устарелой блокировкой - можно применить комбинацию этих двух способов.
     
  17. demoniqus

    demoniqus Активный пользователь

    С нами с:
    29 мар 2011
    Сообщения:
    34
    Симпатии:
    0
    В общем вся проблема в невнимательности! Я указываю приличное время сна в файле clear.php и с дуру подключаю его через require_once - разумеется, основной поток ждет его выполнения! Убрал за ненадобностью лишнюю строчку кода и все заработало, как надо.