LINUX

Una buena comprensión de D-BUS: un mecanismo de IPC en Linux

D-BUS es un mecanismo de IPC (comunicación entre procesos) que ayuda a las aplicaciones a comunicarse entre sí. D-Bus (Bus de escritorio) es un IPC simple, desarrollado como parte de proyectos freedesktop.

comprensión de dbus IPC

Proporciona una capa de abstracción sobre varias aplicaciones para exponer sus funcionalidades y posibilidades. Si desea utilizar alguna función de una aplicación para que otro programa realice una tarea específica, puede implementarla fácilmente haciendo que el proceso sea compatible con D-Bus.

Una vez que una aplicación se hace compatible con D-Bus, no es necesario volver a compilar o incrustar código en ella para que se comunique con otras aplicaciones. Una cosa realmente interesante de D-Bus es que ayuda a los desarrolladores a escribir código para cualquier aplicación compatible con D-Bus en un idioma de su elección. Actualmente, los enlaces D-Bus están disponibles para C / C ++, Glib, Java, Python, Perl, Ruby, etc.

D-Bus es un sistema de bus de mensajes, una forma sencilla para que las aplicaciones se comuniquen entre sí, D-Bus proporciona un sistema y un demonio de sesión.

El demonio del sistema se inicia en el nivel de inicio del sistema y se usa principalmente para eventos de hardware, mientras que el demonio de sesión se inicia cuando el usuario inicia sesión en un entorno de escritorio y se usa para que las aplicaciones de escritorio se conecten entre sí.

Nota: El desarrollador de Dbus siempre recomienda el uso de una biblioteca de enlace DBus, como dbus-glib o dbus-qt, en lugar de usar la API de DBus directamente, dijeron que la API de DBus aún no está congelada y al usar esta API directamente el El programador se está registrando para algo de dolor, en mi opinión, para entender claramente cualquier biblioteca de enlace DBus, es una muy buena idea sumergirse en la programación de bajo nivel DBus, tenga en cuenta que lo que vamos a usar aquí es el parte trivial de la API DBus.

Internos de D-Bus

D-Bus es un demonio de servicio que se ejecuta en segundo plano. Usamos demonios de bus para interactuar con aplicaciones y sus funcionalidades. El demonio de bus reenvía y recibe mensajes hacia y desde aplicaciones. Hay dos tipos de demonios de bus: SessionBus y SystemBus.

Antes de comenzar a hacer cualquier código, hay algunos términos con los que uno debe estar familiarizado:

Conexión DBus:

DBusConnection es la estructura que se usa para abrir una conexión al demonio, ya sea el demonio del bus del sistema especificando DBUS_BUS_SYSTEM o al demonio del bus de sesión usando DBUS_BUS_SESSION.

Mensaje DBus:

Es simplemente un mensaje entre dos procesos, toda la intercomunicación DBus se realiza usando DBus Message, estos mensajes pueden tener los siguientes tipos, llamadas a métodos, retornos de métodos, señales y errores. La estructura DBusMessage puede realizar parámetros, añadiendo al mensaje enteros booleanos, números reales, cadenas, matrices, …

Camino:

Es la ruta de un objeto remoto, ejemplo / org / freedesktop / DBus.

Interfaz:

Es la interfaz de un Objeto dado con el que hablar.

Señal:

Es un mensaje DBus para realizar una emisión de señal.

Método de llamada:

Es un mensaje DBus que se utiliza para invocar un método en un objeto remoto.

Error de DBus:

DBusError es la estructura que contiene el código de error que se produce al llamar a un método DBus.

Conseguir una conexión de autobús:

DBusConnection *connection;

DBusError error;

dbus_error_init(&error); /* Initialize the error structure */

connection = dbus_bus_get(DBUS_BUS_SESSION,&error); /* Or DBUS_BUS_SYSTEM */

if ( dbus_error_is_set(&error) )

{

printf("Error connecting to the daemon bus: %s",error.message);

dbus_error_free(&error);

}

