分类: 嵌入式
2009-09-11 11:25:58
对象
重点来说D-BUS是一个peer-to-peer协议---每个消息有一个来源和终点, 这些地址被作为对象路径来指定。从概念上说,所有使用D-BUS的应用程序包括一个对象的设置, 并且消息从指定的对象被发送,而不是应用程序,这个指定的被对象被对象路径鉴定确认。
另外,每个对象可以支持一个或多个接口。这些接口类似于interfaces in Java or pure virtual classes inC++
.尽管如此,没有一个操作检查是否对象实现了它们要求的接口,并且没有办法从内部查看一个对象所列出的它所支持的接口。接口被用于名称空间的方法名, 所以一个单独的对象可以用同一个名称拥有多重的方法但是是不同的接口。
消息
在D-BUS中有4种类型的消息:方法调用、方法返回、信号、错误。为了在一个D-BUS对象中执行一个方法, 你发送一个方法调用消息给对象. 它将做一些数据处理并且返回一个方法或者消息或者错误信息。信号是不同的,在信号中它们不能返回任何东西:既不是一个"signal return" 消息也不是任何其他类型的错误消息。
消息也可以有任何的参数。参数有明显的类型(booleans, bytes, integers,strings, arrays, and dictionaries)
服务
服务在D-BUS中是最高级别的抽象, 一个应用程序可以用一个bus注册一个服务,如果注册成功,应用程序就已经获得了这个服务。其他应用程序可以检查在bus里是否存在一个特定的服务,也可以让bus启动服务如果服务未启动。
Even though D-BUS is relatively new, it has been adopted very quickly.
As I mentioned earlier, udev
can be built with D-BUS support so that it
sends a signal when a device is hot-plugged. Any application can listen
to these events and perform actions when they are received. For example,
gnome-volume-manager can detect the insertion of a USB memory stick and
automatically mount it; or, it can automatically download photos when a
digital camera is plugged in.
A more amusing but far less useful example is the combination of Jamboree and Ringaling. Jamboree is a simple music player that has a D-BUS interface so that it can be told to play, go to the next song, change the volume, and so on. Ringaling is a small program that opens /dev/ttyS0 (a serial port) and watches what is received. When Ringaling sees the text "RING," it uses D-BUS to tell Jamboree to turn down the volume. The net result is that if you have a modem plugged into your computer and your phone rings, the music is turned down for you. This is what computers are for!
Now, let's walk through a few example uses of D-BUS code.
dbus-ping-send.c sends a signal over the session bus every second with the string "Ping!" as an argument. I'm using GLib to manage the bus so that I don't need to deal with the details of the bus connection myself.
#include #include #include static gboolean send_ping (DBusConnection *bus); int main (int argc, char **argv) { GMainLoop *loop; DBusConnection *bus; DBusError error; /* 1. Create a new event loop to run in */ loop = g_main_loop_new (NULL, FALSE); /* 2. Get a connection to the session bus */ dbus_error_init (&error); bus = dbus_bus_get (DBUS_BUS_SESSION, &error); if (!bus) { g_warning ("Failed to connect to the D-BUS daemon: %s", error.message); dbus_error_free (&error); return 1; } /* 3. Set up this connection to work in a GLib event loop */ dbus_connection_setup_with_g_main (bus, NULL); /* 4. Every second call send_ping() with the bus as an argument*/ g_timeout_add (1000, (GSourceFunc)send_ping, bus); /* 5. Start the event loop */ g_main_loop_run (loop); return 0; } static gboolean send_ping (DBusConnection *bus) { printf("this is sub function\n"); const char *v_STRING = "Ping!!!"; DBusMessage *message; /* Create a new signal "Ping" on the "com.burtonini.dbus.Signal" interface, * from the object "/com/burtonini/dbus/ping". */ message = dbus_message_new_signal("/com/burtonini/dbus/ping", "com.burtonini.dbus.Signal", "Ping"); dbus_message_set_no_reply(message, TRUE); /* Append the string "Ping!" to the signal */ dbus_message_append_args(message, DBUS_TYPE_STRING, &v_STRING, DBUS_TYPE_INVALID); /* Send the signal */ dbus_connection_send(bus, message, NULL); /* Free the signal now we have finished with it */ dbus_message_unref(message); /* Tell the user we send a signal */ g_print("Ping!\n"); /* Return TRUE to tell the event loop we want to be called again */ return TRUE; } Makefile: BIN = dbus-send SRCS=$(wildcard *.c) OBJS=$(patsubst %.c,%.o,$(SRCS)) #all:$(BIN) %.o:%.c gcc -g $< -o $@ -c `pkg-config dbus-glib-1 --cflags` $(BIN):$(OBJS) gcc $(OBJS) -o $(BIN) `pkg-config dbus-glib-1 --libs` clean: rm -rf *.o $(BIN) explain: @echo Source files---------- @echo $(SRCS) @echo Object files---------- @echo $(OBJS) @echo Binary files---------- @echo $(BIN) |
The main
function creates a GLib event
loop, gets a connection to the session bus, and integrates the D-BUS event
handling into the Glib event loop. Then it creates a one-second timer
that calls send_ping
, and starts the event
loop.
send_ping
constructs a new Ping signal,
coming from the object path /com/burtonini/dbus/ping and interface com.burtonini.dbus.Signal
.
Then the string "Ping!"
is added as an argument to the signal and sent across the bus. A
message is printed on standard output to let the user know a signal was
sent.
Of course, it is not good to fire signals down the bus if there is nothing listening to them... which brings us to:
#include #include #include static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data); int main (int argc, char **argv) { GMainLoop *loop; DBusConnection *bus; DBusError error; /* 1. Create a new event loop to run in */ loop = g_main_loop_new (NULL, FALSE); /* 2. Get a connection to the session bus */ dbus_error_init (&error); bus = dbus_bus_get (DBUS_BUS_SESSION, &error); if (!bus) { g_warning ("Failed to connect to the D-BUS daemon: %s", error.message); dbus_error_free (&error); return 1; } /* 3. Set up this connection to work in a GLib event loop */ dbus_connection_setup_with_g_main (bus, NULL); /* 4. listening to messages from all objects as no path is specified */ dbus_bus_add_match (bus, "type='signal',interface='com.burtonini.dbus.Signal'", NULL); dbus_connection_add_filter (bus, signal_filter, loop, NULL); /* 5. Start the event loop */ g_main_loop_run (loop); return 0; } static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { /* User data is the event loop we are running in */ GMainLoop *loop = user_data; /* A signal from the bus saying we are about to be disconnected */ if ( dbus_message_is_signal(message, "org.freedesktop.Local", "Disconnected") ) { /* Tell the main loop to quit */ g_main_loop_quit (loop); /* We have handled this message, don't pass it on */ return DBUS_HANDLER_RESULT_HANDLED; } /* A Ping signal on the com.burtonini.dbus.Signal interface */ else if (dbus_message_is_signal (message, "com.burtonini.dbus.Signal", "Ping")) { DBusError error; char *s; dbus_error_init (&error); if (dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) { g_print("Ping received: %s\n", s); // dbus_free (s); } else { g_print("Ping received, but error getting message: %s\n", error.message); dbus_error_free (&error); } return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } Makefile: BIN = dbus-listen SRCS=$(wildcard *.c) OBJS=$(patsubst %.c,%.o,$(SRCS)) #all:$(BIN) %.o:%.c gcc -g $< -o $@ -c `pkg-config dbus-glib-1 --cflags` $(BIN):$(OBJS) gcc $(OBJS) -o $(BIN) `pkg-config dbus-glib-1 --libs` clean: rm -rf *.o $(BIN) explain: @echo Source files---------- @echo $(SRCS) @echo Object files---------- @echo $(OBJS) @echo Binary files---------- @echo $(BIN) |
This program listens for the signals dbus-ping-send.c is emitting.
The main
function starts as before, creating a
connection to the bus. Then it states that it would like to be notified
when signals with the com.burtonini.dbus.Signal
interface are sent, sets signal_filter
as the
notification function, and enters the event loop.
signal_func
is called when a message that
meets the matches is sent. However, it will also receive bus management
signals from the bus itself. Deciding what to do when a message is
received is a simple case of examining the message header. If the message
is a bus disconnect signal, the event loop is terminated, as there is no
point in listening to a non-existent bus. (The bus is told that the
signal was handled). Next, the incoming message is compared to the
message we are expecting, and, if successful, the argument is extracted and
output. If the incoming message is neither of those, the bus is
told that we did not handle the message.
Those two examples used the low-level D-BUS library, which is complete but can be long-winded to use when you want to create services and many objects. This is where the higher-level bindings come in. There are C# and Python wrappers in development that present a programming interface far closer to the logical model of D-BUS. As an example, here is a more sophisticated reworking of the ping/listen example in Python. Because the Python bindings model the logical interface, it is not possible to send a signal without it coming from a service. So this example also creates a service:
#! /usr/bin/env python |
Most of the code is self-explanatory: a connection to the bus is
obtained, and the service com.burtonini.dbus.SignalService
is registered.
Then a minimal D-BUS object is created and every second a signal is
broadcast from the object. This code is clearer than the corresponding C
code
, but the Python bindings still need work. (For instance,
there is no way to add arguments to signals.)
#! /usr/bin/env python |
This code is more concise than the equivalent C code
in dbus-ping-listen.c and is easier to read. Again,
there are areas where the bindings still need work (when calling bus.add_signal_receiver
, the user must pass in an
interface and an object path; otherwise, malformed matchers are created).
This is a trivial bug, and once it is fixed, the service and object path
arguments could be removed, improving the readability of the code even
more.
D-BUS is a lightweight yet powerful remote procedure call system with minimal overhead costs for the applications that wish to use it. D-BUS is under active public development by a group of very experienced programmers. Acceptance of D-BUS by early adopters is rapid, so it appears to have a rosy future on the Linux desktop.