【surfaceflinger源码分析】surfaceflinger进程的消息驱动模型(二)

news2024/11/17 9:53:35

接着上篇文章中的两个新疑问:

  1. mEventTube是个什么玩意?
  2. MessageQueue::setEventConnection(…) 什么时候有谁调用的 ?

BitTube

mEventTube的类型为class BitTube,BitTube从字面上的意思来看bit管道,bit隧道,应该是一种传递消息的通道。很显然mEventTube的接收信息的一方是surfaceflinger中的class MessageQueue,那么它的发送方是谁呢 ?
先来看看class BitTube的定义

class BitTube : public Parcelable {
public:
    BitTube() = default;
    explicit BitTube(size_t bufsize);
    struct DefaultSizeType {};
    static constexpr DefaultSizeType DefaultSize{};
    explicit BitTube(DefaultSizeType);
    explicit BitTube(const Parcel& data);
    virtual ~BitTube() = default;

    status_t initCheck() const;
    int getFd() const;
    int getSendFd() const;
    base::unique_fd moveReceiveFd();
    void setReceiveFd(base::unique_fd&& receiveFd);
    void setSendFd(base::unique_fd&& sendFd);
    .........................;
private:
    void init(size_t rcvbuf, size_t sndbuf);
    ssize_t write(void const* vaddr, size_t size);
    ssize_t read(void* vaddr, size_t size);
    mutable base::unique_fd mSendFd;
    mutable base::unique_fd mReceiveFd;
    static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
    static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize);
};

从上面的定义来看,有个mSendFd和mReceiveFd,并且是通过接口设置进来的。在setEventConnection中的stealReceiveChannel有调用setReceiveFd和setSendFd。先看看setEventConnection是咋回事。

setEventConnection

void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
    if (mEventTube.getFd() >= 0) {
        //如果recieve fd存在,则将此fd从epoll中删除
        // epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr)
        mLooper->removeFd(mEventTube.getFd());
    }
    mEvents = connection;
    //看样子是要从connection将recieve fd偷过来
    mEvents->stealReceiveChannel(&mEventTube);
    //将偷过来的fd 添加到epoll中
    //epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem)
    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
                   this);
}

继续看看EventThreadConnection::stealReceiveChannel(…)

status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
    //将EventThreadConnection中名为mChannel的BitTube的receiveFd转移并设置到MessageQueue中的mEventTube
    outChannel->setReceiveFd(mChannel.moveReceiveFd());
    //将EventThreadConnection中名为mChannel的BitTube的sendFd复制一份并设置到MessageQueue中的mEventTube
    outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
    //设置完成之后,mChannel只能send消息,mEventTube既可以send又可以receive消息。
    return NO_ERROR;
}

有个疑问,mChannel的receiveFd和sendFd是哪里来的呢 ?
surfaceflinger的initScheduler函数中有调用setEventConnection,看看这个被设置进来的connection如何初始化receiveFd和sendFd

void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
    ........................;
    mScheduler =
            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
                                         *mRefreshRateConfigs, *this);
    mAppConnectionHandle =
            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
                                         impl::EventThread::InterceptVSyncsCallback());
    mSfConnectionHandle =
            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
                                         [this](nsecs_t timestamp) {
                                             mInterceptor->saveVSyncEvent(timestamp);
                                         });
    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
    ........................;
}

sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
    RETURN_IF_INVALID_HANDLE(handle, nullptr);
    return mConnections[handle].connection;
}
//接下来看看mSfConnectionHandle 是如何创建出来的 ?
Scheduler::ConnectionHandle Scheduler::createConnection(
        const char* connectionName, nsecs_t phaseOffsetNs,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
    //创建一个eventThread
    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                           std::move(interceptCallback));
    return createConnection(std::move(eventThread));
}

Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
    const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
    //创建connection
    auto connection =
            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
    //将connect插入到mConnections, 索引为handle
    //  std::unordered_map<ConnectionHandle, Connection> mConnections
    mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
    return handle;
}

sp<EventThreadConnection> Scheduler::createConnectionInternal(
        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
    return eventThread->createEventConnection([&] { resync(); }, configChanged);
}

sp<EventThreadConnection> EventThread::createEventConnection(
        ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
                                     configChanged);
}

EventThreadConnection::EventThreadConnection(EventThread* eventThread,
                                             ResyncCallback resyncCallback,
                                             ISurfaceComposer::ConfigChanged configChanged)
      : resyncCallback(std::move(resyncCallback)),
        mConfigChanged(configChanged),
        mEventThread(eventThread),
        mChannel(gui::BitTube::DefaultSize) {}
