|
|
|
| Но а если в общем случае - если вывести нужно не на жёсткий диск к примеру - а в сокет? Ведь для обмен используеться оперативная память. Вот к примеру процессу нужно вывести информацию на внешнее устройство(не важно какое) он сначала заполняет область оперативной памяти выводимой информацией, затем генирирует исключение для вызова операционной системы(это и называеться системным вызовом), операционная система далее "берёт" эту заполненную область и выводит на внешнее устройство. А если процесс генерирует без конца и края выводимую информацию, что произойдёт в этом случае? Ведь система должна "сказать" ему "стоп, хвати, память заканчиваеться, сейчас пока что я выведу то, что ты нагенерил, а потом продолжим" Так?
[поправлено модератором: начало в теме Буферизиация при вводе-выводе] | |
|
|
|
|
|
|
|
для: dump
(14.11.2011 в 16:15)
| | >Ведь система должна "сказать" ему "стоп, хвати, память заканчиваеться, сейчас пока что я
>выведу то, что ты нагенерил, а потом продолжим" Так?
Когда как (кому говорит, кому нет, если говорит, то про продолжение уже речи не идет). До исчерпания ресурсов дело, как правило не доходит, так как в сокет пишут построчно. Каждая строка занимает, как правило, меньше одного килобайта. Если это бинарные данные, то запись идет блоками кратными степени двойки. С сетями там еще сложнее, там протоколы начинают работать. Причем они расширяют фрейм, а после того, как упираются в ширину канала, снова его сбрасывают (они данные сами очень хитро разбивают). Однако, возвращаясь к отправителю данных, да программа может исчерпать всю память компьютера, если её так написать, но никто так разумеется не пишет, так как такая программа будет снесена после первого же использования. А в ряде случаев, например, при исполнении скриптов на серверах, количество отводимой скрипту памяти ограничивается конфигурационным файлом. Например, в PHP можно делать что угодно, но если зарезервированный им объем памяти превысит значение memory_limit - скрипт будет остановлен. Однако, если это Windows-программа, да еще запущенная из под администратора, Windows отдаст ей все, что она попросит, лишь предупредит, что у неё больше нет памяти и она увеличивает файл подкачки. Кто на C++ пишет, такое много раз видел, если совершал ошибки с быстрой утечкой памяти. Это не очень опасно, так как сразу видно и ошибку можно пофиксить, гораздо хуже, когда память течет медленно - вот это сложно отловить. Запускаешь сервер с потреблением 1Gb, а через неделю он уже жрет 3, и единственный способ вернуть память - его перегружать. Вот это настоящий бич современного ПО. | |
|
|
|
|
|
|
|
для: cheops
(14.11.2011 в 18:16)
| | Ну вот возьмём к примеру ситуацию: процесс создал сокет и записал туда тело htttp-запроса на очень большой файл. Вызвал системный вызов, порция данных в сокете прошла многоступенчатую обработку, была расщеплена сначала на tcp пакеты, затем на ip, затем на Ethernet-фреймы и эти самые ip-пакеты отправились по магистральным каналам до сервера data-центра. Далее они прошли обратную обработку, и наконец, сервер начал клиентскому компьютеру отпраавлять ip-пакеты представляющие из себя тело файла. Вот они собираються на клиентском компьютере обратно в tcp-пакеты и помещаються в оперативную память клиенсткого компьютера, но файл-то очень большой, а пакеты идут и идут от сервера, а память уже заканчиваеться....И что, в этом случае сокет будет разорван, клиентский компьютер отправит серверу tcp-пакет о завершении соединения? И если всё же сервер не "послушаеться" клиента, а будет дальше отправлять ip-пакеты, то это будет считаться уже DDOS-ом клиентского компьютера? | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 12:42)
| | Давайте рассмотрим сначала ситуацию когда речь идет о браузере/клиенте. Браузеры, да и другое сетевое ПО устроены таким образом, что они пишут информацию сначала на диск во временную папку - ведь пользователь потом может нажать кнопку Назад и где браузеру брать страницу, опять качать? Поэтому все, что он получает он сначала пишет на диск, а потом пытается это открыть (если он не сможет адресовать памяти для открытия файла, он просто откажется его открывать).
Теперь пусть это не браузер, а самописанный клиент, который ничего не сохраняет на диск, а прямо так и пишет в память, постоянно её резервируя у операционной системы. Да, в этом случае можно выжрать всю память, при этом клиент не сможет работать, соответственно и принимать данные, не смотря на то, что они будут еще некоторое время приходить на сетевую плату. Более того, сервер ничего не пошлет компьютеру, если он не запросит следующий кусок данных и не подтвердит получения предыдущих кусков... просто не получив ответа, он разорвет соединение по тайм-ауту, поэтому никакого DDOS не будет.
Ситуация которую вы моделируете более характерна для серверов, которым деваться некуда и которых бомбардируют тысячи клиентов. Не смотря на то, что сервер шлет не двухсмысленные ответы и клиенты прекращают посылать ему пакеты, есть еще люди/вирусы/трояны которые грубо говоря постоянно жмут кнопку "обновить" и деваться серверу некуда, даже чуть отдышавшись, ему опять приходится работать с очередью запросов (вот это и есть DDOS - сама сеть не позволит слать длительное время запросы, если их никто не принимает, поэтому для DDOS нужно много источников). Да любой сервер можно довести до состояния отказа от обслуживания. Даже если сервер хорошо и экономно написан. Более того, сервера стараются организовать так, чтобы они не всю память отдавали на обслуживание сервисов. Да, на серверах имеются ошибки, которые позволяют обходить эти ограничения и все-таки загнать его в состояние полной неработоспособности (иногда малым количеством обращений) - их регулярно исправляют.
PS Именно поэтому такой всплеск интереса к JavaScript/Flash - нужно как можно быстрее перенести часть работы с перегруженных серверов, которые обходятся в круглую сумму, на клиентов, которых очень много и которые нихрена не делают. Это и есть основной тренд сегодняшнего Интернета. Чтобы как в торронтах - сервер только координировал работу клиентов и мог бы обеспечивать терабайтные потоки без малейшего напряжения или угрозы для собственной работоспособности: пусть клиенты сами выбирают какой объем работы они потянут. | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 13:20)
| | [QUOTE]
Поэтому все, что он получает он сначала пишет на диск, а потом пытается это открыть
[/QUOTE]
Ну к примеру, клиент используя функцию socket_read ($socket, 1024); прочтёт килобайт данных из сокета (т.е. из оперативной памяти) и запишет их на диск, затем ещё вызовет и дозапишет файл, и так в цикле будет вызвать эту функцию (по-моему так и работают клиенты), но ведь сокет наполняеться без участия клиента, а сокет в свою очередь размещаеться в оперативной памяти, которая может быть очень быстро исчерпана присылаемыми ip-пакетами...Рано или поздно система закроет сокет, а клиенту отправит ошибку... | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 13:41)
| | Упрощенно говоря, пока сервер не получит от клиента ASK, он ему новую порцию не отправит (на самом деле там движущееся окно и речь идет о совокупности ASK-пакетов, чтобы дело побыстрее двигалось). Но в любом случае речь идет о небольшом куске данных. Вас возможно вводит в заблуждение PHP-функция fsockopen() - это довольно далеко от настоящих сокетов, где вы прописываете буквально все, не только размер буфера, но и сколько вы из него отправляете или получаете в него - вы в любой момент можете принять решение об увеличение памяти, или сокращении, о приостановке отправки запросов или возобновлении. fsockopen() и сокеты PHP - это упрощенные оболочки над сокетами, чтобы программисты не плодили ошибок и просто могли спокойно работать, но в PHP есть ограничение на размер памяти, отводимой скрипту, поэтому порушить сервер ими не получится (да и буфер в них зашит скорее всего довольно ограничений, можем порыться в исходных кодах, если вам будет интересно), а в других системах таких упрощенных механизмов нет. Да вы конечно можете попытаться оттяпать у операционной системы пару гигабайт, но это будет ваше осмысленное решение - сколько под буферы выделять памяти, сами же сокеты или сетевые платы ничего такого делать не могут. | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 13:59)
| | [QUOTE]
Упрощенно говоря, пока сервер не получит от клиента ASK, он ему новую порцию не отправит
[/QUOTE]
Да, это понятно. Но ведь сам клиент(тем более php-скрипт) не может напрямую влиять на tcp/ip уровень. Он может только пассивно читать то, что ему дают из сокета. А сокет наполняеться системой. Так вот я и хочу выяснить - какие механизмы контроля существуют для контроля переполнения оперативной памяти из-за сокета как со стороны системы, так и в клиентских приложениях(и в частности php) | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 14:41)
| | В php.ini есть директива memory_limit, которая задает количество памяти, отводимой PHP-скрипту. Если скрипт попытается получить памяти больше этого предела, он будет остановлен с возвращением ошибки
Fatal error: Allowed memory size of ... bytes exhausted (tried to allocate ... bytes) in
|
PS Если же ограничение снято в php.ini, то ничто не помешает ему забрать всю память системы, до тех пор пока Apache или сам PHP (если он запущен независимо) не грохнется и система не получит память обратно (или не будет перезагружена - от системы и её настроек уже зависит). | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 14:55)
| | [QUOTE]
до тех пор пока Apache или сам PHP (если он запущен независимо) не грохнется и система не получит память обратно
[/QUOTE]
Т.е. последовательность действий такова: после исчерпания всей досутпной оперативной памяти система завершит аварийно Apache, в составе которого исполняеться php, соотвественно все открытые apache сокеты будут закрыты, и серверу направлен разрыв соединения? А сам сокет размещаеться в адресном пространстве процесса, создавшем это сокет, и когда мы вызываем, к примеру $dump = socket_create($socket, 1024); мы просто копируем из одного участка пользовательского процесса(сокета) в другой участок(переменную $dump) | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 15:07)
| | Если работа завершается аварийно, система может не направлять никаких разрывов и с другого конца сокет подождет и закроет его сам по тайм-ауту.
>А сам сокет размещаеться в адресном пространстве процесса, создавшем это сокет, и когда мы
>вызываем, к примеру $dump = socket_create($socket, 1024); мы просто копируем из одного
>участка пользовательского процесса(сокета) в другой участок(переменную $dump)
Упрощенно говоря да, но у меня подозрение, что в PHP от socket_create() до реальных сокетов много чего происходит. Скорее всего там где-то зашит буфер и пока он не очищается, система не посылает и не принимает новых данных. Поэтому чтобы исчерпать память, вам придется собрать все $dump в один большой кусок, который ограничивает memory_limit. | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 15:34)
| | Ну а вообще в других библиотеках(например языка C) существуют способы очистки сокета? Вот к примеру вызвали мы функцию $dump = socket_create ($socket, 1024); записали $dump в файл на диске, очистили эти 1024 считанных байт из сокета, чтобы не занимали места в оперативной памяти, затем по новой вызвали socket_create? | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 15:45)
| | И ещё в догонку: что происходит с сокетами и оперативной памятью при попытке скачать очень большой файл, ну скажем с помощью cURL или file_get_contents()? Уже эти-то функции точно не будут очищать сокеты.... | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 15:48)
| | Сокеты в socket_read, cURL, file_get_contents() - одинаковые, вернее последние два задействуют библиотеку потоков PHP (stream), а уж они задействуют сокеты. Однако, эта память не сокетов, а функций, если вы начинаете качать файл цельным куском в переменную скрипта, то это целиком лежит на вашей ответственности. Либо исчерпаете память скрипта, либо, если нет ограничений - память компьютера. По уму при таких операциях сначала определяют размер файла, посылая HTTP-заголовок HEAD, чтобы прикинуть хватит или нет памяти выделенной скрипту. Или сразу читают блоками по 1024 байта, если размер заранее получить невозможно (сразу отправляя эти кусочки на диск). | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 16:01)
| | [QUOTE]
читают файл блоками по 1024 байта
[/QUOTE]
Такая возможность заложена в протокол http? | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 16:15)
| | Протокол HTTP вообще не занимается транспортными проблемами, на это есть транспортные протоколы. HTTP выше их и выше сокетов. Транспортными проблемами занимаются сокеты и протоколы TCP (гарантированная доставка пакетов - Web, почта, FTP и т.п.), UDP (негарантированная доставка пакетов - сервисы точного времени, игры и т.п.). На уровне сокетов вы размер фрагмента можете задавать сами, хоть 1024, хоть 4096, хоть 45 байт, хоть 4Гб (а 64-битных системах можно и больше). Чтобы вам не пришло в голову, протокол TCP разобъет ваши данные на TCP-сегменты, причем размер сегментов будет согласован между отправителем и получателем в момент установки соединения. До чего бы они не договорились размер TCP-сегмента не будет превышать 536 байт (28 байт на заголовки - остальное на данные). Далее этот пакет передается на уровень протокола IP и к нему прибавляется еще 28 байт заголовков IP-пакета (сам IP-пакет уже может быть побольше - 65 Кб) и т.д. вплоть до физических протоколов конкретных сред (Интернет где по кабелю идет, где по оптике, где по радио-релейке, где по спутнику - везде свои протоколы). Получается такая матрешка, в начале идут заголовки самого низкого протокола, потом в данных пакет более высокоуровневого протокола, который опять содержит заголовки и область данных, в которых находится пакет более высокоуровнего протокола. А в самом низу кусок HTTP-кода. На другом конце все собирается снова в полноценный HTTP-документ. За протоколы и промежуточные узлы не переживайте - они себя уронить большим пакетом не дадут. | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 15:45)
| | >$dump = socket_create ($socket, 1024);
Строго говоря речь конечно идет о socket_read(), а не socket_create(), но дело не в этом. В С это несколько по другому выглядит, там вы шагу не ступите не выделив памяти. Однако, даже в PHP вы же $dump каждый раз перезаписываете, т.е. реально это выглядит так
<?php
...
while($dump = socket_create ($socket, 1024))
{
// Вместо вывода в файл, может быть вывод в поток
// т.е. можно сразу отправлять браузеру
fwrite($fd, $dump);
}
...
?>
| Т.е. у вас в каждый момент времени при получении файла занято только 1Кб оперативной памяти, и вы этим 1Кб вычерпываете хоть мегабайтный, хоть гигабайтный файл. В C же вы еще жестче все контролируете, мало того, что вы определяете сколько байт запросить у сервера, вы также определяете в каком объеме вы будете его хранить (складировать в памяти, сколько складировать, что делать, когда все заполнено). Там не получится просто взять и случайно по ошибке исчерпать всю память, как в PHP (это может быть только осознанным актом или действием неуча, который C не знает). С-программист несет ответственность за каждый байт, который он взял у системы - он все должен явно брать и все явно возвращать, кроме самых базовых типов (именно поэтому на C писать так сложно и столько ошибок в ПО). | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 16:11)
| | [QUOTE]
Однако, даже в PHP вы же $dump каждый раз перезаписываете
[/QUOTE]
Вот это на мой взгляд очень хорошая фраза, она для меня многое проясняет. Действительно, сокет как файл - мы записали в него, потом считали, записали, считали, и таким образом мы использовали всего 1 кб оперативной памяти. Но тут есть два вопроса:
1)
Вот к примеру цикл:
do
{
socket_write($socket, $dump);
socket_read($socket, $dump);
}
while ();
Функция socket_write(); пишет в сокет , а после неё сразу вызываеться socket_read(). Но ведь в момент вызова socket_read(), данные ещё ведь не поступили? Как система "заставляет ждать" функции чтения из файлов? Я знаю есть блокирующий режим сокетов - это означает, что функция записи считаеться выполненной только тогда, когда она не только записала данные в сокет, но и ответные данные пришли?
2) Что значит "забить канал"(как его можно забить, если система поочередно передаёт и принимает данные из созданных сокетов) и почему хостеры не дают выполняться скриптам больше 30 секунд?
Ведь если на сервере реализована вытесняющая многозадачность, то что мешает php-скрипту (как треду процесса сервера) выполняться скажем с меньшим приоритетом чем другие php-скрипты? | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 17:19)
| | 1. А где вы этот пример взяли? Какой-то он странный, $dump не инициализирован, потом получается в тот же самый сокет отправляются полученные из него данные.
>Как система "заставляет ждать" функции чтения из файлов?
Они сами себя заставляют ждать, если у них заполнился их внутренний буфер, они не будут ничего читать из сети/буфера сокета, пока они не очистят буфер (не сбросят его на диск) для приема следующей порции данных. На уровне сокетов такие вещи тоже происходят и пока сокет/библиотека занимается своими делами часть данных может быть не получена, но это не страшно, так как используется протокол TCP с гарантированной доставкой пакетов, все, что не дошло будет запрошено и доставлено повторно. | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 17:19)
| | 2. Вообще под новые вопросы лучше заводить новые темы, тем более вы умудряетесь в одном вопросе задавать сразу несколько :) Канал забивается очень просто. В локальной сети компьютеры отправляют широковещательные каналы всем сразу хостам (тот у кого совпадает MAC-адрес с тем, что указан в пакете, забирает его себе, остальные игнорируют) - это происходит в разные моменты времени, поэтому все работает быстро и четко. Чем шире канал, тем больший объем данных может пройти по нему за менее короткий срок. Иногда происходят коллизии - два сигнала отправляются одновременно, в результате оба узла берут тайм-аут, если сразу после тайм-аута еще одна коллизия - время тайм-аута увеличивается. Если таких коллизий очень много - все лезут в Интернет через узкий канал, то тайм-ауты на всех машинах стремительно возрастают, а в сети идут постоянные коллизии, а у вас ничего не грузится, так как у вас либо тайм-аут, либо очередная коллизия.
PS Кстати, именно поэтому существуют ограничения на количество компьютеров в локальной сети и длину кабеля. Если мы будем писать учебник по сетям, давайте делать это в нескольких темах :))) | |
|
|
|
|
|
|
|
для: dump
(15.11.2011 в 17:19)
| | 3. 30-секунд это вот для чего. Скрипту выделяют достаточно много оперативной памяти от 8 до 32Мб, если к серверу присоединиться 100 клиентов и каждый будет по часу не давать скрипту завершиться, то потребуется более 3Гб оперативной памяти и где её брать для новых клиентов? Поэтому чем быстрее скрипт возвращает оперативную память серверу, тем её больше и тем больше клиентов он может обслужить. Ну а 30 секунд от балды поставлено, обычно скрипты еще быстрее срабатывают, по крайней мере к процессору это время отношения не имеет, посчитали, что этого времени должно быть за глаза (многие, кстати, на выделенных серверах выставляют другие временные ограничения или варьируют их от скрипта к скрипту). | |
|
|
|
|
|
|
|
для: cheops
(15.11.2011 в 17:37)
| | А после вызова socket_read ($socket, 1024) в php происходит считывание запрошенных 1024-байт, затем удаление этих 1024 байт из сокета для освобождения памяти? Насколько я понял, в C есть возможность удалить считанную информацию из сокета, а в php нету...При этом если в C не удалять считанные данные из сокета сразу после считывания, то буфер сокета переполниться, и "лишняя" информация в буфере сокета будет удаляться системой, начиная с "конца сокета" т.е. первыми будут удалены данные, которые пришли первыми? | |
|
|
|
|
|
|
|
для: dump
(16.11.2011 в 09:57)
| | Да при чтении информации из буфера, она из него удаляется. Буфер будет держать данные до тех пор пока вы из него их не заберете, при этом данные которые пришли первыми удаляться не будут - просто не будут приниматься новые данные. Однако, этого практически никогда не случается, так как сеть работает много медленнее компьютера. | |
|
|
|
|
|
|
|
для: cheops
(16.11.2011 в 12:23)
| | А если наоборот при записи в сокет(или любой другой тип файла) - слишком больших объёмов данных вызывающих переполнение буфера сокета - система вернёт ошибку(как на C так и на php)? И можно ли считать данные, но не удалить их из сокета? И если буфер чтения всё-таки переполнен, то в этом случае tcp-служба системы разорвёт соединение? Если разорвёться соединение, то в некоторых случаях может пойти на смарку результаты скачивания(например в случае скачивания файла по http-протоколу, без поддержки докачки)... | |
|
|
|
|
|
|
|
для: dump
(16.11.2011 в 12:53)
| | Сокет - это не безразмерная, безответная корзина, в которую сыпятся данные, а она пыжиться их поглотить (хотя в случае PHP образ близок к описываемому). Это довольно сложная библиотека, если буфер заполнен до отказа, сокет просто не будет посылать сетевые команды, чтобы другая сторона ему больше ничего не присылала и рвать соединение он не будет, пока такой команды не поступит (на C вы сами будете решать, что делать, PHP решит за вас - будет увеличивать память, пока она не закончится). Однако, если приложение будет думать слишком долго, то соединение разорвет противоположная сторона по тайм-ауту. | |
|
|
|
|
|
|
|
для: cheops
(16.11.2011 в 13:00)
| | В спецификации TCP написано, что "флаг PSH инструктирует получателя протолкнуть данные, накопившиеся в приемном буфере, в приложение пользователя". Это означает, что если получен tcp-пакет с флагом PSH будут переданы из приёмного буфера tcp-модуля операционной системы в сокет пользовательского процесса, т.е. в адресное пространство процесса?Иными словами данные tcp-пакетов сначала накапливаються в приёмном буфере tcp-модуля, которое находиться в адресном пространстве ядра операционной системы, а уже затем передаються в сокет, т.е. в адресное пространство пользовательского процесса? То же самое касаеться и флага RST, он передаёт команду на очистку буфера tcp-модуля или сокета? | |
|
|
|
|
|
|
|
для: dump
(19.11.2011 в 08:46)
| | Тут принцип действия как с файлами, когда вы пишите большой файл, то буфер вывода может обнуляться в двух случаях - буфер заполнен до отказа, наступил конец записи и нужно очистить буфер до момента его заполнения, так как больше данных не будут. Этот флаг нужен для тех же целей, сообщить - больше не ждите, ничего присылать не собираюсь (поэтому можно очищать буфер и передавать данные получателю), нужно вам еще - шлите запрос. | |
|
|
|
|