Por ahora, hemos aprendido los conceptos básicos de DBus, a continuación aprenderemos detalles internos de una aplicación basada en DBus, mediante ejemplos.

Ejemplo 1: reserva de un nombre de bus

Solo para familiarizarnos más con la programación DBus, en este ejemplo veremos cómo podemos reservar un nombre de bus para nuestra pequeña aplicación. Existe alguna restricción que DBus aplica a los nombres de bus, son una cadena UTF-8 válida y deben tener al menos un ‘.’ que separa el nombre de los elementos, cada elemento debe contener al menos un carácter, ejemplo «org.freedesktop». , para obtener una lista completa, lea la sección Nombres de bus de la especificación DBus.

#include <stdio.h>

#include <dbus/dbus.h>

int main()

{

DBusConnection *connection;

DBusError error;

 

char *name = "org.share.linux";

dbus_error_init(&error);

connection = dbus_bus_get(DBUS_BUS_SESSION, &error);

if ( dbus_error_is_set(&error) )

{

printf("Error connecting to the daemon bus: %s",error.message);

dbus_error_free(&error);

return 1;

}

dbus_bool_t ret = dbus_bus_name_has_owner(connection,name,&error);

if ( dbus_error_is_set(&error) )

{

dbus_error_free(&error);

printf("DBus Error: %sn",error.message);

return 1;

}

if ( ret == FALSE )

{

printf("Bus name %s doesn't have an owner, reserving it...n",name);

int request_name_reply =

dbus_bus_request_name( connection,name, DBUS_NAME_FLAG_DO_NOT_QUEUE,

&error);

if ( dbus_error_is_set(&error) )

{

dbus_error_free(&error);

printf("Error requesting a bus name: %sn",error.message);

return 1;

}

 

if ( request_name_reply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER )

{

printf("Bus name %s Successfully reserved!n",name);

return 0;

}

else

{

printf("Failed to reserve name %sn",name);

return 1;

}

}

else

/*

if ret of method dbus_bus_name_has_owner is TRUE, then this is useful for

detecting if your application is already running and had reserved a bus name

unless somebody stole this name from you, so better to choose a correct bus

name

*/

{

printf("%s is already reservedn", name);

return 1;

}

return 0;

}

Hemos utilizado la bandera DBUS_NAME_FLAG_DO_NOT_QUEUE, DBus no nos pondrá en cola en caso de que el nombre del bus que queremos reservar ya esté en uso. Para obtener una lista completa de indicadores para dbus_bus_request_name y el código de retorno, consulte la API de DBus. Un nombre de bus solicitado siempre se puede liberar usando dbus_bus_release_name.

Para compilar el código anterior, se debe instalar el paquete de desarrollo DBus, dependiendo de su distribución, el nombre de este paquete puede diferir, pero debe ser algo así como libdbus-dev (en un sistema Slackware, todo el paquete viene con sus archivos de desarrollo). Luego compila el código con el siguiente comando:

gcc `pkg-config --libs --cflags dbus-1` example1.c -o example1

pkg-config intenta encontrar el archivo dbus-1.pc, normalmente este archivo se encuentra junto con otros en / usr / lib / pkgconfig, y ese tipo de archivos contiene información sobre las bibliotecas para vincular.

Ejemplo 2: conectar dos aplicaciones de escritorio

En este ejemplo, usaremos DBus para conectar dos aplicaciones de escritorio, una escucha los mensajes DBus y la otra envía mensajes DBus, pero antes de comenzar, el programa de escucha no solo debe comenzar y salir, tiene que esperar eventos, por lo que tenemos para encontrar una manera de organizar los eventos enviados a nuestro programa, una solución simple para esto es usar el bucle de eventos principal de glib, al usarlo podemos mantener nuestro programa en modo de suspensión hasta recibir eventos, otro problema ocurre es el hecho de que cómo podemos integrar nuestra conexión de bus con el bucle de eventos principal glib, aquí viene dbus-glib, por lo que nuestro pequeño programa dependerá también de dbus-glib para una sola llamada, dbus_connection_setup_with_g_main, esta llamada integra el bucle principal glib y los eventos de bus DBus .