//上面mChannel(gui::BitTube::DefaultSize)会调用下面的构造函数, init函数中会创建fd
//而MessageQueue中的mEventTube使用默认构造函数,不会创建fd
BitTube::BitTube(size_t bufsize) {
    init(bufsize, bufsize);
}

void BitTube::init(size_t rcvbuf, size_t sndbuf) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
        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]);
        mSendFd.reset(sockets[1]);
    } else {
        mReceiveFd.reset();
        ALOGE("BitTube: pipe creation failed (%s)", strerror(errno));
    }
}

疑问解答

  1. mEventTube即BitTube是个什么玩意?
    BitTube创建一对相互连接好的socket,其中一个用于发消息,另一个用于接收消息。比如BitTube A初始化了一对socket,可以将发送或者接收fd转移到另一个BitTube B对象,因此BitTube A对象就可以和BitTube B通信了。很明显通信方式为单工。

  2. MessageQueue::setEventConnection(…) 什么时候有谁调用的 ?
    在Surfacelinger中的initScheduler函数中创建了EventThreadConnection,EventThreadConnection中的mChannel为BitTube,将EventThreadConnection通过函数MessageQueue::setEventConnection(…) 将mChannel的receiveFd转移到MessageQueue::mEventTube中。因此EventThreadConnection::mChannel发消息,MessageQueue::mEventTube接收消息。

SurfaceFlinger消息驱动图

SURFACEFLINGER_MESSAGE

  1. surfaceflinger主进程初始化完成后,由MessageQueue::waitMessage() 死循环处理消息,循环触发surfaceflinger合成显示等动作。
  2. Looper中处理两件事情:
    <1> surfaceflinger中会主动请求Vsync信号,并通过epoll监听Vsync事件,当Vsync事件到来后转成INVALIDATE消息,并由Handler处理此消息,推动下一次合成。
    <2> surfaceflinger中会主动调用refresh(), 即向Looper中send REFRESH消息,并由Handler处理此消息强制刷新画面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/350187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20230216在Ubuntu18.04下查看硬盘信息

20230216在Ubuntu18.04下查看硬盘信息 2023/2/16 22:25 百度搜索&#xff1a;查看 HDD 缓存 ubuntu [BEGIN] 2023/2/16 22:10:24 rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ cat /sys/block/sda/device/modelHUS726060ALE61…

VIVADO2022 sdk 工程创建流程

正点原子uart历程复现 create block design&#xff08;起名 为System&#xff09; -> open block design -> 号/IP catalog 中搜索zynq 双击打开&#xff0c; 将和pl相关的时钟都干掉 再auto 布线一下 把herarchy中的sources 中的system.bd右键、 无脑下一步导出 如…

赛狐ERP|亚马逊产品缺货怎么办?该如何补救?

由于物流时效的延长&#xff0c;运输成本的增加&#xff0c;亚马逊的仓储限制等各种原因&#xff0c;断货问题很常成为亚马逊卖家的普遍困扰。那么亚马逊产品缺货应该怎么办&#xff01;1、提高产品价格&#xff1a;除了卖自己的Listing此外&#xff0c;提高产品价格也是一种保…

基于微信小程序的智能招聘小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

Cygwin安装与Mingw

共同点&#xff1a;window下编译环境 区别&#xff1a;cygwin(gnu windows)模拟Linux编译环境&#xff0c; mingw模拟window编译环境&#xff0c;生成.exe可执行文件 目录 Cygwin安装 一、官网下载 二、双击安装 三、选择安装路径后&#xff0c;到连接方式如图 四、添加连…

USB3.0移动硬盘启动Win7的方法(AHCI/AMD USB3.0/Win7)

