Miquel van Smoorenburg <mik...@xs4all.nederland.invalid> wrote:
> In article <
slrnm9iaqh...@xs8.xs4all.nl>,
> Rob <
nom...@example.com> wrote:
>>libev is toch een user-level ding? Wat doet die dan voor geheime dingen
>>die efficienter zijn dan select? Daar kan ik me weinig bij voorstellen.
>
> Die gebruikt mechanismes zoals epoll() of kevent() die veeeel efficienter
> zijn dan select().
Aha die epoll die kende ik niet, alleen (p)select en (p)poll, en die
hebben allemaal hetzelfde nadeel. Wat (p)poll betreft is dat natuurlijk
een gemiste kans, als die alleen een array terug zou geven met de
werkelijk actieve fd's dan was er helemaal geen issue.
Maar dit is zowizo niet zo'n issue omdat 99% van het verkeer op de
eerste 3 fd's is en dan valt er weinig te zoeken.
> Wat je meestal doet is dat je alle data betreffende een verbinding
> (in/uit filedescriptors, buffers, counters, state) in een struct zet.
> Die alloceer je met malloc(). Vervolgens registreer je de filedescriptors
> met het event systeem waarbij je ook een functie-pointer en een pointer
> naar die struct meegeeft. Als er dan 'iets' gebeurt (filedesc wordt
> leesbaar of schrijfbaar) dan wordt die functie aangeroepen, met
> als argument o.a. die pointer naar je struct. Een callback systeem dus.
> Je kan natuurlijk per filedescriptor verschillende callback functies
> gebruiken als dat handig is.
Dat is min of meer wat ik ook doe in mijn programma alleen dan door
middel van een registratiesysteem voor fd's die aan select gevoerd
worden en na de return van de select worden de bitjes gescand en de
geregistreerde functies aangeroepen. Een standaard stukje code wat
ik al had liggen.
Dit kan wellicht snel worden omgebouwd naar gebruik van epoll.
> Op die manier is het veel makkelijker om per-connectie of per
> set filedescriptors iets te doen. Non-blocking is dan geen probleem.
Ik denk dat het daarvoor niet veel uitmaakt...
> Als er op de TCP socket veel data binnenkomt buffer je dat
> eerst in z'n geheel. Mogelijk moet je dat in meerdere keren lezen,
> dus je moet een counter bijhouden hoe vol je buffer inmiddels is.
Ja maar dat wil je natuurlijk niet, het ding moet zo weinig mogelijk
bufferen. Dat doet mijn implementatie ook niet, alles wat met recvmsg
ontvangen is gaat er meteen daarna met sendmsg weer uit.
> Als die data binnen is dan schrijf je die naar de client-tcp-socket.
> Als dat niet in 1x past, zet je voor alle filedescriptors het
> 'read' event uit, en alleen voor de uitgaande socket het 'write'
> event aan. Dan krijg je daarna alleen een callback als je kan
> schrijven. Net zolang tot je hele buffer geschreven is, dan draai
> je die events weer om.
Nadeel is dan dat je gedurende dat hele lange blok niks meer in kunt
voegen. Het bekende "upload beinvloedt download" probleem. Dat
heeft mijn oplossing niet.
> Overigens doe je het maken van uitgaande connecties op dezelfde
> manier, je roept connect() aan, maar die zal -EINPROGRESS
> teruggeven. Je krijgt een callback zodra de connectie er is.
> Dat is iets waar je anders ook tegenaan kan lopen, dat kan ook
> een flinke delay geven af en toe.
Ja dat doe ik al.
> Als je detecteert dat de verbinding gesloten is, zeg je tegen
> libev dat je geen interesse meer hebt in callbacks voor je
> filedescriptors, close() je alle filedescriptors, free() je de
> struct met data, en klaar.
Dat doe ik ook al. Maar een eigenschap van mijn code is dat er
niks gemalloced en gefreed wordt gedurende normaal gebruik, allen
tijdens de initialisatie (om de datastructuren per proxy te alloceren).
Er is dus geen creep in het memorygebruik. Iets wat in C vaak wel
meevalt maar waar de Java versie wel last van had. Dat zijn geen
lekken maar dat komt door fragmentatie van de memory pool en in
het geval van Java door het "garbage collector" model.