Sharing Memory mit Python und PHP

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
Hannes_P64
User
Beiträge: 4
Registriert: Mittwoch 26. April 2023, 14:14

Hallo Liebe Forengemeinde.

Auch wenn ich noch ziemlich am Anfang meiner Python Kariere bin,
habe ich eine Frage, die vielleicht doch etwas fortgeschrittener ist.

Es geht hier bei nicht um ein bestimmtes Projekt, sondern hauptsächlich um das Interesse,
wie das zu realisieren ist.

Wie die Überschrift schon vermuten lässt, will ich einen Wert von Python zu PHP und wieder zurückgeben.

Von den verschiedenen Möglichkeiten wie Socket's, gemeinsame Datei, über Datenbank und eben den gemeinsamen
Speicherbereich. Bin ich aktuell bei dem gemeinsamen Speicher angekommen.

Von einem Python zum anderen Python Prozess hat das auch mit folgendem kleinen code funktioniert.

Code: Alles auswählen

# Python Prozess1
from multiprocessing import shared_memory
shm_a = shared_memory.SharedMemory(name='0xff3', create=True, size=100) 
shm_a.buf[0] = 1							 
print(shm_a.buf[0])
benutzereingabe = input("Eingabe für Weiter:")
shm_a.unlink()

# Python Prozess2
from multiprocessing import shared_memory
shm_a = shared_memory.SharedMemory(name='0xff3', create=False)
data = shm_a.buf[0]
print(data)
benutzereingabe = input("Eingabe für Weiter:")
shm_a.close()
Nun zu PHP dazu habe ich folgenden code gefunden, und auch das funktioniert so weit.

Code: Alles auswählen

// PHP Prozess1
<?php 
// Create 100 byte shared memory block with system id of 0xff3
$shm_key = 0xff3;
$shm_id = shmop_open($shm_key, "c", 0644, 100);
if (!$shm_id) {
    echo "Couldn't create shared memory segment\n";
}
// Lets write a test string into shared memory
$shm_bytes_written = shmop_write($shm_id, "my 2hared memory block", 0);
if ($shm_bytes_written != strlen("my shared memory block")) {
    echo "Couldn't write the entire length of data\n";
}
?>

// PHP Prozess2
<?php 
// Create 100 byte shared memory block with system id of 0xff3
$shm_key = 0xff3;
$shm_id = shmop_open($shm_key, "c", 0644, 100);
if (!$shm_id) {
    echo "Couldn't create shared memory segment\n";
}
// Get shared memory block's size
$shm_size = shmop_size($shm_id);
echo "SHM Block Size: " . $shm_size . " has been created.\n";

// Now lets read the string back
$my_string = shmop_read($shm_id, 0, $shm_size);
if (!$my_string) {
    echo "Couldn't read from shared memory block\n";
}
echo "The data inside shared memory was: " . $my_string . "\n";

//Now lets delete the block and close the shared memory segment
if (!shmop_delete($shm_id)) {
    echo "Couldn't mark shared memory block for deletion.";
}
shmop_close($shm_id);

?>
So und jetzt zu meinem Problem, wenn ich PHP Prozess1 aufrufe und dann Python Prozess2.
Geht das leider nicht.
Da wohl wie erwartet PHP und Python trotz der gleichen Alias für die Speicheradresse.
Diese anders verarbeiten.
Nur wie ??? Könnte mir da bitte jemand auf die Sprünge helfen?

Danke
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die Zahl 0xff3 ist ja schon mal was anderes als ein Dateiname "0xff3". Und zumindest unter Python macht es einen Unterschied ob man unter einem Unix/Linux oder Windows `SharedMemory` verwendet, denn die Systemaufrufe unterscheiden sich da bei beiden deutlich. Man muss sich da wohl auch auf Interna verlassen, denn das `multiprocessing`-Package ist für die Kommunikation zwischen Python-Prozessen, das heisst wie auch immer dort `SharedMemory` unter der Haube implementiert ist, kann sich jederzeit ändern, denn die Gegenseite ist ja immer die gleiche Python-Version.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
Benutzeravatar
DeaD_EyE
User
Beiträge: 1121
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ich habe SharedMemory nie verwendet. Jedenfalls habe ich herausgefunden, dass man mittels mmap die angelegte Datei in /dev/shm/name mit einem anderen Prozess öffnen, lesen und schreiben kann.

Code: Alles auswählen

from multiprocessing.shared_memory import SharedMemory


shm = SharedMemory(name="abc", create=True, size=100)
view = memoryview(shm.buf)
view[0:11] = b"Hello World"

