APP下载

Wayland协议分析

2014-10-21刘琳

科技视界 2014年34期
关键词:协议

刘琳

【摘 要】Wayland是一个新兴的开源显示通信协议,用于替代X窗口系统。本文以Wayland的1.3.0版本作为基础,通过分析源代码,从而分析Wayland协议的具体实现。

【关键词】Wayland;Display;协议;Compositor

0 引言

Wayland是一款规定显示合成器与其客户端之间通信方式的协议,它最初由英特尔开源技术中心的雇员Kristian H?覬gsberg于2008年发起,用以取代X窗口系统。

在本文中,显示合成器、合成器以及服务端都是指提供显示合成、输出服务的一方;客户端是指提供显示数据的一方。

1 模型

Wayland源代码编译以后,会生成分别用于客户端和服务端使用的动态链接库libwayland-client和libwayland-server。

图1 客户端和服务端的关系

客户端通过libwayland-client和服务端进行通信,图形合成器通过libwayland-server和客户端进行通信。

libwayland-client 和libwayland-server 内部封装了 Wayland 通信协议。

Wayland协议是基于面向对象思想而设计的异步通信协议。

图2 客户端和服务端的关系

2 消息

客户端和服务端的控制信息是通过 unix domain socket 进行传输的。而图形数据则通过其他方式传输,比如共享内存技术。

socket 上的数据是以消息为单位进行传输的。从客户端发送给合成器消息叫做 request,从合成器发送给客户端的消息有 event 和 error 两种。

每一个消息可以理解为要求某一个对象进行特定动作,或者某一个对象主动通知状态发生变化。

图3 消息

3 消息格式

消息由消息头和消息体组成。并且整个消息必须4字节对齐。

消息头的长度固定为8个字节。前4个字节存放的是对象ID,紧接着的2个字节是操作码,最后2个字节存放的是整个消息的长度。

消息体长度不固定,但都由4字节对齐的参数组成。不同的操作码在Wayland协议里定义了不同的参数。

4 参数格式

消息体中的参数,Wayland也定义了相关格式。

表1 参数格式

参数标识是libwayland-client和libwayland-server中用于識别参数类型的标识符号。libwayland-client和libwayland-server通过读取协议数据常量部分的wl_message 中的signature 字符串,依次判断字符串中的字符是某一个参数标识,从而知道消息体中的数据类型。

而文件描述符是特殊的参数,通过socket传送文件描述符的时候,需要将文件描述符通过socket的优先数据区域才能发送。

5 数据缓冲区

无论是客户端还是服务端,当发送消息和接收消息的时候,都会首先将消息存放到数据缓冲区中进行缓存,借以减少IO的次数。

数据缓冲区是一个环形缓冲,当客户端明确需要进行发送或者buffer满的时候,才会进行发送动作。

图5 数据缓冲区

6 连接(connection)

连接用于描述物理通信层的信息,主要包括了输入缓冲区和输出缓冲区以及socket本身。

由于文件描述符的传输需要使用特殊的方法,需要为文件描述符建立专用的数据缓冲区,因此每一个连接包含4个数据缓冲区。2个用于输入,2个用于输出。

图6 connection

7 主要对象

由于Wayland 采用了面向对象的设计思想,客户端和服务端的控制信息都是基于对象的。

有一些对象希望所有的客户端都可以访问它,并且会广播这些对象,这样的对象叫做global。

Wayland 环境中,有一些对象只有一个实体,而另外一些对象,是会频繁创建和销毁的。

下面是最主要的几个对象:

表2 主要对象

8 接口(interface)

Wayland把对象提供的能力通过接口来进行描述,接口的类型就是消息的类型:request、event和error。

在Wayland项目里面,接口是通过xml文件来描述的。

下面是wayland.xml文件中描述 protocol、request和event的一部分摘抄,每一个部分将其描述已经参数的类型都描述得十分清楚。

The core global object.  This is a special singleton object.

It is used for internal Wayland protocol features.

The sync request asks the server to emit the 'done' event

on the returned wl_callback object.  Since requests are

handled in-order and events are delivered in-order, this can

used as a barrier to ensure all previous requests and the

resulting events have been handled.

The object returned by this request will be destroyed by the

compositor after the callback is fired and as such the client

must not attempt to use it after that point.

The error event is sent out when a fatal (non-recoverable)

error has occurred.  The object_id argument is the object

where the error occurred, most often in response to a request

to that object.  The code identifies the error and is defined

by the object interface.  As such, each interface defines its

own set of error codes.  The message is an brief description

