Ingeniería inversa de iOS: exploración en profundidad de las soluciones de comunicación entre procesos de iOS e introducción a los sockets locales

En el desarrollo de aplicaciones móviles, la comunicación entre procesos (IPC) es una tecnología crucial para la colaboración y el intercambio de datos entre diferentes aplicaciones. En el ecosistema iOS, los procesos y los subprocesos son conceptos básicos, y las soluciones de comunicación entre procesos brindan un soporte poderoso para la expansión de las funciones de las aplicaciones y la optimización del rendimiento.

1. Procesos e hilos: conceptos centrales de los sistemas operativos

Un proceso se refiere a una instancia de programa independiente que se ejecuta en el sistema operativo. Cada proceso tiene un espacio de memoria independiente, el aislamiento de datos y la comunicación entre diferentes procesos requieren mecanismos específicos. En iOS, cada aplicación se ejecuta en un proceso independiente, lo que garantiza el aislamiento y la estabilidad entre aplicaciones.

Un subproceso es una unidad de ejecución dentro de un proceso y un proceso puede contener varios subprocesos. Los subprocesos comparten el espacio de memoria del proceso, lo que les permite compartir datos más fácilmente. Sin embargo, la programación multiproceso también plantea problemas complejos, como la sincronización de subprocesos y las condiciones de carrera. En iOS, el hilo principal normalmente maneja las interacciones de la interfaz de usuario, mientras que los hilos en segundo plano realizan tareas que requieren mucho tiempo para mantener la capacidad de respuesta de la interfaz de usuario.

Analicemos cómo se crea el proceso desde la perspectiva del código:

1.1 Creación de procesos bajo Linux

En Linux, puede utilizar llamadas al sistema en lenguaje C para crear nuevos procesos. Entre ellos, fork() es una llamada al sistema de uso común, que se utiliza para crear un nuevo proceso, de modo que el proceso principal y el proceso secundario puedan ejecutarse en paralelo. Aquí hay un código de muestra simple que muestra cómo crear un proceso en Linux usando lenguaje C:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    
    
    pid_t child_pid;

    // 创建新进程
    child_pid = fork();

    if (child_pid < 0) {
    
    
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (child_pid == 0) {
    
    
        // 子进程执行的代码
        printf("This is the child process. PID: %d\n", getpid());
        // 子进程退出
        exit(0);
    } else {
    
    
        // 父进程执行的代码
        printf("This is the parent process. Child PID: %d\n", child_pid);
        // 等待子进程结束
        wait(NULL);
        printf("Child process has terminated.\n");
    }

    return 0;
}

En este ejemplo, fork() creará un nuevo proceso y el proceso hijo comenzará a ejecutarse desde donde regresa fork(). En el proceso hijo, imprimimos el PID del proceso hijo y usamos exit(0) para salir del proceso hijo. En el proceso principal, imprimimos el PID del proceso secundario y luego usamos esperar (NULL) para esperar a que finalice el proceso secundario. Tenga en cuenta que el proceso principal y el proceso secundario son independientes en el espacio de memoria, pero comparten recursos como descriptores de archivos.

1.2 Compile y ejecute el código anterior en iOS

En la plataforma iOS, no puede usar fork() directamente para crear un proceso porque las aplicaciones de iOS se ejecutan en un entorno sandbox y tienen algunas restricciones en la gestión y creación de procesos. En iOS, la creación y gestión de procesos está controlada por el sistema, normalmente iniciado por el hilo principal de la aplicación. Puede compilar y ejecutar normalmente, pero intentar llamar a la función fork() provocará que la aplicación falle.

1.3 El principio subyacente de fork()

Insertar descripción de la imagen aquí

Para comprender mejor la comunicación entre procesos, introduzcamos qué cambios ocurren en la parte inferior del sistema operativo cuando se llama a fork():

