IPC cliente-servidor em multiprocessamento Python

O artigo reflete a experiĂŞncia pessoal de desenvolvimento de aplicativos CLI para Linux.





Ele discute como o processo de superusuário pode executar chamadas de sistema privilegiadas quando solicitado pelo programa de controle por meio de uma API bem definida.





O código-fonte é escrito em Python para um aplicativo comercial real, mas abstraído de tarefas específicas para publicação.





Introdução

“A comunicação entre processos (IPC) é a troca de dados entre threads de um ou diferentes processos. É implementado por meio de mecanismos fornecidos pelo kernel do sistema operacional ou por um processo que usa mecanismos do sistema operacional e implementa novos recursos IPC. " - Wikipedia





Os processos podem ter diferentes motivos para a troca de informações. Na minha opinião, tudo isso é consequência da política de segurança do kernel Unix.





Como você sabe, o kernel Unix é um sistema autônomo que funciona sem intervenção humana. Na verdade, um usuário é um objeto do sistema operacional que parecia proteger o kernel de interferências não autorizadas.





A segurança do kernel consiste em dividir o espaço de endereço do sistema operacional em espaço do kernel e espaço do usuário . Daí os dois modos de operação do sistema: modo de usuário e modo kernel. Além disso, uma mudança de modos é uma troca entre dois espaços.





No modo de usuário, as áreas de memória reservadas pelo kernel ficam inacessíveis, assim como as chamadas do sistema que alteram o estado do sistema.





No entanto, o superusuário tem esse acesso.





Pré-requisitos de simultaneidade

Se o seu programa não usa chamadas de sistema privilegiadas, você não precisa de um superusuário, o que significa que você pode escrever um monólito sem simultaneidade.





Caso contrário, você terá que executar seu programa como 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)

      
      



, .





Portanto, ele sempre escuta na porta 6000 e analisa a solicitação quando um datagrama chega. Em seguida, ele chama o método especificado na solicitação e retorna o resultado da execução ao cliente.





Além disso

Aconselho você a equipar seu servidor com o pacote systemd , que permite que programas Python façam login no journald .





Para construir você pode usar o pyinstaller - ele irá empacotar seu código em um arquivo binário com todas as dependências. Não se esqueça da convenção de nomenclatura para arquivos executáveis ​​mencionada anteriormente.





Obrigado pela atenção!








All Articles