Aquí surge una pregunta, si queremos usar solo DBus, cómo podemos evitar el uso de su enlace glib, la respuesta no es simple, primero tenemos que escribir nuestros propios eventos de bucle e integrarlos con los eventos del bus, una buena El comienzo es mirar la fuente DBus, ya que tienen un código útil en dbus / dbus-mainloop, pero para simplificar nuestro trabajo usaremos dbus-glib.

escucha.c

En este programa usaremos dbus_bus_add_match (DbusConnection *, const char * rule, DBusError *) para agregar una coincidencia para los mensajes que queremos recibir, la cadena de reglas tiene un formato específico, consulte la regla de coincidencia DBus para obtener detalles completos.

#include <stdio.h>

#include <dbus/dbus.h>

#include <dbus/dbus-glib.h>

#include <glib.h>

static DBusHandlerResult

dbus_filter (DBusConnection *connection, DBusMessage *message, void *user_data)

{

 

if ( dbus_message_is_signal(message,"org.share.linux","Customize" ) )

{

printf("Message cutomize receivedn");

return DBUS_HANDLER_RESULT_HANDLED;

}

if ( dbus_message_is_signal(message,"org.share.linux","Quit" ) )

{

printf("Message quit receivedn");

GMainLoop *loop = (GMainLoop*) user_data;

g_main_loop_quit(loop);

return DBUS_HANDLER_RESULT_HANDLED;

}

return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

}

int main()

{

DBusConnection *connection;

DBusError error;

/* glib main loop */

GMainLoop *loop;

loop = g_main_loop_new(NULL,FALSE);

dbus_error_init(&error);

connection = dbus_bus_get(DBUS_BUS_SESSION, &error);

if ( dbus_error_is_set(&error) )

{

printf("Error connecting to the daemon bus: %s",error.message);

dbus_error_free(&error);

return 1;

}

dbus_bus_add_match (connection,

"type="signal",interface="org.share.linux"",NULL);

dbus_connection_add_filter (connection, dbus_filter, loop, NULL);

/* dbus-glib call */

dbus_connection_setup_with_g_main(connection,NULL);

/* run glib main loop */

g_main_loop_run(loop);

return 0;

}

enviar.c

#include <stdio.h>

#include <dbus/dbus.h>

static void

send_config(DBusConnection *connection)

{

DBusMessage *message;

message = dbus_message_new_signal ("/org/share/linux",

"org.share.linux",

"Config");

/* Send the signal */

dbus_connection_send (connection, message, NULL);

dbus_message_unref (message);

}

static void

send_quit (DBusConnection *connection)

{

DBusMessage *message;

message = dbus_message_new_signal ("/org/share/linux",

"org.share.linux",

"Quit");

/* Send the signal */

dbus_connection_send (connection, message, NULL);

dbus_message_unref (message);

}

int

main (int argc, char **argv)

{

DBusConnection *connection;

DBusError error;

dbus_error_init (&error);

connection = dbus_bus_get (DBUS_BUS_SESSION, &error);

if (!connection)

{

printf ("Failed to connect to the D-BUS daemon: %s", error.message);

dbus_error_free (&error);

return 1;

}

if ( argc == 1 )

{

return 0;

}

 

int i;

for ( i = 1; i < argc; i++)

{

if (!strcmp(argv[i],"-c") )

{

send_config(connection);

}

else if ( !strcmp(argv[i],"-q") )

{

send_quit(connection);

}

}

return 0;

}

Para compilar, ejecute los siguientes comandos:

gcc `pkg-config --libs --cflags dbus-1 glib-2.0 dbus-glib-1` listen.c -o listen

gcc `pkg-config --libs --cflags dbus-1` send.c -o send

Ejemplo 3: servicios DBus

