Toon posts:

[c++] child processen laten communiceren

Pagina: 1
Acties:

Verwijderd

Topicstarter
ik ben nu bezig met het opzetten van een simpele tcp server, welke later als basis voor een text based spel moet gaan dienen. nu ben ik zelf nog maar net bezig met network programming, en nu loop ik vast bij fork().

ik wil de server doen zonder multiplexen. (dus met een array bij houden welke sockets wel en niet open zijn, maar in 1 process) maar als ik meerdere child's heb, moeten deze met elkaar kunnen communiceren. dus als in het ene child process data ontvangen wordt, moet er de mogelijkheid zijn dit naar 1 of meerdere child-processen te sturen. zodat deze die data weer door kunnen geven naar hun clients.

ik hoop dat jullie het een beetje snappen, maar de vraag is dus: is dit mogelijk met een fork'ed server, of ontkom ik niet aan multiplexen?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 23-05 18:13
Misschien moet je multithreaden in plaats van forken? Dan moet je nog steeds voorzichtig zijn met de communicatie tussen threads, maar het wordt allemaal wel een stuk eenvoudiger.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 22 augustus 2004 @ 01:41:
ik hoop dat jullie het een beetje snappen, maar de vraag is dus: is dit mogelijk met een fork'ed server, of ontkom ik niet aan multiplexen?
Je zal naar bijvoorbeeld IPC, pipes, IP sockets of shared memory moeten kijken. Rechtstreeks zonder deze middelen kan echter niet.
v

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Als je forked maak je een nieuwe adres space aan, en kan je dus niet via static geheugen communiceren. Met threads kan dat wel. Een optie is idd om shared memory aan te maken, maar dan moet je continu heel erg uitkijken met locking etc.

Ik zou bijna altijd voor message passing gaan. Dus dat children berichten sturen naar elkaar, dat kan je implementeren met shared memory, of met pipes etc. Het scheelt je later een hoop gedoe, omdat je nu gewoon send_to_child, en receive_from_child, of receive_all calls hebt.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 23:27

Tomatoman

Fulltime prutser

Aangezien alles in hetzelfde proces draait kun je gewoon communiceren met behulp van custom messages. Dat is het eenvoudigst.

offtopic:
child's --> children

Een goede grap mag vrienden kosten.


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Als je forked draait juist niet alles in hetzelfde process.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Zoijar schreef op 22 augustus 2004 @ 12:48:
Als je forked maak je een nieuwe adres space aan, en kan je dus niet via static geheugen communiceren. Met threads kan dat wel. Een optie is idd om shared memory aan te maken, maar dan moet je continu heel erg uitkijken met locking etc.
Dat moet je met threads ook. Verder zijn threaded apps nou niet bepaald makkelijk te debuggen.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

igmar schreef op 22 augustus 2004 @ 13:39:
Dat moet je met threads ook. Verder zijn threaded apps nou niet bepaald makkelijk te debuggen.
Dat ligt eraan hoe je threading package het implementeert, maar meestal kan je gewoon bij global statics. Dit omdat je in dezelfde address space zit.

En ik zeg niet dat parallel/distributed programmeren makkelijk is, sterker nog denk ik dat het het moeilijkste is wat je kan doen qua programmeren.

Verwijderd

Topicstarter
tnx voor de reacties.

ik heb nu in de tussentijd al ff een multiplexed servertje opgezet. dus met een fd_set. maar zal ook zeker ff threads en shared memory opzoeken, kijken of dat een wat mooier programma geeft dan ik nu heb.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Zoijar schreef op 22 augustus 2004 @ 13:58:
Dat ligt eraan hoe je threading package het implementeert, maar meestal kan je gewoon bij global statics. Dit omdat je in dezelfde address space zit.
Daarvoor doe je geen locking, locking doe je om te voorkomen dat door een contex switch je data corrupt raakt doordat 2 of meer threads tegelijk de data aan het bewerken zijn.

Verwijderd

Normalerwijze is het efficienter om een multiplexed (cq. non-blocking) servertje te schrijven. Een threaded of child-process server moet context switchen, dwz. de kernel moet omschakelen tussen de threads/children bij elke blocking operatie, hetgeen betekent dat hoe meer clients je server heeft, hoe trager hij wordt doordat er steeds meer context switches plaatsvinden. Een non-blocking server doet een controleerbaar aantal context switches tussen de kernel en het ene proces en dat aantal context switches blijft gelijk ongeacht het aantal clients, dwz. een non-blocking server schaalt (veel) beter bij een groot aantal clients.

