El artículo refleja la experiencia personal del desarrollo de aplicaciones CLI para Linux.
Discute cómo el proceso de superusuario puede ejecutar llamadas al sistema privilegiadas cuando lo solicita el programa de control a través de una API bien definida.
El código fuente está escrito en Python para una aplicación comercial real, pero extraído de tareas específicas para su publicación.
Introducción
“La comunicación entre procesos (IPC) es el intercambio de datos entre hilos de uno o diferentes procesos. Se implementa a través de mecanismos proporcionados por el kernel del sistema operativo o por un proceso que utiliza mecanismos del sistema operativo e implementa nuevas capacidades de IPC ". - Wikipedia
Los procesos pueden tener diferentes motivos para intercambiar información. En mi opinión, todos son consecuencia de la política de seguridad del kernel de Unix.
Como saben, el kernel de Unix es un sistema autónomo que funciona sin intervención humana. De hecho, un usuario es un objeto del sistema operativo que parecía proteger al kernel de interferencias no autorizadas.
La seguridad del kernel consiste en dividir el espacio de direcciones del sistema operativo en espacio de kernel y espacio de usuario . De ahí los dos modos de funcionamiento del sistema: modo usuario y modo kernel. Además, el cambio de modos es un cambio entre dos espacios.
En el modo de usuario, las áreas de memoria reservadas por el núcleo son inaccesibles, al igual que las llamadas al sistema que cambian el estado del sistema.
Sin embargo, el superusuario tiene este acceso.
Requisitos previos de concurrencia
Si su programa no usa llamadas al sistema privilegiadas, no necesita un superusuario, lo que significa que puede escribir un monolito sin concurrencia.
De lo contrario, tendrá que ejecutar su programa en 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)
, .
Por lo tanto, siempre escucha en el puerto 6000 y analiza la solicitud cuando llega un datagrama. Luego llama al método especificado en la solicitud y devuelve el resultado de la ejecución al cliente.
Adicionalmente
Le aconsejo que equipe su servidor con el paquete systemd , que permite que los programas de Python se registren en journald .
Para compilar, puede usar pyinstaller : empaquetará su código en un archivo binario con todas las dependencias. No olvide la convención de nomenclatura para archivos ejecutables mencionada anteriormente.
¡Gracias por la atención!