古董电脑(intel处理器&#xff0c;无USB3.0接口)突然坏了&#xff0c;已经没有维修价值了&#xff0c;硬盘还是完好的。欲把硬盘拆下来&#xff0c;装到USB3.0硬盘盒上&#xff0c;然后在新电脑(AMD R5-4650G/A520)上从USB3.0硬盘盒上启动。 一、需要工具 SATA数据线PS/2鼠标…

合并两个有序数组-力扣88-java

一、题目描述给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。注意&#xff1a;最终&#xff0c;合…

【mac】在m2 mbp上通过Parallels Desktop安装ubuntu22.04

文章目录前言一、参考文章二、版本信息三、方法1:通过ubuntu官网提供的iso安装3.1 配置服务器3.2 安装图形界面四、方法2:通过Parallels Desktop提供的安装包五、 小工具5.1 调整应用栏图标大小5.2 ubuntu获取mac的剪切板5.3 调整terminal字体大小5.4 安装samba5.5 ubuntu连接m…

【Java基础】022 -- Lambda与递归练习

目录 一、Lambda表达式 1、Lambda初体验 2、函数式编程 3、Lambda表达式的标准格式 4、小结 5、Lambda表达式的省略写法 ①、示例代码 ②、小结 6、Lambda表达式的练习 ①、Lambda表达式简化Comparator接口的匿名形式 二、综合练习 1、按照要求进行排序&#xff08…

四【Spring框架】

目录一 Spring概述二 .Spring 的体系结构三 Spring的开发环境3.1 配置pom.xml文件四 项目案例&#xff1a;4.1 创建实体类4.2 在pom.xml中引入依赖4.3 配置Spring-config.xml文件4.4 Test✅作者简介&#xff1a;Java-小白后端开发者 &#x1f96d;公认外号&#xff1a;球场上的…

Java kafka

JAVA面试题--Kafka&#xff08;最新最全&#xff09; 目录概述需求&#xff1a;设计思路实现思路分析1.URL管理2.网页下载器3.爬虫调度器4.网页解析器5.数据处理器拓展实现性能参数测试&#xff1a;参考资料和推荐阅读)Survive by day and develop by night. talk for import b…

Mac M1 使用Centos8➕VMware Fusion进行静态网络配置

大部分的流程网络上面都有当我们已经下载好mac m1版的Centos8链接: https://pan.baidu.com/s/1UTl4Lo-_c17s-PDj3dA6kA 提取码: 7xh2 和VMware Fusionhttps://www.vmware.com/cn/products/fusion.html之后就可以进行安装了在导入过后 记得将硬盘和内存都设置好了 记得在关机状态…

Flutter-Charts_painter大数据量绘制性能优化-数据收敛

Flutter-Charts_painter大数据量绘制性能优化-数据收敛 1、背景介绍 HRV测量仪器上传的数据&#xff0c;每秒有250个数据&#xff0c;业务上需要测量180秒&#xff0c;预计有3w-5w个数据点需要绘制到折线图上去。Charts_painter绘制这么大的数据是时候会有些卡顿&#xff0c;…

自定义markdown-loader

webpack markdown-loader 想要创建自己的makdown-loader 首先创建markdown文件 # 前端学习总结## 一、深入学习HTMLCSS* HTML常见特性## 二、深入学习javascript创建my-md-loader.js 我们需要先将markdown的语法转化成html标签语法 使用marked pnpm add marked loader返回…

“2023数据安全智能化中国行”活动,开幕即高能

工信部等16部门近日发布的《关于促进数据安全产业发展的指导意见》提出&#xff0c;到2025年&#xff0c;数据安全产业基础能力和综合实力明显增强&#xff0c;数据安全产业规模超过1500亿元&#xff0c;年复合增长率超过30%。到2035年&#xff0c;数据安全产业进入繁荣成熟期。…

MySQL JDBC 中 tinyint(1) 处理为Boolean 的代码逻辑

MySQL JDBC 中 tinyint(1) 类型&#xff0c;在查询时默认会被处理为 Boolean 类型。 参数配置 官方文档中提供了参数配置&#xff1a; 上图标记的两个参数可以控制如何处理 tinyint(1) 类型和 BIT 类型。 tinyInt1isBit 默认 true&#xff0c;因此会把 tinyint(1) 类型转换…

Python3 函数实例及演示

函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。 函数能提高应用的模块性&#xff0c;和代码的重复利用率。我们已经知道Python提供了许多内建函数&#xff0c;比如print()。但也可以自己创建函数&#xff0c;这被叫做用户…

JS 设计模式 - 怎么让你的代码提示一个档次

设计模式是我们在解决一些问题的时候 &#xff0c;针对特定的问题给出的简介并且优化的处理方案 这篇文章说提及到的 JavaScript 设计模式将围绕着封装方法类来展开叙述 构造器模式 构造器模式本质就是我们平常在编码中常用的封装方法&#xff0c;重复利用构造函数 // 这是…

72 优化算法【动手学深度学习v2】

72 优化算法【动手学深度学习v2】 深度学习学习笔记 学习视频&#xff1a;https://www.bilibili.com/video/BV1bP4y1p7Gq/?spm_id_from333.1007.top_right_bar_window_history.content.click&vd_source75dce036dc8244310435eaf03de4e330 优化问题 优化问题一般是最小化f…

怎么样可以查看系统的内存和显示您硬盘中文件和文件夹的分布情况——SpaceSniffer

一、找内存 &#xff08;1&#xff09;右击此电脑&#xff0c;点击属性&#xff0c;即可看到如下 &#xff08;2&#xff09;或者可以打开电脑桌面&#xff0c;右击任务栏&#xff0c;如下 然后打开任务管理器 二、搜索各个盘的文件 SpaceSniffer是一个可以显示您硬盘中文…