Je kunt een simpele schil opzetten die de operaties object georienteerd maakt:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
class PolledFd {
public:
    static void poll_fds(const Timeval& timeout);

    virtual void on_read(const Timeval& timestamp) = 0;
    virtual void on_write(const Timeval& timestamp) = 0;
    virtual void on_error(const Timeval& timestamp, int errorcode) = 0;

protected:
    int _fd;
    PolledFd* _next;
};

Dat heeft bij spelservers het bijkomende voordeel dat je opzet van de grond af aan event-driven is, je kunt vanuit die on_read() gegevens lezen en een on_player_input() aanroepen en zo structureer je de hele serveropzet verder. Omdat de server maar uit een proces bestaat kun je vanuit de on_XXX() methods zonder problemen andere PolledFd objecten manipuleren en is het schrijven van een interactieve server makkelijk (veel gemakkelijker dan met inter- thread of process communication).

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Standaard C++ heeft (nog) geen threads of sockets, dus het is belangrijk om te weten welk OS/API je gebruikt. Als ik fork lees gok ik POSIX/Linux? In dat geval kun je inderdaad forken en via shared memory met de andere processen communiceren. Ook een shared DB (bv MySQL) zou kunnen.

Los daarvan, "multiplexen" of beter gezegd, select() is in de praktijk de manier om zware servers te bouwen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

igmar schreef op 22 augustus 2004 @ 16:49:
Daarvoor doe je geen locking, locking doe je om te voorkomen dat door een contex switch je data corrupt raakt doordat 2 of meer threads tegelijk de data aan het bewerken zijn.
Uhmm, ik lees geen locking in mijn zin. Een context switch heeft er niets mee te maken, dat is iets dat je os/processor doet als hij naar een andere thread overschakelt. Locking is misschien niet de best beschrijvende term, toegegeven, maar volstaat wel om het algeme idee aan te geven. Concurrency control was beter gewees, of het voorkomen van race condities. In processor context zorgt een lock ervoor dat de bus niet gedeeld wordt, en er geen cache wordt gebruikt.

Verwijderd

Topicstarter
ik heb nu idd besloten hem maar gewoon met select() te doen, en is nu ook werkend zoals ik wilde, me al wat extra spul als extra data per discriptor. hij is op dit moment niet echt event driven(heb hem nu in standaard c staan, omdat ik daar wat meer ervaring in heb, en voor mijn eerste ervaring met tcp ff zo min mogelijk andere struikelpunten wil dan die ik tegenkom bij de connectietroep). maar wil dat komende week ff uit gaan proberen. wil hem toch wel object georienteerd in c++ hebben.

maar idd is gewoon posix/linux.

zeker omdat het de bedoeling is zeker 300 gebruikers te ondersteunen, heb ik maar afgezien van fork's of threads. ik dacht eerst dat losse processen of threads beter/efficienter zouden werken. maar ben er nu achter dat dat dus niet zo is ;)

in ieder geval bedankt voor de reacties.

Verwijderd

Verwijderd schreef op 25 augustus 2004 @ 00:58:
ik heb nu idd besloten hem maar gewoon met select() te doen, en is nu ook werkend zoals ik wilde, me al wat extra spul als extra data per discriptor.
Tipje: kijk eens naar poll() ipv. select(). Als je 300 gebruikers verwacht kun je het normaal gezien nog met select() af, maar select() kent een voorgedefinieerd maximum aantal descriptors (FD_SETSIZE voor fd_set) wat dus ook een arbitrair maximum aantal clients voor je server betekent.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Zoijar schreef op 23 augustus 2004 @ 03:03:
Uhmm, ik lees geen locking in mijn zin.
Ik wel :

'Een optie is idd om shared memory aan te maken, maar dan moet je continu heel erg uitkijken met locking etc.'
Een context switch heeft er niets mee te maken, dat is iets dat je os/processor doet als hij naar een andere thread overschakelt. Locking is misschien niet de best beschrijvende term, toegegeven, maar volstaat wel om het algeme idee aan te geven. Concurrency control was beter gewees, of het voorkomen van race condities. In processor context zorgt een lock ervoor dat de bus niet gedeeld wordt, en er geen cache wordt gebruikt.
Concurrency control doe je om te voorkomen dat je data corrupt raakt omdat 2 threads de data lopen te bewerken, en er tijdens het bewerken een context switch plaatsvind. Locking op bus nivo is iets wat je compiler dient te regelen, normaliter hoef je je daar als gebruiker / programmeur niet mee bezig te houden.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

