UNIXwork

File Descriptor zwischen Prozessen austauschen

05. März 2017

Es ist möglich, dass mehrere Prozesse Zugriff auf die gleichen File-Deskriptoren haben. Beispielsweise werden bei einem Fork alle File-Deskriptoren kopiert. Der Kind-Prozess kann dann auf die selben Dateien oder Sockets zugreifen.

Man kann auch nach einem Fork oder mit nicht-verwandten Prozessen File-Deskriptoren austauschen. Dies geht über Unix Domain Sockets mit den Funktionen sendmsg und recvmsg.

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

Die Nachricht vom Typ struct msghdr hat so einige Felder, die man erstmal ausfüllen muss.

struct msghdr {
	void         *msg_name;       /* optional address */
	socklen_t     msg_namelen;    /* size of address */
	struct iovec *msg_iov;        /* scatter/gather array */
	size_t        msg_iovlen;     /* # elements in msg_iov */
	void         *msg_control;    /* ancillary data, see below */
	size_t        msg_controllen; /* ancillary data buffer len */
	int           msg_flags;      /* flags on received message */
 };

Das Feld msg_name wird hier nicht benötigt, daher setzen wir msg_name und msg_namelen auf 0.

struct msghdr msg;
msg.msg_name = NULL;
msg.msg_namelen = 0;

Die eigentlichen Daten überträgt man mit dem Feld msg_iov, ein Array an Buffern, wie man es von writev kennt. Man erstellt ein Array mit struct iovec Elementen und gibt jeweils einen Pointer auf die Daten und die Länge an. Es reicht natürlich, nur einen Buffer anzugeben.

struct iovec iov[1] ;
iov[0].iov_base = (void*)buf;
iov[0].iov_len = len;
	 
msg.msg_iov = iov;
msg.msg_iovlen = 1;

Jetzt wollen wir noch einen Filedescriptor übertragen. Für solche (und andere) Zwecke gibt es das Feld msg_control für folgende struct:

struct cmsghdr {
	size_t cmsg_len;    /* Data byte count, including header
	                       (type is socklen_t in POSIX) */
	int    cmsg_level;  /* Originating protocol */
	int    cmsg_type;   /* Protocol-specific type */
	/* followed by
	   unsigned char cmsg_data[]; */
};

Nach den Feldern der struct müssen also noch zusätzliche Daten im Speicher liegen. Für den Fall, dass man Filedeskriptoren übertragen möchte, müssen ein oder mehrere Integer folgen. Es empfiehlt sich zunächst einen statischen Buffer mit der entsprechenden Größe anzulegen. Hierfür gibt es das Makro CMSG_SPACE, was die Größe des Buffers liefert.

char cmsg_buf[CMSG_SPACE(sizeof(int))];

Anschließend setzen wir die Felder der struct.

struct cmsghdr *cmsg = (struct cmsghdr*)cmsg_buf;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS; // indicates fd payload data

Jetzt fehlt nur noch der Filedescriptor. Dieser muss, wie schon erwähnt, hinter der struct liegen. Mit dem Makro CMSG_DATA erhält man einen Pointer auf die Payload-Data.

int *data = (int*)CMSG_DATA(cmsg);
*data = fd;

Jetzt setzen wir nur noch msg_control und msg_controllen, sowie msg_flags. Danach kann sendmsg aufgerufen werden.

msg.msg_control = cmsg;
msg.msg_controllen = CMSG_LEN(sizeof(int));
	
msg.msg_flags = 0;
   
sendmsg(sock, &msg, 0);

Um einen Filedescriptor zu empfangen nutzt man recvmsg, was in der Benutzung ähnlich ist. Dort kommt die selbe struct für die Message zum Einsatz und man muss die Felder auch genauso setzen.

Ich hab ein kleines Beispielprogramm geschrieben, welches nach einem Fork eine Datei im Elternprozess öffnet und anschließend den Filedescriptor dieser Datei an den Kindprozess sendet. Das Kind empfängt dann den Filedescriptor und schreibt etwas in diese Datei.

Autor: Olaf | 0 Kommentare | Tags: unix, c, socket, ipc

Linkdump

20. März 2016
Autor: Olaf | 0 Kommentare | Tags: links, unix, postgresql

postgresql embedded

05. Dezember 2019

Wenn man für seine Anwendung eine embedded Datenbank braucht, aber keine Schrott-DB wie SQLIte (no offense) benutzen will, kann man auch einfach Postgresql nutzen. Ganz ohne umständliche systemweite Installation. Man benötigt nur die Binaries, die man natürlich bequem per Paketverwaltung installieren kann, und eine Konfiguration, die in keinster Weise mit anderen Postgres-Instanzen interferiert.

Erreichen kann man dies sehr einfach. Es gibt nur zwei Hindernisse:

Die Lösung ist trivial. Man kann einfach in der Konfiguration ein anderes Verzeichnis angeben. Und TCP-Verbindungen deaktivieren wir einfach, denn wer keine Ports braucht, dem können sie auch nicht fehlen.

Was wir zunächst benötigen, ist ein schöner Ort für unsere Datenbank. Hier im Beispiel ist das $HOME/pg/

$ mkdir $HOME/pg

Danach erstellen wir eine Konfiguration für unsere Datenbank mit dem Postgresql-Tool initdb

$ cd $HOME/pg
$ initdb -D data

Dies erstellt den Ordner data, der diverse Konfigurationsdateien, aber auch die eigentlichen Daten der Datenbank enthält.

In $HOME/pg/data/ befindet sich die Konfigurationsdatei postgresql.conf. In dieser müssen wir zwei Dinge ändern bzw. einfügen. Um das TCP-Socket zu deaktivieren:

listen_addresses = ''

Das Verzeichnis, in welchem Postgresql sein Unix-Domain-Socket ablegt, wird über die Direktive unix_socket_directories konfiguriert. Hier geben wir am besten keinen absoluten Pfad an, sondern einen relativen Pfad. Dieser bezieht sich auf das data-Verzeichnis.

unix_socket_directories = 'run'

Das run-Verzeichnis muss noch angelegt werden

$ mkdir $HOME/pg/data/run

Nun kann der Server auch schon gestartet werden.

$ pg_ctl -D $HOME/pg/data start

Was wir noch brauchen ist eine Datenbank. Diese wird mit createdb angelegt.

$ createdb -h $HOME/pg/data/run/ mydb

Das wars. Jetzt kann man sich mit beliebigen Clients verbinden. Dabei muss dann nur das Verzeichnis $HOME/pg/data/run angegeben werden. Um sich z.B. mit psql zu verbinden:

$ psql -h $HOME/pg/data/run/ -d mydb

Statt mit dem -h Parameter kann man den Pfad auch mit der Umgebungsvariable PGHOST angeben.

Wenn man jetzt weitere Postgresql-Instanzen benötigt, kann man einfach wieder mit initdb eine erstellen, und dann reicht es, wenn die Konfigurationsdatei wieder entsprechend angepasst wird.

Das Ganze ist sehr praktisch für Software-Testumgebungen oder wenn eine Anwendung seine eigene DB mitbringen soll.

Autor: Olaf | 0 Kommentare | Tags: postgresql, sql, db
Zurück