DOMANDA [c] select e server

Pupo96

Nuovo Utente
5
0
Ciao a tutti, devo scrivere il codice di un server. Il server è un pool di thread avente un listener single thread che accetta le richieste dai client e workers multi-thread che accettano al massimo una delle n operazioni che devono svolgere i client. Dovrei usare la funzione select o la pselect (per avere una gestione migliore dei segnali) per il controllo dei descrittori pronti. Ho letto molto sulla select ma non riesco bene a capire come implementare il tutto e ho qualche dubbio sul corretto utilizzo di questa funzione. L'idea che mi sono fatto è: I client vegono creati e fanno la richiesta di connessione al listener. Il listener prende l'fd del client e lo inserisce nel set della select. Ogni volta che un client ha qualcosa da scrivere o da leggere la select me lo notifica e a quel punto sveglio i worker che prenderanno in incarico la richiesta. La mia idea è corretta ed implementabile? Mi date qualche suggerimento sull'implementazione?
Leggevo tuttavia in alcuni esempi che la select controlla tutto il server (quindi sia listener sia workers). In questo caso non saprei come implementare il tutto perchè io non so a priori i fd dei client.
Uno dei miei dubbi più grandi è come fare "dormire" tutti i workers che non stanno operando e come svegliarne 1 solo in modo equo (quindi non posso svegliarne 1 in particolare ma dovrebbe essere casuale). La select non svolge questa operazione quindi la devo implementare io tramite le lock, wait e signal?
 
Ultima modifica:

signore del tempo

Utente Èlite
3,228
491
CPU
Intel Core i5 4670K
Scheda Madre
Asus Z87-Plus
HDD
WD Caviar Green 500GB
RAM
G.Skill Ares 2x4GB 1600MHz
GPU
Sapphire 7850 1GB @ 1050MHz
Audio
Integrata
Monitor
Acer V193w
PSU
XFX ProSeries 550W Core Edition
Case
CM HAF 912 plus
OS
ArchLinux + KDE - Windows 10
La select(2) non è delle system calls più semplici da gestire: ti consiglio di provare le varie casistiche (ro, rw) su 1 o pochi descrittori semplici, come stdin o un file. In effetti, la select(2), è piuttosto inefficiente e poco scalabile (oltre che non sempre immediata da impostare; il famoso "event-loop") [dai uno sguardo al C10K problem per curiosità, anche se non credo sia lontanamente da tenere conto nel tuo caso]: per questo, ti consiglio la poll(2), che è molto più intuitiva e credo la apprezzerai maggiormente, benché equivalenti. Poi chiaramente se devi necessariamente adoperare la select, è un altro discorso.

L'idea che mi sono fatto è: I client vegono creati e fanno la richiesta di connessione al listener. Il listener prende l'fd del client e lo inserisce nel set della select. Ogni volta che un client ha qualcosa da scrivere o da leggere la select me lo notifica e a quel punto sveglio i worker che prenderanno in incarico la richiesta. La mia idea è corretta ed implementabile? Mi date qualche suggerimento sull'implementazione?
Sì, l'idea è più o meno corretta. È pratica comune avere, sul thread principale, un socket in listening, guardato dalla select ovviamente, ed una lista di socket connessi altrettanto guardati che quando richiedono attenzione vengono processati separatamente (processo e/o thread).

Uno dei miei dubbi più grandi è come fare "dormire" tutti i workers che non stanno operando e come
svegliarne 1 solo in modo equo
Se è casuale, non è equo. Ti serve uno scheduler, per quanto semplici i suoi criteri saranno. Comunque, per addormentare i threads, una volta creati e joinati, hai due modi: condition variables (pthread_cond_wait) o i segnali (sigaddset + sigwait + pthread_kill con ad esempio SIGUSR1), che non ti consiglio.
 
  • Mi piace
Reazioni: Pupo96

Pupo96

Nuovo Utente
5
0
Ciao e grazie per la risposta!

ti consiglio la poll(2), che è molto più intuitiva e credo la apprezzerai maggiormente, benché equivalenti. Poi chiaramente se devi necessariamente adoperare la select, è un altro discorso.

In questo progetto sono abbastanza libero di decidere l'implementazione. Credo utilizzerò la poll(2) dato che è più efficiente. Avevo deciso di usare la select dato che l'avevamo studiata durante il corso di studi e avevo qualche idea su come utilizzarla. La poll(2) però sembra abbastanza semplice da usare! Grazie mille!

Sì, l'idea è più o meno corretta. È pratica comune avere, sul thread principale, un socket in listening, guardato dalla select ovviamente, ed una lista di socket connessi altrettanto guardati che quando richiedono attenzione vengono processati separatamente (processo e/o thread).

L'idea rimane la stessa anche se uso la poll(2) giusto? Escludendo l'implementazione della funzione, l'idea generale credo sia la stessa dato che le funzioni sono più o meno equivalenti come dicevi tu.

Se è casuale, non è equo. Ti serve uno scheduler, per quanto semplici i suoi criteri saranno.

Si hai ragione! Mi sono espresso male. Intendevo che devo garantire un uso equo dei thread workers ed evitare che ne lavori solo uno. Quindi ho pensato che scegliere in modo casuale i thread sarebbe bastato a "risolvere" questo problema. Per quanto riguarda lo scheduler come potrei implementarlo? Potresti passarmi, se puoi, qualche guida?

