LINUX

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

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

entender dbus IPC

Proporciona una capa de abstracción sobre varias aplicaciones para exponer su funcionalidad y capacidades. Si desea utilizar una función determinada de una aplicación para que otro programa realice una tarea determinada, puede implementarla fácilmente haciendo consciente el proceso D-Bus.

Una vez que una aplicación es compatible con D-Bus, no hay necesidad de volver a compilar o incrustar código para que se comunique con otras aplicaciones. Una cosa muy interesante sobre D-Bus es que ayuda a los desarrolladores a escribir código para cualquier aplicación habilitada para D-Bus en el lenguaje de su elección. Actualmente, los enlaces de 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 simple 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 al iniciar el 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 usar la biblioteca de enlace de 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 que usa esta API directa. En mi opinión, para comprender claramente cualquier biblioteca de enlace de DBus, es una muy buena idea sumergirse en la programación de bajo nivel de DBus, tenga en cuenta que lo que usaremos aquí es parte de la trivialidad de la API de DBus.

Bus D interno

D-Bus es un demonio de servicio que se ejecuta en segundo plano. Usamos demonios de bus para interactuar con las aplicaciones y su funcionalidad. El demonio de bus reenvía y recibe mensajes hacia y desde las 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 debe familiarizarse:

Conexión DBus:

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

Mensaje DBus:

Es simplemente un mensaje entre dos procesos, toda la intercomunicación DBus se realiza mediante 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, agregando al mensaje enteros booleanos, números reales, cadenas, matrices, ….

Sendero:

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

Interfaz:

Es la interfaz de un objeto en particular para hablar.

Señal:

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

Llamada al método:

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

Error 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 a través de ejemplos.

Ejemplo 1: Reserva de un nombre de bus

Solo para familiarizarnos más con la programación de DBus, en este ejemplo veremos cómo podemos reservar un nombre de bus para nuestra pequeña aplicación. Existe una cierta restricción que DBus aplica a los nombres de bus, son una cadena UTF-8 válida y deben tener al menos un «.» separando los nombres de los elementos, cada elemento debe contener al menos un carácter, por ejemplo, «org.freedesktop». , para obtener una lista completa, lea la sección de especificación Bus Name 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;

}

Usamos el indicador DBUS_NAME_FLAG_DO_NOT_QUEUE, DBus no nos pondrá en cola si el nombre del bus que queremos reservar ya está en uso. Para obtener una lista completa de los indicadores y el código de retorno de dbus_bus_request_name, consulte la API de DBus. Siempre se puede liberar un nombre de bus solicitado utilizando dbus_bus_release_name.

Para compilar el código anterior, se debe instalar el paquete de desarrollo DBus, según 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 los archivos 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, este archivo generalmente se encuentra con otros en /usr/lib/pkgconfig y dichos archivos contienen información sobre las bibliotecas para vincular.

Ejemplo 2: Conexión de dos aplicaciones de escritorio

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

Aquí surge una pregunta, si queremos usar solo DBus, ¿cómo podemos evitar usar su enlace simplista? La respuesta no es simple, primero debemos escribir nuestros propios eventos de bucle e integrarlos con eventos de bus, algo bueno. 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 la regla tiene un formato específico, consulte la regla de coincidencia de 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 al DBus que inicie un servicio por su nombre, generalmente el nombre debe ser conocido, como org.freedesktop.TextEditor.

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

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, el otro es la aplicación que activa este servicio

compartir-linux-servicio-ejemplo.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, debe instalar el archivo de servicio en /usr/share/dbus-1/service, asígnele el nombre org.share.linux y edite

Exec ruta a donde tiene el binario de servicio de muestra.

iniciar-servicio.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;

}

Solución de problemas de D-BUS

Para depurar aplicaciones basadas en D-Bus, podemos usar DBus-monitor para examinar eventos para su análisis. Alternativamente, podemos usar algunas de las herramientas de depuración fácilmente disponibles, como el depurador D-Feet D-Bus (escrito 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 para la interfaz DBus. Facilita que las aplicaciones se comuniquen entre sí y elimina la tarea de alto nivel de volver a compilar cada aplicación para que sea 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