igmar schreef op 25 augustus 2004 @ 11:21:
Concurrency control doe je om te voorkomen dat je data corrupt raakt omdat 2 threads de data lopen te bewerken, en er tijdens het bewerken een context switch plaatsvind. Locking op bus nivo is iets wat je compiler dient te regelen, normaliter hoef je je daar als gebruiker / programmeur niet mee bezig te houden.
Je heb het nodig om een semaphore te implementeren. Op x86 bv "lock bts". En niet je compiler, maar je operating system moet dat regelen met library calls. Tenzij je een 'moderne' compiler gebruikt, die zelf code paralleliseert (orca, parallel fortran, etc).
Het gaat erom dat twee threads nooit tegelijk dezelfde data bewerken. Je moet er vanuit gaan dat een context switch altijd kan gebeuren op elk moment.

Maar waarom discussieren over terminologie? Ik gebruik gewoon even het wordt 'locken' omdat iedereen dan wel begrijpt wat er bedoeld wordt. Ik in ieder geval wel, ik studeer af in dit gedoe...

(Met message passing kan je makkelijk 'monitors' van je classes maken. Dwz dat er altijd maar 1 functie tegelijk actief kan zijn. Dat is nuttig, omdat je je dan nergens druk over hoeft te maken...behalve in je mp routines, maar dat is library als het goed is)

[ Voor 14% gewijzigd door Zoijar op 25-08-2004 12:13 ]


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Zoijar schreef op 25 augustus 2004 @ 12:10:
Je heb het nodig om een semaphore te implementeren. Op x86 bv "lock bts". En niet je compiler, maar je operating system moet dat regelen met library calls. Tenzij je een 'moderne' compiler gebruikt, die zelf code paralleliseert (orca, parallel fortran, etc).
Ik kan me niet voorstellen dat je als programmeur zelf je low-level locking uitprogrammeert. Hoe er gelocked word is CPU en OS afhankelijk, en je hoeft als programmeur alleen maar de te zorgen dat de functies worden aangeroepen.
Het gaat erom dat twee threads nooit tegelijk dezelfde data bewerken. Je moet er vanuit gaan dat een context switch altijd kan gebeuren op elk moment.
Niet helemaal correct, maar da's we het idee ja.
Maar waarom discussieren over terminologie? Ik gebruik gewoon even het wordt 'locken' omdat iedereen dan wel begrijpt wat er bedoeld wordt.
Omdat terminologie in dit geval belangrijk is, en fout gebruik ervan zinloze discussie kan opleveren.

Ik viel vooral over locking met shared memory, omdat dat impliceerde dat je daar met threads geen last van zou hebben.
Ik in ieder geval wel, ik studeer af in dit gedoe...
Ik ben d'r in afgestudeerd :)
(Met message passing kan je makkelijk 'monitors' van je classes maken. Dwz dat er altijd maar 1 functie tegelijk actief kan zijn. Dat is nuttig, omdat je je dan nergens druk over hoeft te maken...behalve in je mp routines, maar dat is library als het goed is)
MP heeft mijn voorkeur ook. Ee mm lib maakt dit allemaal een stuk makkelijker voor de programmeur, en je hoeft je ook geen zorgen meer te maken om OS afhankelijke details.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

igmar schreef op 25 augustus 2004 @ 14:50:
Ik viel vooral over locking met shared memory, omdat dat impliceerde dat je daar met threads geen last van zou hebben.
Oh ok ik zie nu wat je bedoelt. Maar nee, dat impliceerde ik niet. Mijn gedachte gang was dat de TS al fork gebruikte, en dus geen shared geheugen had (itt threads die dat wel kunnen hebben). Op het moment dat je dan shared geheugen tussen processen gaat maken, moet je dat controleren. Bij threads natuurlijk ook, maar die werden niet gebruikt.

