概述
最近开始做一些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