Client-Server-IPC in Python-Multiprocessing

Der Artikel spiegelt die persönlichen Erfahrungen bei der Entwicklung von CLI-Anwendungen für Linux wider.





Es wird erläutert, wie der Superuser-Prozess privilegierte Systemaufrufe ausführt, wenn er vom Steuerungsprogramm über eine genau definierte API angefordert wird.





Der Quellcode ist in Python für eine echte kommerzielle Anwendung geschrieben, jedoch von bestimmten Aufgaben zur Veröffentlichung abstrahiert.





Einführung

„Interprozesskommunikation (IPC) ist der Datenaustausch zwischen Threads eines oder mehrerer Prozesse. Es wird durch Mechanismen implementiert, die vom Betriebssystemkern bereitgestellt werden, oder durch einen Prozess, der Betriebssystemmechanismen verwendet und neue IPC-Funktionen implementiert. " - Wikipedia





Prozesse können unterschiedliche Gründe für den Informationsaustausch haben. Meiner Meinung nach sind sie alle eine Folge der Sicherheitsrichtlinien des Unix-Kernels.





Wie Sie wissen, ist der Unix-Kernel ein autonomes System, das ohne menschliches Eingreifen funktioniert. Tatsächlich ist ein Benutzer ein Betriebssystemobjekt, das den Kernel vor unbefugten Interferenzen zu schützen schien.





Bei der Kernelsicherheit geht es darum, den Adressraum des Betriebssystems in Kernelraum und Benutzerraum zu unterteilen . Daher die beiden Betriebsarten des Systems: Benutzermodus und Kernelmodus. Darüber hinaus ist ein Moduswechsel ein Wechsel zwischen zwei Räumen.





Im Benutzermodus kann nicht auf vom Kernel reservierte Speicherbereiche zugegriffen werden, ebenso wie auf Systemaufrufe , die den Status des Systems ändern.





Der Superuser hat jedoch diesen Zugriff.





Parallelitätsvoraussetzungen

Wenn Ihr Programm keine privilegierten Systemaufrufe verwendet, benötigen Sie keinen Superuser. Dies bedeutet, dass Sie einen Monolithen ohne Parallelität schreiben können.





Andernfalls müssen Sie Ihr Programm unter root ausführen.





, .





, , .





, , , , . , . , , , — .





IPC.






















, POSIX.









, POSIX.





(Message queue)





.









; , , Windows, , , IPC.









.









.









, POSIX.





(mmap)





, POSIX. . Windows , API, API, POSIX.





( )





MPI, Java RMI, CORBA .









.









, POSIX.









, POSIX.






API .





, , .





, , .





, , daemon. «d». : systemd.





, daemon , . : systemctl.





: ssh sshd.





, . , .





.





.
├── core
│   ├── api.py
│   └── __init__.py
├── main.py
      
      



core



— , . api



.





API

from multiprocessing.connection import Client
from multiprocessing.connection import Listener

#   (  )  
# 
daemon = ('localhost', 6000)
#   ( )  
#   
cli = ('localhost', 6001)

def send(request: dict) -> bool or dict:
    """
        .
     ,    
          .
    """
    with Client(daemon) as conn:
        conn.send(request)
    with Listener(cli) as listener:
        with listener.accept() as conn:
            try:
                return conn.recv()
            except EOFError:
                return False

def hello(name: str) -> send:
    """
         
    send   .
    """
    return send({
        "method": "hello",
        "name": name
    })

      
      



connection



multiprocessing



, API — socket.





Client



— , .





Listener



.





.





, Python, , . « » .





API

main.py



api



.





from core import api

response = api.hello("World!")
print(response)
      
      



. lick Framework LI , API.





API

, . .





def hello(request: dict) -> str:
    """
      .
    """
    return " ".join(["Hello", request["name"])

      
      



API

from core import api
from multiprocessing.connection import Listener
from multiprocessing.connection import Client

#   ( )   
daemon = ('localhost', 6000)
#     
cli = ('localhost', 6001)
while True:
    with Listener(daemon) as listener:
        with listener.accept() as conn:
            request = conn.recv()
            if request["method"] == "hello":
                response = api.hello(request)
            with Client(cli) as conn:
                conn.send(response)

      
      



, .





Daher überwacht es immer Port 6000 und analysiert die Anforderung, wenn ein Datagramm eintrifft. Anschließend ruft es die in der Anforderung angegebene Methode auf und gibt das Ausführungsergebnis an den Client zurück.





zusätzlich

Ich empfehle Ihnen, Ihren Server mit dem systemd- Paket auszustatten, mit dem Python-Programme sich bei journald anmelden können .





Zum Erstellen können Sie pyinstaller verwenden - es wird Ihren Code in eine Binärdatei mit allen Abhängigkeiten packen . Vergessen Sie nicht die zuvor erwähnte Namenskonvention für ausführbare Dateien.





Vielen Dank für Ihre Aufmerksamkeit!








All Articles