Comunque, per addormentare i threads, una volta creati e joinati, hai due modi: condition variables (pthread_cond_wait) o i segnali (sigaddset + sigwait + pthread_kill con ad esempio SIGUSR1), che non ti consiglio.

Accetto il tuoi consiglio e userò le condition variables! Le trovo più facili da usare rispetto la gestione dei segnali!

Ancora un grazie gigantesco!
 

signore del tempo

Utente Èlite
3,228
491
CPU
Intel Core i5 4670K
Scheda Madre
Asus Z87-Plus
HDD
WD Caviar Green 500GB
RAM
G.Skill Ares 2x4GB 1600MHz
GPU
Sapphire 7850 1GB @ 1050MHz
Audio
Integrata
Monitor
Acer V193w
PSU
XFX ProSeries 550W Core Edition
Case
CM HAF 912 plus
OS
ArchLinux + KDE - Windows 10
L'idea rimane la stessa anche se uso la poll(2) giusto? Escludendo l'implementazione della funzione, l'idea generale credo sia la stessa dato che le funzioni sono più o meno equivalenti come dicevi tu.
Sì: pensa che molti progetti event-driven, il più notabile di questi è forse proprio Nginx, o comunque librerie come libevent (ti consiglio un'occhiata), hanno delle macro per switchare tra i vari metodi di I/O multiplexing disponibili e chiaramente sceglierne il migliore disponibile, che purtroppo ha quasi sempre lo svantaggio di essere OS-specific; per Linux, questo si chiama epoll(2); BSD ha kqueue(2); Windows le I/O completition ports ecc. Laddove non trovi alternative fai un fall-back proprio a poll/select che, essendo abbastanza semplici da implementare, al costo di una scarsa scalabilità (select(2) ha perfino un estremo superiore sul numero di sockets, indicato da FD_MAX se non sbaglio [spesso 1024]) sono disponibili praticamente ovunque.
Intendevo che devo garantire un uso equo dei thread workers ed evitare che ne lavori solo uno. Quindi ho pensato che scegliere in modo casuale i thread sarebbe bastato a "risolvere" questo problema. Per quanto riguarda lo scheduler come potrei implementarlo? Potresti passarmi, se puoi, qualche guida?
In realtà a te non serve realmente uno scheduling come per i processi, bensì come distribuire la computazione parallelamente; ho fatto un po' di confusione anch'io. Comunque non è difficile, ci sono molti modi di farlo [già mi viene in mente static allocation vs on-demand] e magari c'è una via preferenziale del prof del tuo cds. Dicci un po' tu: personalmente, ma credo valga in senso lato, non so cosa fai fare e quindi le tue competenze.
Ancora un grazie gigantesco!
Non c'è problema. :vv:
 
  • Mi piace
Reazioni: Pupo96

Pupo96

Nuovo Utente
5
0
In realtà a te non serve realmente uno scheduling come per i processi, bensì come distribuire la computazione parallelamente; ho fatto un po' di confusione anch'io. Comunque non è difficile, ci sono molti modi di farlo [già mi viene in mente static allocation vs on-demand] e magari c'è una via preferenziale del prof del tuo cds. Dicci un po' tu: personalmente, ma credo valga in senso lato, non so cosa fai fare e quindi le tue competenze.

Studio informatica e devo fare questo progetto di sistemi operativi. Comunque (spero di aver capito bene) i workers devono essere allocati staticamente. Ho 2 file di configurazione del server in cui è specificato, oltre vari parametri, il numero di workers che devono essere presenti.

Per ovviare a questo problema di "scheduling" pensavo di utilizzare una variabile condizione sulla quale si bloccano i workers e ogni volta che c'è qualcosa da fare viene svegliato il primo in coda. Credo che come distribuzione del lavoro vada bene, il primo che finisce è il primo a ricevere un lavoro. Cosa ne pensi?
 

signore del tempo

Utente Èlite
3,228
491
CPU
Intel Core i5 4670K
Scheda Madre
Asus Z87-Plus
HDD
WD Caviar Green 500GB
RAM
G.Skill Ares 2x4GB 1600MHz
GPU
Sapphire 7850 1GB @ 1050MHz
Audio
Integrata
Monitor
Acer V193w
PSU
XFX ProSeries 550W Core Edition
Case
CM HAF 912 plus
OS
ArchLinux + KDE - Windows 10
  • Mi piace
Reazioni: Pupo96

Pupo96

Nuovo Utente
5
0
...che dovresti provare e vedere.
Ciao. Tutto sta procedendo molto bene. Ho un dubbio sul set della poll e spero che tu mi possa aiutare. In alcuni test ho pochi client mentre in altri ne ho molti. Il set della poll dovrebbe essere quindi dinamico. Se il numero dei client aumenta possa utilizzare la realloc per aumentare il set. Ma se dopo averlo ingrandito lo volessi ridurre avrei vari problemi. Ad esempio se avessi 100 client in un set grande 100 posti e in un momento t avrei solo 20 client se riduco la dimensione del set potrei perdere dei client (client ad esempio che occupano le ultime posizioni andrebbero persi). Volevo evitare di fare procedure costose per aggirare il problema. Ti viene in mente qualche suggerimento? Il tempo impiegato dalla poll per controllare gli FD inseriti dipende dalla grandezza del set giusto? Avere ad esempio un set grande 300-400 posizioni è molto costoso in termini di tempo?
 

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!