of the error, for (debugging) convenience.

9 代碼自动生成

wayland-scanner是Wayland项目的一个小工具,用于将描述接口的xml文件翻译成C语言的源文件和头文件。

接口的使用分为客户端和合成器端,因此生成了2个头文件,一个用于客户端,一个用于服务端。

而源文件主要包含了接口的C语言实现,无论客户端或者是合成器端,都是使用同一个。

Wayland已经实现了:

客户端、将C语言接口调用的数据做成消息并发送。

服务端、将接收到的消息转化成C语言的函数调用。

因此对于开发者而言,只需要写好接口描述xml文档,而不用关注接口的C语言封装。

图7 代码自动生成

下面是使用wayland-scanner将wayland.xml解析完成后生成的C语言源代码的部分摘抄,能够和接口部分对应起来。

static const struct wl_message wl_display_requests[] = {

{ "sync", "n", types + 8 },

{ "get_registry", "n", types + 9 },

};

static const struct wl_message wl_display_events[] = {

{ "error", "ous", types + 0 },

{ "delete_id", "u", types + 0 },

};

WL_EXPORT const struct wl_interface wl_display_interface = {

"wl_display", 1,

2, wl_display_requests,

2, wl_display_events,

};

static inline struct wl_callback *

wl_display_sync(struct wl_display *wl_display)

{

struct wl_proxy *callback;

callback = wl_proxy_create((struct wl_proxy *) wl_display,

&wl_callback_interface);

if (!callback)

return NULL;

wl_proxy_marshal((struct wl_proxy *) wl_display,

WL_DISPLAY_SYNC, callback);

return (struct wl_callback *) callback;

}

struct wl_display_interface {

/**

* sync - asynchronous roundtrip

* @callback: (none)

*

*The sync request asks the server to emit the 'done' event on

* the returned wl_callback object. Since requests are handled

* in-order and events are delivered in-order, this can used as a

* barrier to ensure all previous requests and the

* resulting events  have been handled.

*

* The object returned by this request will be destroyed by the

* compositor after the callback is fired and as such the client

* must not attempt to use it after that point.

*/

void (*sync)(struct wl_client *client,

struct wl_resource *resource,

uint32_t callback);

/**

* get_registry - get global registry object

* @callback: (none)

*

* This request creates a registry object that allows the client

* to list and bind the global objects available from the

* compositor.

*/

void (*get_registry)(struct wl_client *client,

struct wl_resource *resource,

uint32_t callback);

};

10 通信日志

客戶端或者合成器端通过 export WAYLAND_DEBUG=1,可以将客户端和合成器端的消息通信日志输出到当前终端。

在日志中能够看到实时的通信交互情况,有利于分析通信协议。

下面是截取的一段通信日志, 符号"->"表示发送,没有这个符号表示接收。@+数字表示对象ID。

-> wl_display@1.get_registry(new id wl_registry@2)

-> wl_display@1.sync(new id wl_callback@3)

wl_display@1.delete_id(3)

wl_registry@2.global(1, "wl_display", 1)

wl_registry@2.global(2, "wl_compositor", 3)

-> wl_registry@2.bind(2, "wl_compositor", 1, new id [unknown]@4)

wl_registry@2.global(3, "wl_subcompositor", 1)

wl_registry@2.global(4, "wl_scaler", 2)

wl_registry@2.global(5, "wl_text_input_manager", 1)

wl_registry@2.global(6, "wl_data_device_manager", 1)

wl_registry@2.global(7, "wl_shm", 1)

-> wl_registry@2.bind(7, "wl_shm", 1, new id [unknown]@5)

wl_registry@2.global(8, "wl_drm", 2)

wl_registry@2.global(9, "wl_seat", 3)

wl_registry@2.global(10, "wl_input_method", 1)

wl_registry@2.global(11, "wl_output", 2)

11 结束语

Wayland协议以其高效简洁的设计,赢得越来越多第三方开发人员以及软件的支持。本文通过对Wayland协议进行概要的介绍,使读者可以更了解Wayland的具体实现,从而添加更符合自己需求的自定义扩展协议。相信Wayland项目将来会对开源图形系统产生更加深远的影响。

【参考文献】

[1]http://wayland.freedesktop.org/docs/html/[OL].

[2]http://en.wikipedia.org/wiki/Wayland_%28display_server_protocol%29[OL].

[责任编辑:杨玉洁]

猜你喜欢

协议
基于数字化变电站SV报文通信可靠性问题研究
基于物联网技术的多电机监控系统的设计