2011-09-13 29 views
29

Sto scrivendo un server socket di dominio Unix per Linux.Come sapere se un processo è associato a un socket di dominio Unix?

Una particolarità dei socket di dominio Unix Ho scoperto rapidamente che, mentre si crea un socket Unix in ascolto, si crea la voce corrispondente del filesystem, chiudendo il socket non lo si rimuove. Inoltre, fino a quando la voce del filesystem non viene rimossa manualmente, non è possibile effettuare il bind() un socket per lo stesso percorso: bind() fallisce con EADDRINUSE se il percorso è già presente nel file system.

Di conseguenza, la voce del file system del socket deve essere unlink() 'su spegnimento del server per evitare di ottenere EADDRINUSE al riavvio del server. Tuttavia, questo non può sempre essere fatto (vale a dire: crash del server). La maggior parte delle domande frequenti, i messaggi del forum, Q & Un sito Web che ho trovato consigliare solo, come soluzione, a unlink() il socket prima di chiamare bind(). In questo caso, tuttavia, è preferibile sapere se un processo è associato a questo socket prima di quello unlink().

Infatti, unlink() 'un socket Unix mentre un processo è ancora associato a esso e quindi ricreare il socket di ascolto non genera alcun errore. Di conseguenza, il vecchio processo del server è ancora in esecuzione ma irraggiungibile: il vecchio socket di ascolto è "mascherato" dal nuovo. Questo comportamento deve essere evitato. Idealmente, usando i socket di dominio Unix, l'API socket dovrebbe avere esposto lo stesso comportamento di "mutua esclusione" esposto quando si vincolano i socket TCP o UDP: "Voglio associare il socket S all'indirizzo A, se un processo è "Purtroppo questo non è il caso ...

C'è un modo per far rispettare questo comportamento di" mutua esclusione "? Oppure, dato un percorso del filesystem, c'è un modo per sapere, via l'API socket, se qualche processo sul sistema ha un socket di dominio Unix legato a questo percorso? Devo utilizzare una primitiva di sincronizzazione esterna all'API socket (flock(), ...)? O mi sta sfuggendo qualcosa ?

Grazie per i vostri suggerimenti.

Nota: i socket Unix dello spazio dei nomi di Linux sembrano risolvere questo problema, in quanto non esiste una voce del file system su unlink(). Tuttavia, il server che sto scrivendo vuole essere generico: deve essere robusto contro entrambi i tipi di socket di dominio Unix, in quanto non sono responsabile della scelta degli indirizzi di ascolto.

risposta

18

So che sono molto in ritardo per la festa e che questa è stata una risposta molto tempo fa, ma ho appena incontrato questo alla ricerca di qualcos'altro e ho una proposta alternativa.

Quando si verifica il ritorno EADDRINUSE da bind() è possibile immettere una routine di controllo degli errori che si connette al socket. Se la connessione riesce, c'è un processo in esecuzione che è almeno abbastanza vivo da aver fatto il accept(). Questo mi sembra il modo più semplice e più portabile per raggiungere ciò che vuoi ottenere. Ha degli svantaggi in quanto il server che ha creato l'UDS in primo luogo potrebbe effettivamente essere ancora in esecuzione ma "bloccato" in qualche modo e incapace di fare un accept(), quindi questa soluzione non è certamente infallibile, ma è un passo avanti direzione giusta, penso.

Se lo connect() fallisce, andare avanti e unlink() l'endpoint e provare di nuovo lo bind().

9

Non penso che ci sia molto da fare oltre le cose che hai già considerato. Sembra che tu l'abbia studiato bene.

Ci sono modi per determinare se un socket è collegato a un socket unix (ovviamente lsof e netstat lo fanno) ma sono abbastanza complicati e dipendenti dal sistema che mi chiedo se valgono lo sforzo di affrontare i problemi che si presentano .

Si stanno davvero sollevando due problemi: affrontare collisioni di nomi con altre applicazioni e gestire istanze precedenti della propria app.

Per definizione più istanze del tuo pgm non dovrebbero provare a collegarsi allo stesso percorso in modo che probabilmente significhi che si desidera eseguire una sola istanza alla volta. In questo caso, puoi semplicemente utilizzare la tecnica standard pid filelock in modo che due istanze non vengano eseguite contemporaneamente. Non dovresti scollegare il socket esistente o persino correre se non riesci a ottenere il lucchetto. Questo si occupa anche dello scenario di crash del server.Se riesci a ottenere il blocco, sai che puoi scollegare il percorso del socket esistente prima del binding.

Non c'è molto che tu possa fare AFAIK per controllare altri programmi che creano collisioni. Le autorizzazioni per i file non sono perfette, ma se l'opzione è disponibile, puoi inserire la tua app nel proprio utente/gruppo. Se esiste un percorso socket esistente e non lo possiedi, non scollegarlo e inserire un messaggio di errore e consentire all'utente o amministratore di sistema di risolverlo. L'utilizzo di un file di configurazione per renderlo facilmente modificabile e disponibile per i client potrebbe funzionare. Oltre a questo, è quasi necessario un qualche tipo di servizio di discovery, che sembra un enorme overkill a meno che non si tratti di un'applicazione davvero critica.

Nel complesso si può prendere conforto che ciò non avvenga spesso.

+0

Grazie per la risposta. Usare un sistema di lockfile tradizionale è sicuramente il modo più sicuro per andare. Inoltre, se un sistema di rilevamento di servizi è eccessivo o meno: ironicamente, questo server è progettato per essere parte di un sistema di individuazione di servizi da solo (il sistema di "registrazione" di servizio sembra più appropriato). Questo dovrebbe rispondere alla tua domanda ;-) –