Code: Alles auswählen

import mmap


with open("/dev/shm/abc", "r+b") as fd:
    mm = mmap.mmap(fd.fileno(), 0)


print(mm[0:11])
Für PHP gibt es sicherlich Vergleichbares.
Besser ist es wahrscheinlich nicht SharedMemory zu verwenden und stattdessen direkt mmap. Die Datei muss man dann selbst erstellen und auch für die passende Größe sorgen.

Code: Alles auswählen

import mmap
import os

from pathlib import Path

# Hack
Path.truncate = lambda self, size: os.truncate(self, size)


SHM_FILE = Path("/dev/shm/abc")
if not SHM_FILE.exists():
    SHM_FILE.touch()
    # wird mal Zeit, dass truncate eine Methode von Path wird
    SHM_FILE.truncate(100)

with open(SHM_FILE, "r+b") as fd:
    mm = mmap.mmap(fd.fileno(), 0)
    mm[0:11] = b"Hello World"
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Hannes_P64
User
Beiträge: 4
Registriert: Mittwoch 26. April 2023, 14:14

Danke euch beiden für eure informativen Antworten.

__blackjack__: da hast du natürlich recht, dass eine zahl etwas anderes als ein string ist.
Aber wie du schon so schön geschrieben hast, weiß man nicht genau, was da unter der Haube so läuft.
Also ging ich von der naivsten Situation aus, um meine Frage zu stellen.

DeaD_EyE: Danke für die Beispiele werde noch ein bisschen nach mmap Rescherschieren

Unterm Strich heißt das aber, dass eine Kommunikation der beiden Sprachen, auf diesem Weg eine Sackgasse ist.
Wenn ich eure antworten richtig verstehe.

Gibt es denn eine Möglichkeit außer mit Socket, mit Python ohne zugriff auf die Festplatte daten für
andere sprachen zur Verfügung zu stellen?
Sirius3
User
Beiträge: 18051
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Problem hier ist, dass shared memory in unterschiedlichen Systemen unterschiedlich implementiert wird und daher auch die Implementation in den unterschiedlichen Sprachen unterschiedlich ist.
Pythons sharedmemory benutzt z.B. intern mmap, während PHP unter Linux shmget verwendet, deshalb wird er Key auch unterscheidlich interpretiert. Unter Windows läuft das beides auf MapViewOfFileEx hinaus. Aber wie gesagt, die Interna sind da sehr versteckt.
Wenn Du unter Linux unterwegs bist, kannst versuchen eine shmget-Implementierung für Python zu benutzen.
Unter Windows mußt Du halt wissen, dass das, was Python bei mmap tagname nennt bei PHP "TSRM_SHM_SEGMENT:%d" % shm_key ist.

Bei SharedMemory besteht das Problem, dass Du zusätzlich noch einen weiteren Kanal zur Synchronisation brauchst.

Deshalb die Frage zum Schluß: was möchtest Du überhaupt erreichen? Welche Daten sollen denn zwischen PHP und Python ausgetauscht werden? Ist SharedMemory überhaupt das richtige dafür?
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hannes_P64: Ohne Zugriff auf die Festplatte gäbe es auch immer noch die Möglichkeit eine RAM-Disk zu verwenden. Dann bieten die meisten Betriebssysteme auch IPC-Mechanismen, wo dann die API aber wieder systemspezifisch ist. Sockets sind da schon das Mittel das überall nahezu gleich funktioniert, weil sich die BSD-Socket-API ziemlich breit durchgesetzt hat.

Da hat man dann aber auch immer noch eine Auswahl zu treffen was konkret man über die Sockets laufen lässt. Davon was eigenes zu erfinden wird üblicherweise abgeraten. Es gibt schon Unmengen an Protokollen und Bibliotheken die man nutzen kann. Von verschiedenen RPC-Protokollen wie Corba (nutzt wahrscheinlich heute kaum jemand für neue Sachen), XML-RPC, JSON-RPC, oder schnell gebastelten HTTP-REST-APIs, bis zu Sachen wie Nanomsg, ZeroMQ, oder RabbitMQ. Je nach dem was man da am Ende mit machen will, was für Daten da drüber gehen müssen, und welche Sprachen oder Bibliotheken beteiligt sind.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich würde auf Domain sockets via nanomsg setzen. Damit bekommt man tausende Transaktionen / Sekunde abgehandelt. Wie das mit den eher lahmen Krücken PHP & Python ein bottleneck werden würde, möchte ich sehen.

