libhybris简介

原文地址:http://blog.csdn.net/jinzhuojun/article/details/41412587

libhybris主要作用是为了解决libc库的兼容问题,目的是为了在基于GNU C library的系统运行那些用bionic编译的库(主要是Android下的闭源HAL库)。它在Ubuntu touch, WebOS, Jolla Sailfish OS等系统中都有使用。因为这些系统都是基于glibc生态的,然而现有的硬件厂商提供的driver多是为Android而写的,自然也是用bionic编译的。那么问题来了,说服厂商再写一套驱动不是那么容易的,就算写出来也需要经过一段时间才能变得成熟。那如何让基于glibc的系统能够重用现有Android的driver呢?这就需要像libhybris这样的兼容层。

libhybris的源码可以从https://github.com/mer-hybris/libhybris.git下载。编译过程可参见《Sailfish OS Hardware Adaptation Development Kit Documentation》。它的一些主要目录如下:

compat // 一些核心模块的compatibility layer。它主要用在Ubuntu Touch中,和hybris目录下的相应目录一起,组成实现Android模块抽象接口的跳板库的两端。这下面的都是基于bionic编译的。

hybris // 主要功能实现。这下面的都是基于glibc编译的。

common // glibc/bionic的处理,包含一个自定义的Android版本相关的linker。

egl // EGL platform,这是一个backend无关的egl抽象。其中还包含若干个具体backend的实现。

glesv1/glesv2 // GLES库的wrapper

hardware // libhardware的wrapper。

sf/camera/ui/input // 跳板库的glibc端。它们会利用libhybris来打开compat目录下对应的bionic版本的跳板库。

libnfc_nxp/libnfc_ndef_nxp/vibrator // 利用libhybris打开基于bionic的HAL库。

utils //  Utility脚本 ,如抽取用于编译的Android的头文件等。

值得注意的是libhybris有好几个系统在用,每个系统用法还不太一样。代码的贡献者也分好几波。而这些代码都放在同个git中,所以阅读代码时还得留意下文件头。写着Canonical的一般是for Ubuntu Touch的。写着Jolla一般是for Sailfish OS的。写着Collabora一般是为了添加Wayland的支持。

总得来说,libhybris在解决libc库兼容性问题的基础上,还有Android模块抽象兼容层(主要是Ubuntu Touch在用)和EGL platform(主要是Sailfish OS在用)的部分。下面分这几个部分简单阐述。

1. Bionic/glibc兼容

前面提到过,libhybris的主要作用是libc的兼容,说白了就是把库中bionic的symbol替换成glibc中相应兼容的版本(当然对于其中一些会加些glue code)。它是主要工作原理是先为那些基于bionic编译的库写一个wrapper,当上层调用到这些库时,会首先load这个wrapper,这个wrapper就会使用一个自带的linker去load那些真正我们想load的库。这个自带的linker是一个基于Android中linker修改而成的版本,其中加入了对libc中symbol的特殊处理。下图是原理图。

图片来源: http://events.linuxfoundation.org/sites/events/files/slides/Ubuntu%20Touch%20Internals_1.pdf

我们知道libc和bionic在pthread, IPC, exception, STL, wchar等方面都是有差异的,两者并不完全兼容。这就意味着简单的symbol mapping不能解决所有问题。在libhybris,它是通过linker中维护的symbol映射hash表和在bionic中打一些patch来共同解决的。bionic中打的patch具体可参见《Sailfish OS Hardware Adaptation Development Kit Documentation》Release 1.0.2-EA2 中的12.2节或者mer-hybris的git。

对于一个基于bionic编译的库,其加载过程大致是这样的:首先wrapper库中会调用android_dlopen()来加载bionic编译的目标库。以gles库为例:

64 static void  __attribute__((constructor)) _init_androidglesv2()  {

65     _libglesv2 = (void *) android_dlopen(getenv("LIBGLESV2") ? getenv("LIBGLESV2") : "libGLESv2.so", RTLD_NOW);

66     GLES2_LOAD(glBlendColor);

其中GLES2_LOAD宏为:

50 #define GLES2_LOAD(sym)  { *(&_ ## sym) = (void *) android_dlsym(_libglesv2, #sym);  }

对于这个库中的每个函数,都会调用android_dlsym来找到其地址。看来神奇之处是android_dlopen()和android_dlsym(),它们的定义都在自带的linker中。先来看看android_dlopen():

android_dlopen()

find_library()

load_library() // 加载lib。

open_library()  // open, read, lseek, etc.

load_segments()

init_library() // 初始化lib。

link_image()

find_library() // 递归加载所需要的库。

reloc_library() // 处理symbol重定位。

call_constructors_recursive()

// 忽略bionic版本的libc。

其中的reloc_library()中做了libc中symbol的hook:

reloc_library()

get_hooked_symbol()

hooks_install()

hook_add() // 将 hybris/common/hooks.c中定义的hooks数组加入到hash表中(如没有加过),待查。

hook_find() // 在hash表中查找该symbol,找到会直接返回,找不到会将尝试将其添加到hash表中。

//白名单中的symbol会fallback到bionic中的版本。

hook_add_from_lib(sym, "librt.so") // 在librt.so中检查该symbol。

dlopen(),  dlsym()

hook_add() // 加到hash表中,待查。

hook_add_from_lib(sym, "libc.so.6") // 在libc.so.6中检查该symbol。

dlopen(), dlsym()

hook_add() // 加到hash表中,待查。

可以看到,这个包含symbol映射关系的hash表中包含了两部分:一部分是hooks这个数组中定义的映射。在这里面的多半是将symbol地址指向libhybris中实现的wrapper函数。wrapper中执行一些glue code来处理兼容性问题,然后调用到glibc版本的对应版本。 另一部分是bionic的symbol到libc.so.6和 librt.so中对应版本的直接映射。前者是静态定义的,后者是runtime生成的。

2. Android Abstraction Compatibility Layer

除了通过自带linker直接打开bionic的库这种方法外,还有种截在比较高层的方法就是在glibc和bionic世界通过跳板库来桥接。这种方法适用于有一个访问HAL的service,并且Client通过IPC申请服务的情况。下图是Ubuntu Touch早期的做法。它将Android作为Host,而Ubuntu作为Guest跑在Container里(2013.7后就倒过来了)。libhybris定义了一套Android模块抽象接口,用于Ubuntu世界(基于glibc)调用Android世界(基于bionic)的服务。

图片来源:http://sysmagazine.com/posts/180505/

可以看到,对于一个模块,有两个实现了模块抽象接口的跳板库。这对跳板库有两个对应的so组成(如文章前面提到的),一个是glibc的,一个是bionic的。前者会通过libhybris来load后者,两个世界就通过它们桥接起来了。然后bionic那个会通过IPC向service申请服务,而service可以是链接bionic的。通过这套机制,glibc世界就间接地使用了HAL层。

3. EGL Platform

除此之外,在Graphics方面,libhybris还实现了EGL platform,这是一套backend无关的遵循egl接口的图形平台,以及多个backend的实现(如fbdev, hwcomposer, wayland, null)。它们的共同接口是ws_module(其中有init_module(), CreateWindow(), eglQueryString(), finishSwap()等)。hybris/egl/platform目录下,每个backend实现都会有一个名为ws_module_info的函数指针数组,其中是ws_module这个接口的实现函数。因此,上层要使用backend首先要获得这个结构。几个backend目录如下:

common(libhybris-eglplatformcommon.so):各个backend通用的部分,如BaseNativeWindow,BaseNativeWindowBuffer等,还有Wayland的支持。

fbdev(eglplatform_fbdev.so):定义了FbDevNativeWindow,其中打开framebuffer HAL,通过fbPost()往FB上绘制。对于Wayland compositor来说,一般设成fbdev(如果直接操作framebuffer)或者下面的hwcomposer(如果用hwcomposer合成)。

null(eglplatform_null.so):通过android_createDisplaySurface()创建FramebufferNativeWindow,FramebufferNativeWindow是Android早期用于操作FB的类。

wayland(eglplatform_wayland.so):创建WaylandNativeWindow,其中finishSwap()时向Wayland compositor提交buffer。对于Client来说,如果想要用egl接口来向compositor请求绘制,设成wayland即可。

hwcomposer(eglplatform_hwcomposer.so):定义了HWComposerNativeWindow。真正用时,会创建其实现类HWComposer。调用eglSwapBuffers() -> queueBuffer()时会调用HWComposer的present()函数。最终调用hwcomposer HAL的prepare()和set()函数。如qt5-qpa-hwcomposer-plugin是hwcomposer backend的实现,源码位于https://github.com/mer-hybris/qt5-qpa-hwcomposer-plugin.git

hybris/egl/目录中为libEGL的实现,它是真正libEGL(基于bionic那个)的wrapper,它会load真正的libEGL库,同时也会hook某些函数,同时也会增加一些扩展函数。具体来说,上层对EGL接口的调用,分三种情况:

1. 对于大多数函数,由于它们是backend无关的,因此通过之前的android_dlopen(), android_dlsym()得到真实libEGL库中的地址,映射过去进行调用就行。

2. 对于几个关键函数,如eglCreateWindowSurface(),eglSwapBuffers()等,这些是backend相关的。需要对它们进行hook,加进glue code。以eglCreateWindowSurface()为例,首先会调用eglplatform的接口(ws_module)的wrapper函数(ws_CreateWindow())。这个wrapper首先会调用_init_ws()初始化真正的backend,具体基于哪个backend是在_init_ws()里根据环境变量HYBRIS_EGLPLATFORM或EGL_PLATFORM选择的。初始化后会调用相应backend的实现函数中(如wayland
backend的话是waylandws_CreateWindow())。在其中创建完本地窗口对象后,接着还是会调用真实的eglCreateWindowSurface()来把创建的本地窗口传给EGL层。

eglCreateWindowSurface(dpy, config, win, ...) // win的类型为wl_egl_window。

EGL_DLSYM(&_eglCreateWindowSurface, ...) // 找到真实的那个eglCreateWindowSurface()地址。

win = ws_CreateWindow(win, ...) // 为wl_egl_window创建本地窗口WaylandNativeWindow。

_init_ws()

ws = dlsym(wsmod, "ws_module_info"); // load backend-specifc module

ws->CreateWindow() // backend-specific implementation

(*_eglCreateWindowSurface)(win,...) // 调用真实的eglCreateWindowSurface()函数。

3. 另外,这个libEGL的wrapper还会增加一些额外的扩展函数,如eglBindWaylandDisplayWL()。它通过hook eglQueryString()和eglGetProcAddress()函数来实现。前者会返回EGL_WL_bind_wayland_display的字符串,表示支持该扩展。eglGetProcAddress()则会返回其地址:

eglGetProcAddress()

ws_eglGetProcAddress()

return ws->eglGetProcAddress(procname) // eglplatformcommon_eglGetProcAddress()

这个函数实现在eglplatformcommon.c,这里就可以加很多标准EGL不支持的函数,如eglBindWaylandDisplayWL()/eglUnbindWaylandDisplayWL()/eglQueryWaylandBufferWL()等。

由此,可以看到libhybris中的EGL platform结构大体如下:

下面以wayland backend为例,大体描述下客户端(simple-egl)通过EGL platform向Wayland compositor(Weston)提交渲染申请的过程。

首先,在Server端,会调用刚才提到的EGL扩展函数eglBindWaylandDisplayWL(),它会调用server_wlegl_create(),其作用是在Server端注册server_wlegl的global资源对象,该对象接口为android_wlegl_interface(其中包含了create_handle(), create_buffer()接口)。以后Client就可以通过Wayland扩展协议向Server端提交图形缓冲区,然后通过Wayland协议通知Compositor渲染新buffer。

Server端的初始化后,在Client端,先调用wl_compsitor_create_surface()通过Wayland协议创建窗口的代理对象wl_surface,然后调用wl_egl_window_create()创建硬件渲染窗口结构wl_egl_window,它在之后的eglCreateWindowSurface()中作为参数传入。在Wayland backend中,接着会调用waylandws_CreateWindow()。它创建WaylandNativeWindow,前面创建的wl_egl_window会设到这个WaylandNativeWindow的成员变量中。这个WaylandNativeWindow继承自ANativeWindow,因此它可以被传到真正的EGL库的eglCreateWindowSurface()中。可以看到,wl_egl_window是一个Wayland下硬件渲染窗口的抽象,它一头连着类型EGLNativeWindowType的WaylandNativeWindow与下层EGL连接,一头连着wl_surface与Wayland
compositor连接。

之后Client进行绘制,绘制完后调用eglSwapBuffers(),libhybris提供的libEGL wrapper中会将之转化为:

ws_prepareSwap()

waylandws_prepareSwap()

WaylandNativeWindow::prepareSwap()

(*_eglSwapBuffers)(); // 真实libEGL中的eglSwapBuffers()函数

WaylandNativeWindow::queueBuffer()

WaylandNativeWindow::dequeueBuffer()

ws_finishSwap()

waylandws_finishSwap()

WaylandNativeWindow::finishSwap()

wlbuffer_from_native_handle() // 通过Wayland协议向Wayland compositor传输图形缓冲区句柄。这里用到了前面在Server端创建的server_wlegl资源对象。

wl_surface_attach/damage/commit() // 通过Wayland协议向Wayland compositor提交更新buffer和重绘请求。

对于Wayland协议相关通信简介,可以参考此文(http://blog.csdn.net/jinzhuojun/article/details/40264449)。下图简单画了Client通过libhybris中的wayland backend来渲染的过程。

对于其它的backend,原理也是一样的,只是ANativeWindow的具体实现类不同而已。如hwcomposer backend的话,WaylandNativeWindow的位置就被替换为HWComposerNativeWindow,当queueBuffer()时,会调用其实现继承类(如test_hwcomposer或qt5-qpa-hwcomposer-plugin中的HWComposer)的present()函数将该帧交由hwcomposer处理。再如fbdev backend,用的就是FbDevNativeWindow,它在queueBuffer()时会调用framebuffer
HAL的post()将该帧放到FB中。null backend就更省事了,直接借用了Android中的FramebufferNativeWindow。

时间: 2024-11-09 09:38:16

libhybris简介的相关文章

Android网络通讯简介

网络通信应该包含三部分的内容:发送方.接收方.协议栈.发送方和接收方是参与通信的主体,协议栈是发送方和接收方进行通信的契约.按照服务类型,网络通信可分为面向连接和无连接的方式.面向连接是在通信前建立通信链路,而通信结束后释放该链路.无连接的方式则不需要在通信前建立通信连接,这种方式不保证传输的质量. Android提供了多种网络通信的方式,如Java中提供的网络编程,在Android中都提供了支持.Android中常用的网络编程方式如下: 针对TCP/IP协议的Socket和ServerSock

微信红包的架构设计简介

@来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微信红包的架构,于是乎有了下面的文字(有误请提出,谢谢) 概况:2014年微信红包使用数据库硬抗整个流量,2015年使用cache抗流量. 微信的金额什么时候算? 答:微信金额是拆的时候实时算出来,不是预先分配的,采用的是纯内存计算,不需要预算空间存储.. 采取实时计算金额的考虑:预算需要占存储,实时效率很高,预算才效率低. 实时性:为什么明明抢到红包,点开后发现没有? 答:2014年的红包一点开就知道金额,分两次操作

JSON 简介

ylbtech-JSON: JSON 简介 JSON:JavaScript Object Notation(JavaScript 对象表示法) JSON是存储和交换文本信息的语法,类似 XML. JSON 比 XML 更小.更快.更易解析. JSON 实例 { "employee":[ {"firstName":"John","lastName":"Doe"}, {"firstName"

Docker简介

Docker简介 什么是Docker: 正所谓Docker的英文本意为"搬运工",所以在我们的世界里,可以理解为Docker搬运的是装满任意类型的APP的集装箱,开发者可以通过Docker将APP变成一种标准化的.可移动植的.自动管理的组件.它用一种新的方式实现了轻量级的虚拟机,专业术语成为应用容器(Application Container) Docker的优势: 1.利用率高 ·Docker对系统资源的利用率很高,一台主机可以同时运行数千个Docker容器 2.可以快速的交付应用程

kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)

问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状态信息是如何保存的? 4.partitions设计的目的的根本原因是什么? 一.入门 1.简介 Kafka is a distributed,partitioned,replicated commit logservice.它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现.k

Quartz.NET简介及入门指南

Quartz.NET简介 Quartz.NET是一个功能完备的开源调度系统,从最小的应用到大规模的企业系统皆可适用. Quartz.NET是一个纯净的用C#语言编写的.NET类库,是对非常流行的JAVA开源调度框架 Quartz 的移植. 入门指南 本入门指南包括以下内容: 下载 Quartz.NET 安装 Quartz.NET 根据你的特定项目配置 Quartz 启动一个样例程序 下载和安装 你可以下载 zip 文件或使用 Nuget 程序包.Nuget 程序包只包含 Quartz.NET 运

ASP.Net简介、IIS服务器和Repeater重复器

简介:ASP.NET - 制作网站应用程序的技术 WebForm -出来时间比较早,敏捷.便捷开发,封装一些控件,慢慢发现一些控件做的挺好,真正使用没有那么敏捷 MVC -出来时间比较晚 什么东西? winform 界面 - 后台 - 数据库 共同组合出来的程序:ASP.NET 界面(HTML+CSS+JS) - 后台 - 数据库 运行机制:winform - 程序是安装在用户的电脑上,程序是运行在用户电脑上的.net Framework框架上的 ASP.NET - 通过浏览器向服务器发送请求,

CloudFoundry in 1 Box简介:PCF-Dev篇

在<CloudFoundry in 1 Box简介:Bosh-lite篇>我们介绍了Bosh-lite的架构和部署.在本篇中,我们将详细描述另一个CloudFoundry in 1 Box解决方案PCF-Dev. 1PCF-dev简介 PCF是Pivotal发行的Cloud Foundry商业版,PCF-Dev原名MicroPCF,是Pivotal为PCF的应用开发人员准备的一款App单虚拟机版的CloudFoundry.但是,麻雀虽小,五脏俱全.PCF-Dev虽然可以在仅仅一台虚拟式上即可运

1、elasticsearch简介

1.elasticsearch简介 中文帮助文档地址:http://es.xiaoleilu.com/ • Elasticsearch是一个基于Lucene的实时的分布式搜索和分析引擎.设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便.基于RESTful接口. – 普通请求是...get?a=1 – rest请求....get/a/1 • Elasticsearch的用户 – GitHub,Wikipedia,ebay等... • ES VS Solr – 接口 • 类似web