weston 3: wayland通信总结

概述

最近开始做一些weston相关的细节梳理,希望对大家有所帮助,如有错误或遗漏欢迎指正。

图中通信相关已更新完毕,内容持续更新中

(

        必备基础知识:

                epoll机制介绍: 深入理解 Linux 的 epoll 机制

                eventfd: Linux fd 系列 — eventfd 是什么?

)

本文针对下图中服务端流程①②③和客户端ⒶⒷⒸ进行阐述

(图的源文件可自行下载 gitee 地址 zwzfj2-drawio

wayland协议主要是基于epoll机制,主要逻辑如下:

1.服务端启动(其他流程会单独成章描述,此处只说通信相关)创建wl_socket,将创建的fd以EPOLLIN(可读)的触发方式放到epoll池内,等待新的连接产生,绑定的触发函数为socket_data

2.客户端启动,调用客户端wl_display_connect,创建与服务端的连接,此时触发服务端的socket_data函数。

3.服务端socket_data函数创建client_fd,将client_fd以EPOLLIN(可读)的触发方式放到epoll池内,等待客户端发送的数据,绑定的触发函数为wl_client_connection_data

4.接收到数据,服务端wl_client_connection_data函数触发后,通过一系列解析(下面细说),将数据还原,并通过wl_closure_invoke调用解析出的对应函数完成相应处理。

5.服务端发送数据,是通过wl_display_run调用的wl_connection_flush最终将已写入的数据发送。

6.客户端发送数据,是通过wl_display_dispatch调用的wl_connection_flush最终将已写入的数据发送。

7.客户端接收数据,通过wl_display_dispatch调用wl_display_dispatch_queue函数,最终调用到dispatch_event函数,通过一系列解析,将数据还原,并通过wl_closure_invoke调用解析出的对应函数完成相应处理。

8.如上述流程,完成了wayland的基本通信(发送数据的写入会以wl_display_get_registry的整个流程为例,详细描述)

① wl_display_create(创建服务端wl_display)

//wayland-server.c
struct wl_display {
	struct wl_event_loop *loop;
	bool run;

	uint32_t next_global_name;
	uint32_t serial;

	struct wl_list registry_resource_list;
	struct wl_list global_list;
	struct wl_list socket_list;
	struct wl_list client_list;
	struct wl_list protocol_loggers;

	struct wl_priv_signal destroy_signal;
	struct wl_priv_signal create_client_signal;

	struct wl_array additional_shm_formats;

	wl_display_global_filter_func_t global_filter;
	void *global_filter_data;

	int terminate_efd;
	struct wl_event_source *term_source;
};

1.创建epoll实例,并将epoll_fd赋给wl_display持有。

2.创建eventfd,并将eventfd赋给wl_display持有。

3.通过wl_event_loop_add_fd将eventfd以EPOLLIN(可读)的触发方式放到epoll池内。

4.初始化wl_display中的其他数据。

注:服务端和客户端都有struct wl_display,分别位于wayland-server.c和wayland-client.c,服务端wl_display为全局唯一,客户端wl_display比服务端低一个层级,和服务端wl_client一一对应(可在图中查看层级和对应关系)。

②weston_create_listening_socket

wl_display_add_socket函数中,创建socket fd,

s->source = wl_event_loop_add_fd(display->loop, s->fd,WL_EVENT_READABLE,
                     socket_data, display);//将fd以EPOLLIN(可读)的触发方式放到epoll池内,绑定函数为socket_data

WL_EXPORT struct wl_event_source *
wl_event_loop_add_fd(struct wl_event_loop *loop,
		     int fd, uint32_t mask,
		     wl_event_loop_fd_func_t func,
		     void *data)
{
	struct wl_event_source_fd *source;

	source = zalloc(sizeof *source);
	if (source == NULL)
		return NULL;

	source->base.interface = &fd_source_interface;
	source->base.fd = wl_os_dupfd_cloexec(fd, 0);
	source->func = func;
	source->fd = fd;

	return add_source(loop, &source->base, mask, data);
}
static struct wl_event_source *
add_source(struct wl_event_loop *loop,
	   struct wl_event_source *source, uint32_t mask, void *data)
{
	struct epoll_event ep;

	if (source->fd < 0) {
		free(source);
		return NULL;
	}

	source->loop = loop;
	source->data = data;
	wl_list_init(&source->link);

	memset(&ep, 0, sizeof ep);
	if (mask & WL_EVENT_READABLE)
		ep.events |= EPOLLIN;
	if (mask & WL_EVENT_WRITABLE)
		ep.events |= EPOLLOUT;
	ep.data.ptr = source;

	if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &ep) < 0) {
		close(source->fd);
		free(source);
		return NULL;
	}

	return source;
}
static int
wl_event_source_fd_dispatch(struct wl_event_source *source,
			    struct epoll_event *ep)
{
	struct wl_event_source_fd *fd_source = (struct wl_event_source_fd *) source;
	uint32_t mask;

	mask = 0;
	if (ep->events & EPOLLIN)
		mask |= WL_EVENT_READABLE;
	if (ep->events & EPOLLOUT)
		mask |= WL_EVENT_WRITABLE;
	if (ep->events & EPOLLHUP)
		mask |= WL_EVENT_HANGUP;
	if (ep->events & EPOLLERR)
		mask |= WL_EVENT_ERROR;

	return fd_source->func(fd_source->fd, mask, source->data);
}

struct wl_event_source_interface fd_source_interface = {
	wl_event_source_fd_dispatch,
};

注:此处将source(wl_event_source_fd *source)绑定到ep.data.ptr (epoll_event ep)最终会在函数被触发时调用,下面流程会讲解。

此处注意在函数传递中wl_event_source_fd和wl_event_source 首地址相同。

Ⓐwl_display_connect

fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0);  //创建socket fd

connect(fd, (struct sockaddr *) &addr, size) < 0  //连接

wl_display_connect_to_fd(int fd)

//此函数创建客户端wl_display,初始化各种数据

        -->display->connection = wl_connection_create(display->fd);//创建缓冲区  

③wl_display_run

先说③中的wl_event_loop_dispatch

while (true) {
		count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
		if (count >= 0)
			break; /* have events or timeout */
		else if (count < 0 && errno != EINTR && errno != EAGAIN)
			break; /* have error */

		if (use_timeout) {
			clock_gettime(CLOCK_MONOTONIC, &now);
			timeout = timespec_to_ms(timespec_sub(end, now));
			if (timeout <= 0) {
				/* too late */
				count = 0;
				break;
			}
		}
	}

当有客户端连接时,可以取出epoll,调用注册的函数( 上述步骤中source->func = func

func 为 socket_data)。

for (i = 0; i < count; i++) {
    source = ep[i].data.ptr;
	if (source->fd != -1)
		source->interface->dispatch(source, &ep[i]);
}

socket_data函数

client_fd = wl_os_accept_cloexec(fd, (struct sockaddr *) &name, &length);

        -->fd = accept(sockfd, addr, addrlen);//获取与客户端连接的socket fd

struct wl_client *client;

client = zalloc(sizeof *client);//创建wl_client

client->source = wl_event_loop_add_fd(display->loop, fd,WL_EVENT_READABLE,
                            wl_client_connection_data, client);//将fd以EPOLLIN(可读)的触发方式放到epoll池内,绑定函数为wl_client_connection_data

client->connection = wl_connection_create(fd);//创建缓冲区  

wl_list_insert(display->client_list.prev, &client->link);//将client插入到client_list

Ⓑwl_display_get_registry(重点,真正的第一次发送数据)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值