Senden file-Deskriptor von Linux-Buchse
Ich versuche zu senden, einige Datei-Deskriptor von linux-socket, aber es funktioniert nicht. Was mache ich falsch? Wie soll man zum Debuggen so etwas? Ich habe versucht, perror() ist es überall möglich ist, aber Sie behauptet, dass alles ok ist. Hier ist, was ich geschrieben habe:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
void wyslij(int socket, int fd) //send fd by socket
{
struct msghdr msg = {0};
char buf[CMSG_SPACE(sizeof fd)];
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof fd);
*((int *) CMSG_DATA(cmsg)) = fd;
msg.msg_controllen = cmsg->cmsg_len; //why does example from man need it? isn't it redundant?
sendmsg(socket, &msg, 0);
}
int odbierz(int socket) //receive fd from socket
{
struct msghdr msg = {0};
recvmsg(socket, &msg, 0);
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
unsigned char * data = CMSG_DATA(cmsg);
int fd = *((int*) data); //here program stops, probably with segfault
return fd;
}
int main()
{
int sv[2];
socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
int pid = fork();
if (pid > 0) //in parent
{
close(sv[1]);
int sock = sv[0];
int fd = open("./z7.c", O_RDONLY);
wyslij(sock, fd);
close(fd);
}
else //in child
{
close(sv[0]);
int sock = sv[1];
sleep(0.5);
int fd = odbierz(sock);
}
}
Ich meinte
Warum schicken Sie die Wert der Dateideskriptor über einen socket? Was die Empfänger damit machen soll? Wenn der Empfänger nicht im gleichen Prozess wie der Absender, wird der file-Deskriptor wird bedeutungslos sein, um den Empfänger, wie ein Datei-Deskriptor ist Prozess-spezifisch. In jedem Fall, auf der Empfängerseite, bei der Verwendung von
Nur um zu überprüfen, Sie sind sich bewusst, dass es möglich ist, zu senden (nutzbare) Datei-Deskriptoren, die zwischen Prozessen mit
Ich ziehe meinen Kommentar über das senden der Wert eines file-descriptor-über Prozessgrenzen hinweg unwirksam. Aber mein Kommentar zu benötigen, um richtig zu Scannen, erhielt
FWIW, Es ist nicht Linux-spezifisch sind (viele, aber ich glaube, nicht alle Unix-Geschmacksrichtungen, die es unterstützen). Aber ich Stimme zu, dass das Scannen der
CMSG_DATA
ich weiß nicht, was Sie sind, aber ich denke, dass ich das problem gefunden.Warum schicken Sie die Wert der Dateideskriptor über einen socket? Was die Empfänger damit machen soll? Wenn der Empfänger nicht im gleichen Prozess wie der Absender, wird der file-Deskriptor wird bedeutungslos sein, um den Empfänger, wie ein Datei-Deskriptor ist Prozess-spezifisch. In jedem Fall, auf der Empfängerseite, bei der Verwendung von
recvmsg()
müssen Sie eine Schleife durch das empfangen der Nachrichten-Header auf der Suche für die SOL_SOCKET/SCM_RIGHTS
header. Jetzt sind Sie unter der Annahme, dass jede Nachricht beginnt mit SCM_RIGHTS
, und das ist wahrscheinlich nicht immer der Fall sein.Nur um zu überprüfen, Sie sind sich bewusst, dass es möglich ist, zu senden (nutzbare) Datei-Deskriptoren, die zwischen Prozessen mit
SCM_RIGHTS
und AF_UNIX
sockets?Ich ziehe meinen Kommentar über das senden der Wert eines file-descriptor-über Prozessgrenzen hinweg unwirksam. Aber mein Kommentar zu benötigen, um richtig zu Scannen, erhielt
recvmsg()
Header für SCM_RIGHTS
immer noch gilt.FWIW, Es ist nicht Linux-spezifisch sind (viele, aber ich glaube, nicht alle Unix-Geschmacksrichtungen, die es unterstützen). Aber ich Stimme zu, dass das Scannen der
recvmsg()
Header ist die beste Praxis 🙂InformationsquelleAutor Dekakaruk | 2015-01-17
Du musst angemeldet sein, um einen Kommentar abzugeben.
Stevens (et al) UNIX Network Programming, Vol 1: The Sockets Networking API beschreibt den Prozess der übertragung von Datei-Deskriptoren, zwischen Prozessen, die in Kapitel 15 Unix Domain Protokolle und speziell §15.7 Vorbei Deskriptoren. Es ist fummelig zu beschreiben, in vollem Umfang, aber es muss getan werden, auf einem Unix-domain-socket (
AF_UNIX
oderAF_LOCAL
), und den Absender-Prozess verwendetsendmsg()
während der Empfänger verwendetrecvmsg()
.Ich habe diese leicht modifiziert (und instrumentiert) version der code aus der Frage für mich zu arbeiten auf Mac OS X 10.10.1 Yosemite mit GCC 4.9.1:
Die Ausgabe des instrumentierten, aber lose version von der original-code war:
Beachten Sie, dass die Eltern fertig, bevor das Kind, so die Aufforderung erschien in der Mitte der Ausgabe.
Den Ausgang von der 'festen' code war:
Den primären signifikante änderungen waren das hinzufügen der
struct iovec
auf die Daten in derstruct msghdr
in beiden Funktionen, und bietet Raum für die in der receive-Funktion (odbierz()
) für die control-Nachricht. Ich berichtete in einem Zwischenschritt in das debugging, wo ich dasstruct iovec
zu den Eltern und die Eltern "Nachricht zu lang" - Fehler wurde entfernt. Um zu beweisen, es funktioniert (einen Datei-Deskriptor übergeben wurde), fügte ich code Lesen und drucken Sie die Datei aus dem übergebenen file-Deskriptor. Der ursprüngliche code hattesleep(0.5)
aber dasleep()
nimmt eine vorzeichenlose Ganzzahl, entspricht nicht schlafen. Ich verwendet C99 zusammengesetzte Literale zu haben, das Kind schlafen für 0,5 Sekunden. Der Elternteil schläft seit 1,5 Sekunden, so dass der Ausgang aus dem Kind abgeschlossen ist, bevor der Elternteil beendet wird. Ich könntewait()
oderwaitpid()
auch, aber war zu faul das zu tun.Ich habe nicht zurück gegangen, und überprüft, dass alle Ergänzungen wurden notwendig.
Den
"stderr.h"
header deklariert dieerr_*()
Funktionen. Es ist code, den ich schrieb (erste version vor 1987), um Fehler zu melden prägnant auf den Punkt. Dieerr_setlogopts(ERR_PID)
call-Präfixe werden alle Nachrichten mit der PID. Für timestamps aucherr_setlogopts(ERR_PID|ERR_STAMP)
würde die Arbeit machen.Problemen Ausrichtung
Nominal Tier schlägt in einem Kommentar:
Nicht nur Linux-Architekturen: sowohl SPARC-und Power benötigen ausgerichteten Daten und sind oft Solaris und AIX beziehungsweise. Once upon a time, DEC Alpha erforderlich, dass auch, aber Sie sind selten gesehen in den Bereich in diesen Tagen.
Den code in die manual-Seite
cmsg(3)
damit verbunden ist:Die Zuordnung zu
fdptr
erscheint, anzunehmen, dassCMSG_DATA(cmsg)
ist ausreichend gut ausgerichtet, um in eineint *
und diememcpy()
verwendet wird, auf der Annahme, dassNUM_FD
ist nicht nur 1. Mit dieser sagte, es soll zeigen das arraybuf
, und, die möglicherweise nicht ausreichend gut ausgerichtet, wie die Nominalen Tier schlägt, scheint es mir so, dass diefdptr
ist nur ein Eindringling, und es wäre besser, wenn das Beispiel verwendet:Und der umgekehrte Vorgang am empfangenden Ende wäre dann angemessen. Dieses Programm übergibt nur eine einzige Datei-Deskriptor, so dass der code geändert werden, um:
Gegeben, dass Mac OS X (Darwin/BSD-basis) erfordert mindestens einen
struct iovec
, selbst wenn, der beschreibt, mit der Länge null Nachricht, ich bin bereit zu glauben, dass der code oben gezeigt, umfasst ein 3-byte-Nachricht, ist ein guter Schritt in die richtige Richtung zeigen. Die Nachricht sollte vielleicht ein einzelnes null byte statt 3 Buchstaben.Habe ich überarbeitet, den code zu Lesen, wie unten gezeigt. Es nutzt
memmove()
kopieren der Datei-Deskriptor und von dercmsg
Puffer. Es überträgt eine einzelne Nachricht byte, die ein null-byte.Es hat auch die parent-Prozess-Lesen (bis zu) 32 bytes der Datei vor der Weitergabe der Datei-Deskriptor für das Kind. Das Kind wird weiterhin Lesen, wo die Eltern aufgehört haben. Dies zeigt, dass der Datei-Deskriptor übertragen enthält die Datei offset.
Receiver sollte mehr tun, eine Validierung auf der
cmsg
vor und behandeln Sie es als eine Datei-Deskriptor übergeben-Nachricht.Und eine Probe ausführen:
Froh, dass es für Sie arbeiten. Auf Mac OS X, es scheint, dass die
struct iovec
Teile benötigt werden, aber die Versand-version kannstruct iovec io = { .iov_base = "", .iov_len = 0 };
und das ist OK. Der Empfang war mitchar mbuffer[1];
, aber nicht ohne diestruct iovec
.Kann ich schlage vor, Sie ändern den code zu kopieren descriptor int mit
memcpy()
statt den Zugriff auf die Daten direkt? Es ist nicht unbedingt richtig ausgerichtet ist-das ist, warum Sie die man page Beispiel verwendet auchmemcpy()
- und es gibt viele Linux-Architekturen, in denen unverbunden int Zugriff Probleme verursacht (bis zu SIGBUS-signal Tötung der Prozess). Auch ich scheine zu erinnern, historische Themen auf verschiedenen Betriebssystemen wrt. zusätzliche Daten mit keine normale Nutzdaten, vermeiden, indem das senden mindestens ein dummy-byte zu, aber ich finde keine Referenzen zu überprüfen, so könnte ich erinnere mich falsch.Danke — siehe meine aktualisierte Antwort. Das Beispiel auf der man-Seite ist ein bisschen verwirrend mit seinen (unnötigen und unsicheren) nutzen
fdptr
. Ich denke, was ich geschrieben habe, wird überall unterstützt, dass die grundlegenden Mechanismen (aber ich gestehe, mein Test ist sehr begrenzt). Ich benutzememmove()
stattmemcpy()
denn ich sollte nicht zu kümmern, ob die Dinge sich überschneiden. Damemmove()
ist garantiert immer Arbeit undmemcpy()
ist nicht garantiert immer Arbeit ('Arbeit' Sinn 'arbeiten korrekt, unabhängig von der übergebenen Argumente'), verwende ichmemmove()
. Das ist eine persönliche Entscheidung.Die iovec-Daten scheint erforderlich zu sein, wie folgende Notiz aus der man-page (unix - (7)) heißt es: "pass Dateideskriptoren oder Anmeldeinformationen, die über einen SOCK_STREAM, müssen Sie zum senden oder empfangen von mindestens einem byte nonancillary Daten in der gleichen sendmsg(2) und recvmsg(2) Aufruf."
InformationsquelleAutor Jonathan Leffler