Pythonマルチプロセッシングにおけるクライアント-サーバーIPC

この記事は、Linux用のCLIアプリケーションを開発した個人的な経験を反映しています。





明確に定義されたAPIを介して制御プログラムから要求されたときに、スーパーユーザープロセスが特権システム呼び出しを実行する方法について説明します。





ソースコードは、実際の商用アプリケーション用にPythonで記述されていますが、公開用の特定のタスクから抽象化されています。





前書き

「プロセス間通信(IPC)は、1つまたは異なるプロセスのスレッド間でデータを交換することです。これは、OSカーネルによって提供されるメカニズム、またはOSメカニズムを使用して新しいIPC機能を実装するプロセスによって実装されます。」-ウィキペディア





プロセスには、情報を交換するさまざまな理由があります。私の意見では、これらはすべてUnixカーネルのセキュリティポリシーの結果です。





ご存知のように、Unixカーネルは人間の介入なしに機能する自律システムです。実際のところ、ユーザーは、カーネルを不正な干渉から保護しているように見えるオペレーティングシステムオブジェクトです。





カーネルセキュリティとは、オペレーティングシステムのアドレス空間をカーネル空間ユーザー空間に分割することです。したがって、システムの2つの動作モード:ユーザーモードとカーネルモード。さらに、モードの変更は2つのスペース間の切り替えです。





ユーザーモードでは、システムの状態を変更するシステム呼び出し同様に、カーネルによって予約されているメモリの領域にアクセスできません





ただし、スーパーユーザーにはこのアクセス権があります。





同時実行の前提条件

プログラムが特権システム呼び出しを使用しない場合、スーパーユーザーは必要ありません。つまり、並行性なしでモノリスを作成できます。





それ以外の場合は、rootでプログラムを実行する必要があります。





, .





, , .





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





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)

      
      



, .





したがって、常にポート6000でリッスンし、データグラムが到着すると、要求を解析します。次に、リクエストで指定されたメソッドを呼び出し、実行結果をクライアントに返します。





さらに

サーバーにsystemdパッケージを装備することをお勧めます。これにより、Pythonプログラムはjournaldにログを記録できます





ビルドするには、pyinstallerを使用できます。これにより、コードがすべての依存関係を持つバイナリファイルにパッケージ化されます。前述の実行可能ファイルの命名規則を忘れないでください。





ご清聴ありがとうございました!








All Articles