El bus de mensajes puede iniciar aplicaciones (servicios) en nombre de otras aplicaciones, la aplicación le pide a DBus que inicie un servicio por su nombre, generalmente el nombre debe conocerse como org.freedesktop.TextEditor.

Para que DBus encuentre el ejecutable correspondiente a un nombre en particular, el demonio de bus busca archivos de descripción de servicio que generalmente están instalados en / usr / share / dbus-1 / services y tienen .service en su nombre de extensión (todas las distribuciones de Linux que sé que usan este prefijo para instalar archivos de servicios dbus), como un ejemplo de un archivo de servicio.

Ejemplo de archivo de servicio DBus:

[D-BUS Service]

Name=org.share.linux

Exec=path to the executable.

Escribiremos dos programas, uno es el servicio que queremos iniciar y el otro es la aplicación que activa este servicio

share-linux-serivce-example.c

#include <stdio.h>

#include <dbus/dbus.h>

int main()

{

DBusConnection *connection;

DBusError error;

 

dbus_error_init(&error);

connection = dbus_bus_get(DBUS_BUS_STARTER, &error); /* DBUS_BUS_STARTER is

the bus that started us */

/* Do something here to make sure that the application was successfully

started by DBus

* Example could be something like

* FILE *tmp;

* tmp = fopen("/tmp/share-linux-service.result", "w");

* fprintf(tmp,"share-linux service was started successfully");

* fclose(tmp);

* /

 

/* After that you have the service up, so you can do whetever you like */

dbus_connection_unref(connection);

 

return 0;

}

Compile este ejemplo con el argumento dbus-1 para pkg-config, necesita instalar el archivo de servicio en / usr / share / dbus-1 / service, asígnele el nombre org.share.linux y edite el

Ruta de ejecución a donde tiene el binario de ejemplo de servicio.

start-service.c

#include <stdio.h>

#include <dbus/dbus.h>int main()

{

DBusConnection *connection;

DBusError error;

DBusMessage *message;

 

const char *service_name = "org.share.linux";

dbus_uint32_t flag; /* Currently this is not used by DBus, they say it is for

futur expansion*/

dbus_bool_t result;

 

dbus_error_init(&error);

 

connection = dbus_bus_get(DBUS_BUS_SESSION, &error);

 

if ( dbus_error_is_set(&error) )

{

printf("Error getting dbus connection: %sn",error.message);

dbus_error_free(&error);

dbus_connection_unref(connection);

return 0;

}

 

message = dbus_message_new_method_call("org.freedesktop.DBus",

"/org/freedesktop/DBus",

"org.freedesktop.DBus",

"StartServiceByName");

 

if ( !message )

{

printf("Error creating DBus messagen");

dbus_connection_unref(connection);

return 0;

}

 

dbus_message_set_no_reply(message, TRUE); /* We don't want to receive a reply

*/

 

/* Append the argument to the message, must ends with DBUS_TYPE_UINT32 */

dbus_message_append_args(message,

DBUS_TYPE_STRING,

&service_name,

DBUS_TYPE_UINT32,

&flag,

DBUS_TYPE_INVALID);

 

result = dbus_connection_send(connection, message, NULL);

 

 

if ( result == TRUE )

{

printf("Successfully activating the %s servicen",service_name);

}

else

{

printf("Failed to activate the %s servicen",service_name);

}

dbus_message_unref(message);

dbus_connection_unref(connection);

return 0;

}

Depuración de D-BUS

Para depurar aplicaciones basadas en D-Bus, podemos usar DBus-monitor para examinar los eventos para su análisis. Alternativamente, podemos usar algunas de las herramientas de depuración fácilmente disponibles, como la herramienta de depuración D-Feet D-Bus (escrita por John Palmeri).

Conclusión

En el mundo de las aplicaciones de hoy, la mayoría de las aplicaciones de GNOME y KDE vienen con soporte de interfaz DBus. Facilita que las aplicaciones se comuniquen entre sí y elimina la tarea de alto grado de volver a compilar cada aplicación para hacerla compatible con otra.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba
Cerrar