Copiar un proceso: cuando un proceso llama a fork(), el sistema operativo crea un nuevo proceso, llamado proceso hijo. El proceso hijo es una copia del proceso padre, incluida toda la memoria, descriptores de archivos y otros recursos del proceso padre.
Copiar espacio de memoria: el espacio de direcciones del proceso hijo será el mismo que el del proceso padre, pero el proceso hijo obtendrá una copia independiente. Esto se logra mediante el mecanismo "Copia en escritura", lo que significa que los procesos padre e hijo inicialmente comparten memoria, pero si algún proceso modifica el contenido de la memoria, el sistema operativo creará una nueva copia de la parte modificada.
Asignar ID de proceso: el sistema operativo asigna un identificador de proceso único (PID) al proceso secundario. El PID del proceso hijo es diferente del proceso padre, pero otros atributos (como UID, GID, etc.) pueden permanecer consistentes.
Manejo de descriptores de archivos: el proceso hijo heredará el descriptor de archivo del proceso padre. Los descriptores de archivos son identificadores que se utilizan para acceder a recursos de E/S, como archivos y sockets. Los procesos secundarios pueden compartir el mismo archivo o conexión de red después de heredar el descriptor del archivo.
Valor de retorno: en el proceso padre, la función fork() devuelve el PID (entero positivo) del proceso hijo. En el proceso hijo, la función fork() devuelve 0. Si fork() falla, el valor de retorno es un número negativo, lo que indica que falló la creación del proceso hijo.
Continuar la ejecución: tanto el proceso padre como el proceso hijo continúan la ejecución desde el lugar después de la llamada fork(). Dado que el proceso hijo es una copia del proceso padre, ambos comienzan a ejecutarse desde la misma ubicación del código.

Entre ellos, puede prestar especial atención al concepto de "copiar mientras se escribe", que suele ser el foco de las entrevistas en las grandes fábricas. Si está interesado, lo presentaremos más adelante y puede seguir esta columna para obtener las últimas novedades . artículos.

2. Solución de comunicación entre procesos en iOS

En iOS, el intercambio de datos y la comunicación entre diferentes aplicaciones requieren el uso de esquemas de comunicación entre procesos específicos. A continuación se muestran algunos métodos comunes de comunicación entre procesos de iOS:

**Esquema de URL:** Las aplicaciones pueden registrar un esquema de URL personalizado, llamar a la aplicación de destino a través de la URL en otras aplicaciones y transferir datos. Esto generalmente se usa para saltos de aplicaciones simples y para compartir datos.
Grupos de aplicaciones: los grupos de aplicaciones permiten que diferentes aplicaciones compartan el mismo conjunto de directorios de contenedores para almacenar datos compartidos, como preferencias y archivos.
Compartir llavero: Keychain es un contenedor seguro que se utiliza para almacenar datos confidenciales. Las aplicaciones pueden compartir datos de Keychain bajo la cuenta de desarrollador para lograr el intercambio de datos entre aplicaciones.
Notificación: las aplicaciones pueden usar el centro de notificaciones para enviar y recibir notificaciones para implementar mensajes entre aplicaciones. Esto es adecuado para escenarios de comunicación de uno a muchos.
Socket local: los procesos de iOS también pueden comunicarse a través de sockets locales. Este es un método más general. Lo presentaremos en detalle a continuación.

3. Utilice Local Socket para la comunicación entre procesos.

Local Socket es un método de comunicación entre procesos basado en sockets de red, adecuado para establecer conexiones de comunicación entre diferentes procesos en el mismo dispositivo. A continuación se muestra un breve código de ejemplo para la comunicación entre procesos utilizando Local Socket:

Proceso 1: enviar datos

import Foundation

let serverURL = URL(fileURLWithPath: "/path/to/socket")
let socket = try! Socket.create(family: .unix, type: .stream, proto: .unix)
try! socket.connect(to: serverURL)

let dataToSend = "Hello, Process 2!".data(using: .utf8)!
try! socket.write(from: dataToSend)
socket.close()

Proceso 2: recibir datos

import Foundation

let serverURL = URL(fileURLWithPath: "/path/to/socket")
let socket = try! Socket.create(family: .unix, type: .stream, proto: .unix)
try! socket.bind(to: serverURL)
try! socket.listen(maxBacklogSize: 1)

let clientSocket = try! socket.acceptClientConnection()

var receivedData = Data()
_ = try! clientSocket.read(into: &receivedData)
let receivedString = String(data: receivedData, encoding: .utf8)
print("Received data: \(receivedString ?? "")")
clientSocket.close()

Las soluciones de comunicación entre procesos de iOS tienen diferentes ventajas y usos en diferentes escenarios. Debemos elegir métodos de comunicación apropiados basados ​​en las necesidades reales para lograr el intercambio de datos y la colaboración entre aplicaciones y brindar a los usuarios una mejor experiencia.

Supongo que te gusta

Origin blog.csdn.net/zh405123507/article/details/132600899
Recomendado
Clasificación