Onze dicussie is verder redelijk zinloos, moeten we nou elkaar overtuigen van iets dat we beiden al weten? Te veel inhaken op terminologie, als de bedoeling duidelijk is, geeft juist van die zinloze discussies. En dat gebeurt hier al zo veel in P&W. Ik leer geen boek definities uit mijn hoofd (die dan alsnog vaak dubbelzinning zijn), het is belangrijker om het algemene plaatje in je hoofd te hebben en daar dan aan te refereren.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Voor de TS : Als je met shared mem aan de gang gaat, kan ik je aanraden om OSSP MM te gebruiken. Het spaart je iig behoorlijk wat werk uit betreft het afhandelen van OS specifieke zaken.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 23-05 18:13
igmar schreef op 25 augustus 2004 @ 11:21:
Concurrency control doe je om te voorkomen dat je data corrupt raakt omdat 2 threads de data lopen te bewerken, en er tijdens het bewerken een context switch plaatsvind. Locking op bus nivo is iets wat je compiler dient te regelen, normaliter hoef je je daar als gebruiker / programmeur niet mee bezig te houden.
Zelfs zonder context switches kun je op een multi-processor-systeem in de problemen komen. Juist daarom moet je op de bus locken (anders kun je er op de x86 ook wel gebruik van maken dat increment/decrement atomaire operaties zijn).

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Verwijderd schreef op 25 augustus 2004 @ 02:10:
Tipje: kijk eens naar poll() ipv. select(). Als je 300 gebruikers verwacht kun je het normaal gezien nog met select() af, maar select() kent een voorgedefinieerd maximum aantal descriptors (FD_SETSIZE voor fd_set) wat dus ook een arbitrair maximum aantal clients voor je server betekent.
FD_SETSIZE is standaard 1024 in Linux, dus dat is voorlopig toch geen probleem?
Ook in Windows kun je met #define FD_SETSIZE 1024 de sets vergroten.

Verder is het voordeel dat select ook onder Windows nog werkt.

[ Voor 6% gewijzigd door Olaf van der Spek op 25-08-2004 15:37 ]


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

[quote]Soultaker schreef op 25 augustus 2004 @ 15:35:
Zelfs zonder context switches kun je op een multi-processor-systeem in de problemen komen. Juist daarom moet je op de bus locken (anders kun je er op de x86 ook wel gebruik van maken dat increment/decrement atomaire operaties zijn).
Klopt, maar is nog steeds niet iets wat je je als gebruiker druk om hoeft te maken. Dit soort low-level zaken zijn architectuur specifiek, en dienen door het OS / libs afgehandeld te worden.

Verwijderd

OlafvdSpek schreef op 25 augustus 2004 @ 15:36:
FD_SETSIZE is standaard 1024 in Linux, dus dat is voorlopig toch geen probleem?
Ook in Windows kun je met #define FD_SETSIZE 1024 de sets vergroten.

Verder is het voordeel dat select ook onder Windows nog werkt.
Volgens mij specificeert POSIX niet dat je FD_SETSIZE naar een andere waarde mag definen (ik zou me kunnen vergissen, maar het lijkt me sterk). Daarnaast is de schaalbaarheid van poll() vs. select() in het verleden uitvoerig bestudeerd:

Gaurav Banga and Jeffrey C. Mogul. Scalable kernel performance for Internet servers under realistic loads. In Proceedings of the USENIX Annual Technical Conference, June 1998.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
In addition,
our results also show that, contrary to conventional wisdom,
even a select() based server can provide high
throughput, even though it has high overhead, if its overhead
is amortized by performing more useful work per
select() call.
structures. In general, poll() has a smaller overhead
than select() if the interest set or ready set
is sparse. But if these sets are dense, then the overhead
is usually higher because pollfd structures
are bigger than a bit (they are a few bytes typically).
Other than that, poll() has the same problems as
select().
Dus de een is niet strikt beter dan de ander.

[ Voor 95% gewijzigd door Olaf van der Spek op 25-08-2004 16:13 ]


Verwijderd

OlafvdSpek schreef op 25 augustus 2004 @ 16:06:
Dus de een is niet strikt beter dan de ander.
Ik had die HP paper ook gevonden bij het zoeken naar die USENIX transscript ;) Volgens mij redeneert dit heen om het punt dat de ready set meestal sparse is, het komt slechts zelden voor dat zowat alle fds die gepolled worden ook een event te verwerken krijgen. Hoe verzadigd die sets zijn is afhankelijk van een groot aantal externe omstandigheden (snelheid vd. client-verbindingen, timeout van de vorige poll call ed.), maar de praktijk leert dat je met poll() normalerwijze beter zeilt dan met select().
Pagina: 1