Und so ganz ohne einen mindestens ähnlich teuren Mechanismus kommt auch Shared Memory nicht aus, außer man fährt einen Poll-Betrieb. Denn irgendwo muss ja ein Cross-Prozess-Signal etabliert werde , DASS da was passiert ist. Semaphore oder Ähnliches sind zumindest bis mehrere hundert KB da nicht langsamer als in einen Domain socket zu schreiben.
Hannes_P64
User
Beiträge: 4
Registriert: Mittwoch 26. April 2023, 14:14

@Sirius3: was möchtest Du überhaupt erreichen?

Wie schon erwähnt handelt es sich hier bei um kein spezielles Projekt, aber wenn Du wissen willst aus welchen Grund ich auf Festplatten
zugriff verzichten will, muss ich zugeben, dass es mehr ein subjektiver als ein objektiver ist.

Ich bewege mich wie wohl die meisten Bastel Wastels auf einem Raspberry Pi. Jetzt ging mir durch den Kopf, das wenn man einen Sensor
überwachen will und der viele schalt, Zyklen hat und die regelmäßig für die abfrage von php über eine Datei Kommunikation zur Verfügung
stellt, die Platte doch ziemlich stark beansprucht wird. Ein RAM aber für so etwas entwickelt wurde.

Nach etwas Googeln stolperte ich dann über Socket's und Sharing Memory.
Ich weiß zwar nicht ob eine Socket Kommunikation Festplatten zugriffe auslöst. Gehe aber jetzt mal nicht davon aus (wieso auch).
Somit ist für das aktuelle vorhaben ein Daten Austausch über Socket wohl voll aus reichend,
da muss ich Dir recht geben __deets__.

Wobei mir auch die Idee von __blackjack__ mit RAM-Disk gefällt.
Mal gucken.

Trotzdem hätte es mich Interesse halber interessiert, wie so etwas zu realisieren wäre.
Somit werde ich mir die shmget-Implementierung auch noch zu Gemüte führen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Grundsätzlich ist deine Befürchtung schon richtig - schreibzyklen will man vermeiden. Und sockets erzeugen keine. Sind also geeignet.

Das shared memory ist halt so eine Sache. Es fehlt dabei eben der Mechanismus der einem klar macht, ob die Daten valide sind oder nicht. Bei einem einzelnen 32 Bit wert ist das egal. Aber bei einer größeren Menge an Daten wird das relevant, sonst liest man partiell ungültige Daten ein.
Benutzeravatar
sparrow
User
Beiträge: 4361
Registriert: Freitag 17. April 2009, 10:28

Und dann ist da natürlich noch die Frage, warum überhaupt PHP eingesetzt wird.
nezzcarth
User
Beiträge: 1686
Registriert: Samstag 16. April 2011, 12:47

Hannes_P64 hat geschrieben: Mittwoch 26. April 2023, 14:20 Wie die Überschrift schon vermuten lässt, will ich einen Wert von Python zu PHP und wieder zurückgeben.
Mal abgesehen von Sparrows Einwand, den ich teile, kann man der Vollständigkeit halber als IPC-Mechanismus unter Linux/Unix, den man sich zumindest einmal ansehen kann, noch Named Pipes/Fifos nennen. Verglichen mit anderen Mechanismen wie z.B. Sockets sind diese weniger populär, weil man halt "Dateien" (die keine sind) im Dateisystem hat und eine Named Pipe pro Kommunikations-Richtung braucht. Ich finde sie aber einfach in der Handhabung als Sockets, deren korrekte Implementierung hier im Forum ein Dauerthema ist. Ansonsten finde ich auch bereits erwähnte Bibliotheken wie ZeroMQ recht einfach in der Handhabung.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@nezzcarth: wo ist denn der semantische Unterschied bei FIFOs zu sockets? Die POSIX Aufrufe haben doch die gleiche Semantik. Beim abliefern muss man send all nutzen bzw implementieren. Und beim lesen hat man auch keine Garantie auf eine komplett abgelieferte Nachricht.
Hannes_P64
User
Beiträge: 4
Registriert: Mittwoch 26. April 2023, 14:14

Entschuldigt mein etwas längeres schweigen.
Die frage, warum PHP hat mich erst ein mal auf eine selbst Findung Reise geschickt, auf der ich mich noch befinde ;-)
@nezzcarth die Funktion von Pipe bzw. Named Pipe war mir nicht bekannt (vielleicht sollte ich mich der shell doch etwas mehr widmen)

Danke noch mal für eure Hilfe
Antworten