Android 12(S) 图像显示系统 - 基础知识之 BitTube
必读:
Android 12(S) 图像显示系统 - 开篇
一、基本概念
在Android显示子系统中,我们会看到有使用BitTube来进行跨进程数据传递。BitTube的实现很简洁,就是一对“parcel-able”模式的socket,用Linux/Unix中的专业术语就是socketpair。socketpair是Linux/Unix系统中用于进程间通信的一种机制,和pipe十分类似。
socketpair利用socket为双方建立了全双工的通信管道(communication pipe)。通过文件描述符的复用(dup/dup2),可以传递socket handle到另一个进程,复用它并开启通信。
BitTube使用了Linux/Unix socket中的顺序数据包(sequenced packets,SOCK_SEQPACKET),像SOCK_DGRAM,它只传送整包数据;又像SOCK_STREAM,面向连接且提供有序的数据报传送。
尽管socketpair是一个全双工的管道,但BitTube是按照单向方式使用它的:一端写入数据,另一端读出数据。收、发缓存默认限制为4KB大小。在BitTube中,提供了收发序列化对象的方法(sendObjects, recvObjects)。
二、源码解读
BitTube代码量很少,在(frameworks\native\libs\gui\BitTube.cpp)中,我们直接看它的几个重要的接口。
2.1 构造函数
[/frameworks/native/libs/gui/include/private/gui/BitTube.h]
BitTube() = default; // 默认构造函数,未初始化
explicit BitTube(size_t bufsize); // 创建指定发送、接收缓存大小的BitTube对象,creates a BitTube with a a specified send and receive buffer size
explicit BitTube(DefaultSizeType); // 默认缓存大小4KB,creates a BitTube with a default (4KB) send buffer
explicit BitTube(const Parcel& data); // 从Parcel中解析创建对象,用于跨进程传递该对象
BitTube提供了四个构造函数,用于不同的场景
[/frameworks/native/libs/gui/BitTube.cpp]
BitTube::BitTube(size_t bufsize) {
init(bufsize, bufsize); // 根据指定的buffer size,进行初始化
}
BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {}
BitTube::BitTube(const Parcel& data) {
readFromParcel(&data);
}
构造函数中最主要的还是调用了init方法进行初始化。
2.2 init初始化
init方法中就是去创建/配置sockect pair
[/frameworks/native/libs/gui/BitTube.cpp]
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { // 创建socket pair
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); // //对socketfd进行配置
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
// since we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK); //设置为非阻塞
fcntl(sockets[1], F_SETFL, O_NONBLOCK); //设置为非阻塞
mReceiveFd.reset(sockets[0]); //用于数据接收的socket handle
mSendFd.reset(sockets[1]); //用于数据发送的socket handle
} else {
mReceiveFd.reset();
ALOGE("BitTube: pipe creation failed (%s)", strerror(errno));
}
}
成员变量mReceiveFd,看起来是一个接收端,实际上这个fd也可以用来发送,同样mSendFd也可以用来接收,只是BitTube是按照单向方式使用它的:一端写入数据,另一端读出数据。
2.3 sendObjects 发送数据
先看其定义,sendObject实现为一个模板函数,sendObjects里调用的是write成员函数,write中调用send接口将数据写入mSendFd中。
[/frameworks/native/libs/gui/include/private/gui/BitTube.h]
// send objects (sized blobs). All objects are guaranteed to be written or the call fails.
template <typename T>
static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
return sendObjects(tube, events, count, sizeof(T));
}
发送成功则返回:发送的对象的个数
发送失败则返回:负数
[ /frameworks/native/libs/gui/BitTube.cpp]
ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
const char* vaddr = reinterpret_cast<const char*>(events);
ssize_t size = tube->write(vaddr, count * objSize);
...
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
ssize_t BitTube::write(void const* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); // 通过mSendFd,发送数据
// cannot return less than size, since we're using SOCK_SEQPACKET
err = len < 0 ? errno : 0;
} while (err == EINTR);
return err == 0 ? len : -err;
}
2.4 recvObjects 接收数据
先看其定义,recvObject实现为一个模板函数,recvObjects里调用的是read成员函数,read中调用rev接口将数据从mReceiveFd中读出。
接收成功则返回:接收的对象的个数
接收失败则返回:负数
[ /frameworks/native/libs/gui/include/private/gui/BitTube.h]
template <typename T>
static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
return recvObjects(tube, events, count, sizeof(T));
}
[/frameworks/native/libs/gui/BitTube.cpp]
ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
char* vaddr = reinterpret_cast<char*>(events);
ssize_t size = tube->read(vaddr, count * objSize);
...
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
ssize_t BitTube::read(void* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
err = len < 0 ? errno : 0;
} while (err == EINTR);
if (err == EAGAIN || err == EWOULDBLOCK) {
// EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the
// client should care about.
return 0;
}
return err == 0 ? len : -err;
}
2.5 writeToParcel & readFromParcel
writeToParcel & readFromParcel用于跨进程传递BitTube对象,进行序列化和反序列化,主要是传递mReceivedFd 和 mSendFd。
status_t BitTube::writeToParcel(Parcel* reply) const {
if (mReceiveFd < 0) return -EINVAL;
status_t result = reply->writeDupFileDescriptor(mReceiveFd); // mReceiveFd写入Parcel
mReceiveFd.reset();
if (result != NO_ERROR) {
return result;
}
result = reply->writeDupFileDescriptor(mSendFd);// mSendFd写入Parcel
mSendFd.reset();
return result;
}
status_t BitTube::readFromParcel(const Parcel* parcel) {
mReceiveFd.reset(dup(parcel->readFileDescriptor())); // 获取 mReceiveFd
if (mReceiveFd < 0) {
mReceiveFd.reset();
int error = errno;
ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
return -error;
}
mSendFd.reset(dup(parcel->readFileDescriptor())); // 获取 mSendFd
if (mSendFd < 0) {
mSendFd.reset();
int error = errno;
ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
return -error;
}
return NO_ERROR;
}
三、使用
关于如何使用BitTube实现跨进程的数据通信,提供一个简单的测试Demo:
https://github.com/yrzroger/BitTubeTest
在测试demo中,创建了一个BitTube对象,这样就建立了通信的 socketpair。
然后使用fork系统调用创建新的进程,来模拟跨进的通信中的不同进程(一个父进程,一个子进程)
父进程和子进程就可以使用BitTube对象的sendObjects方法发送数据,或使用recvObjects方法接收数据
Demo的主要代码如下:
struct Event {
int id;
int message;
};
int main()
{
gui::BitTube* dataChannel = new gui::BitTube(gui::BitTube::DefaultSize);
printf("\033[0mBitTube info: mReceiveFd=%d, mSendFd=%d\n", dataChannel->getFd(), dataChannel->getSendFd());
if(fork()) {
// 父进程发送数据
Event events[] = { {0, 888}, {1, 999} };
ssize_t size = gui::BitTube::sendObjects(dataChannel, events, 2);
if(size < 0)
printf("\033[32mprocess(%d) send failed, in parent process", getpid());
else
printf("\033[32mprocess(%d) send success, object size = %d\n", getpid(), size);
sleep(1);
// 父进程接收数据
size = gui::BitTube::recvObjects(dataChannel, events, 2);
if(size < 0) {
printf("\033[32mprocess(%d) receive failed, in child process", getpid());
}
else {
printf("\033[32mprocess(%d) receive success, object size = %d\n", getpid(), size);
for(int i = 0; i < size; ++i) {
printf("\033[32mprocess(%d): id=%d, message=%d\n", getpid(), events[i].id, events[i].message);
}
}
sleep(1);
} else {
// 子进程接收数据
Event events[2];
ssize_t size = gui::BitTube::recvObjects(dataChannel, events, 2);
if(size < 0) {
printf("\033[31mprocess(%d) receive failed, in child process", getpid());
}
else {
printf("\033[31mprocess(%d) receive success, object size = %d\n", getpid(), size);
for(int i = 0; i < size; ++i) {
printf("\033[31mprocess(%d): id=%d, message=%d\n", getpid(), events[i].id, events[i].message);
}
}
// 子进程发送数据
events[0].message+=1; events[1].message+=1;
size = gui::BitTube::sendObjects(dataChannel, events, 2);
if(size < 0)
printf("\033[31mprocess(%d) send failed, in parent process", getpid());
else
printf("\033[31mprocess(%d) send success, object size = %d\n", getpid(), size);
sleep(2);
}
delete dataChannel;
return 0;
}
放到Android源码下,执行mm,编译得到可执行档BitTubeTest,push到测试板/system/bin/目录下 执行BitTubeTest可以查看打印的结果:
绿色字体是父进程的打印(PID=12374),红色字体是子进程的答应(PID=12375)
在Android图像显示子系统中, /frameworks/native/libs/gui/DisplayEventReceiver.cpp 及 /frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp 中可以看到使用BitTube的身影。
BitTube用于建立跨进程传递数据的通道,主要是display evnets, 比如hotplug events , vsync events等等
至于具体的使用过程,在接下来的文章中我们会再详细介绍,,本篇就仅先讲解必要的基础知识。
Android 12(S) 图像显示系统 - 基础知识之 BitTube的更多相关文章
- Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)
必读: Android 12(S) 图像显示系统 - 开篇 1 前言 这一篇文章,将继续讲解有关VSync的知识,前一篇文章 Android 12(S) 图像显示系统 - SurfaceFlinger ...
- Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ...
- Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六)
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 为了提高Android系统的UI交互速度和操作的流畅度,在Android 4.1中,引入了Project Butter,即&quo ...
- Android 12(S) 图像显示系统 - SurfaceFlinger GPU合成/CLIENT合成方式 - 随笔1
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 SurfaceFlinger中的图层选择GPU合成(CLIENT合成方式)时,会把待合成的图层Layers通过renderengi ...
- Android 12(S) 图像显示系统 - HWC HAL 初始化与调用流程
必读: Android 12(S) 图像显示系统 - 开篇 接口定义 源码位置:/hardware/interfaces/graphics/composer/ 在源码目录下可以看到4个版本的HIDL ...
- Android 12(S) 图像显示系统 - drm_hwcomposer 简析(上)
必读: Android 12(S) 图像显示系统 - 开篇 前言 Android源码中有包含drm_hwcomposer:/external/drm_hwcomposer/ drm_hwcompose ...
- Android 12(S) 图像显示系统 - drm_hwcomposer 简析(下)
必读: Android 12(S) 图像显示系统 - 开篇 合成方式 合成类型的定义:/hardware/interfaces/graphics/composer/2.1/IComposerClien ...
- Android 12(S) 图形显示系统 - Surface 一点补充知识(十二)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 因为个人工作主要是Android多媒体播放的内容,在工作中查看源码或设计程序经常会遇到调用API: static inline i ...
- Android 12(S) 图形显示系统 - createSurface的流程(五)
题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...
随机推荐
- 【Java分享客栈】Java程序员为争一口气熬夜硬刚CSS实现掘金首页
前言 如果我做不了最厉害的Java工程师,那我就做Java工程师中最厉害的前端工程师. 前段时间,我默默给自己又喂了这碗心灵鸡汤-- 我不是很厉害的Java工程师,哪怕我已经工作八年,我依然觉得自己和 ...
- Azure DevOps (六) 通过FTP上传流水线制品到Linux服务器
上一篇我们实现了把流水线的制品保存到azure的流水线制品仓库里去,本篇我们会开始研究azure的发布流水线. 本篇要研究的是把流水线仓库的制品发布到任意一台公网的linux服务器上去,所以我们先研究 ...
- Docker容器入门介绍
1.前言 Docker是一种新兴的虚拟化技术,能够一定程度上的代替传统虚拟机.不过,Docker 跟传统的虚拟化方式相比具有众多的优势.Docker: 本意是码头工人,言外之意是集装箱: Java号称 ...
- [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
- springMVC的执行流程?
springMVC是由dispatchservlet为核心的分层控制框架.首先客户端发出一个请求web服务器解析请求url并去匹配dispatchservlet的映射url,如果匹配上就将这个请求放入 ...
- JavaScript的访问器
一.访问器属性: 1.Configurable:表示能否通过delete删除属性,从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性.对于直接在对象上定义的属性,这个特性的默认值为tr ...
- SpringBoot集成SpringBootDataElasticSearch
先放出依赖: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spri ...
- 学习MFS(二)
MooseFS,是一个具备冗余容错功能的分布式网络文件系统,它将数据分别存放在多个物理server或单独disk或partition上,确保一份数据有多个备份副本,对于访问MFS的client或use ...
- Quantum CSS,一个超快的CSS引擎
开始 本文翻译自Inside a super fast CSS engine: Quantum CSS,如果想要阅读原文,可以点击前往,以下内容夹杂本人一些思考,翻译也并不一定完全. 碎碎念 为什么翻 ...
- VueJs单页应用实现微信网页授权及微信分享功能
在实际开发中,无论是做PC端.WebApp端还是微信公众号等类型的项目的时候,或多或少都会涉及到微信相关的开发,最近公司项目要求实现微信网页授权,并获取微信用户基本信息的功能及微信分享的功能,现在总算 ...