SurfaceFlinger简单理解

本文会从SurfaceFlinger启动和画面显示流程2个方面简单分析理解。

init进程启动SurfaceFlinger的大致流程如下:


https://www.jianshu.com/p/3f3f8a5bbc6b

一、SurfaceFlinger启动

1.SurfaceFlinger编译。

首先来看frameworks/native/services/surfaceflinger/Android.bp文件

// https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/native/services/surfaceflinger/Android.bp?r=f824b538
cc_binary {
    name: "surfaceflinger",
    defaults: ["libsurfaceflinger_binary"],
    init_rc: ["surfaceflinger.rc"],
    srcs: [
        ":surfaceflinger_binary_sources",
        // Note: SurfaceFlingerFactory is not in the filegroup so that it
        // can be easily replaced.
        "SurfaceFlingerFactory.cpp",
    ],
    shared_libs: [
        "libSurfaceFlingerProp",
    ],

     logtags: ["EventLog/EventLogTags.logtags"],
}

cc_binary表示编译成可执行的二进制文件surfaceflinger

可执行的二进制文件在Linux中,shell中,我们手动执行方式为输入./xxx,在Linux中要如何调用呢?实际用到的是Linux中的execv函数

Android.bp中的init_rc对应的是早期(android8及以前的版本)的Android.mk文件中定义的LOCAL_INIT_RC
build/make/core/base_rules.mk中会使用该宏,

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/build/make/core/base_rules.mk?r=9bbb60e1
ifndef LOCAL_IS_HOST_MODULE
# Rule to install the module's companion init.rc.
ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)
my_init_rc := $(LOCAL_FULL_INIT_RC)
else
my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))
endif
ifneq ($(strip $(my_init_rc)),)
# Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
# have init.rc files that need to be installed alongside them. Manually handle the case where the
# output file is in the recovery partition.
my_init_rc_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))
my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))

# Make sure we only set up the copy rules once, even if another arch variant
# shares a common LOCAL_INIT_RC.
my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))
ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)

rc文件目录规范:
/system/etc/init/ 用于核心系统功能,例如SurfaceFlinger、MediaService和logd等。
/vendor/etc/init/ 用于芯片供应商的一些核心操作或者守护进程功能。
/odm/etc/init/ 用于设备制造厂商的功能,比如传感器和其他外围设备所需的操作或者守护进程。
可参考https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/system/core/init/ 中的README

Android编译完成surfaceflinger这个模块后,会把surfaceflinger.rc
放到编译生成的out目录中的/system/etc/init/目录下。

2.解析对应的rc文件,启动surfaceflinger

系统启动的时候,system/core/init/init.cpp里面的LoadBootScripts函数会解析rc文件。

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/system/core/init/init.cpp?r=cedb057e
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

frameworks/native/services/surfaceflinger/surfaceflinger.rc定义如下。

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/native/services/surfaceflinger/surfaceflinger.rc?r=60f3ab27
service surfaceflinger /system/bin/surfaceflinger
    class core animation
    user system
    group graphics drmrpc readproc
    capabilities SYS_NICE
    onrestart restart zygote
    task_profiles HighPerformance
    socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
    socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
    socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0

service表示以服务的形式来启动进程,surfaceflinger是进程名字,/system/bin/surfaceflinger是可执行程序的路径。
class core,表示 surfaceflinger属于core服务,后面加个animation表示surfaceflinger服务将bootanim启动(surfaceflinger最后会启动StartPropertySetThread从而启动bootanim)。

init.rc中有一句

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/system/core/rootdir/init.rc?r=03067a21
class_start core

class_start core这样一条命令被执行,就会启动类型为core的所有service。

inti.rc被init_parser.cpp 解析后,init进程的main()函数中会通过调用action_for_each_trigger()函数来把需要执行的"action"加入到执行列表的action_queue中,进入while()循环依次取出执行队列action_queue中的command执行。

https://blog.csdn.net/wwwlyj123321/article/details/122514593

https://blog.csdn.net/wwwlyj123321/article/details/122514593

走到action_manager.cpp中。

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/raw/system/core/init/action_manager.cpp?r=52c8422e
void ActionManager::ExecuteOneCommand() {
    {
        auto lock = std::lock_guard{event_queue_lock_};
        // Loop through the event queue until we have an action to execute
        while (current_executing_actions_.empty() && !event_queue_.empty()) {
            for (const auto& action : actions_) {
                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
                               event_queue_.front())) {
                    current_executing_actions_.emplace(action.get());
                }
            }
            event_queue_.pop();
        }
    }

    if (current_executing_actions_.empty()) {
        return;
    }

    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
                  << ":" << action->line() << ")";
    }

    action->ExecuteOneCommand(current_command_);

    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0;
        if (action->oneshot()) {
            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
                           actions_.end());
        }
    }
}

然后走到action.cpp

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/raw/system/core/init/action.cpp?r=ce44c8ac
void Action::ExecuteOneCommand(std::size_t command) const {
    // We need a copy here since some Command execution may result in
    // changing commands_ vector by importing .rc files through parser
    Command cmd = commands_[command];
    ExecuteCommand(cmd);
}

void Action::ExecuteAllCommands() const {
    for (const auto& c : commands_) {
        ExecuteCommand(c);
    }
}

void Action::ExecuteCommand(const Command& command) const {
    android::base::Timer t;
    auto result = command.InvokeFunc(subcontext_);
    auto duration = t.duration();

    // Any action longer than 50ms will be warned to user as slow operation
    if (!result.has_value() || duration > 50ms ||
        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
        std::string trigger_name = BuildTriggersString();
        std::string cmd_str = command.BuildCommandString();

        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                  << ":" << command.line() << ") took " << duration.count() << "ms and "
                  << (result.ok() ? "succeeded" : "failed: " + result.error().message());
    }
}

Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
    if (subcontext) {
        if (execute_in_subcontext_) {
            return subcontext->Execute(args_);
        }

        auto expanded_args = subcontext->ExpandArgs(args_);
        if (!expanded_args.ok()) {
            return expanded_args.error();
        }
        return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
    }

    return RunBuiltinFunction(func_, args_, kInitContext);
}


Result<void> RunBuiltinFunction(const BuiltinFunction& function,
                                const std::vector<std::string>& args, const std::string& context) {
    auto builtin_arguments = BuiltinArguments(context);

    builtin_arguments.args.resize(args.size());
    builtin_arguments.args[0] = args[0];
    for (std::size_t i = 1; i < args.size(); ++i) {
        auto expanded_arg = ExpandProps(args[i]);
        if (!expanded_arg.ok()) {
            return expanded_arg.error();
        }
        builtin_arguments.args[i] = std::move(*expanded_arg);
    }

    return function(builtin_arguments);
}

这里从ExecuteOneCommand->InvokeFunc->RunBuiltinFunction->BuiltinFunction

builtins.h中定义了BuiltinFunction

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/raw/system/core/init/builtins.h?r=ce44c8ac
namespace android {
namespace init {

using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;

struct BuiltinFunctionMapValue {
    bool run_in_subcontext;
    BuiltinFunction function;
};

using BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>;

const BuiltinFunctionMap& GetBuiltinFunctionMap();

extern std::vector<std::string> late_import_paths;

}  // namespace init
}  // namespace android

这里使用了using,指定别名。

using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
using的写法把别名的名字强制分离到了左边,而把别名指向的放在了右边,比较清晰。

C++11以后在标准库里引入了std::function模板类,这个模板概括了函数指针的概念。函数指针只能指向一个函数,而std::function对象可以代表任何可以调用的对象,比如说任何可以被当作函数一样调用的对象。std::function的实例可以存储、复制和调用任何可调用对象。

也就是,这个函数的作用就是把builtin_arguments对象存储后返回。
Linux中的builtin:运行一个builtin shell,向它传递参数,并获取它的退出状态。
builtin命令用于执行指定的bash内建命令,builtin命令调用的bash内建命令优先于同名的外部命令及同名的shell函数。 返回该内建命令执行的返回值,除非传递的不是bash内建命令或该内建命令被禁用。
Action中的function,只是返回了需要执行的命令对象,不是真正执行的地方。
builtins.cpp中的do_start会调用service.cpp中的Start函数

https://android-opengrok.bangnimang.net/android-12.0.0_r3/raw/system/core/init/builtins.cpp?r=cedb057e
static Result<void> do_start(const BuiltinArguments& args) {
    Service* svc = ServiceList::GetInstance().FindService(args[1]);
    if (!svc) return Error() << "service " << args[1] << " not found";
    if (auto result = svc->Start(); !result.ok()) {
        return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
    }
    return {};
}

system/core/init/service.cpp中的ExpandArgsAndExecv

//https://android-opengrok.bangnimang.net/android-12.0.0_r3/raw/system/core/init/service.cpp?r=f3fea377
Result<void> Service::Start() {
....
  if (!ExpandArgsAndExecv(args_, sigstop_)) {
            PLOG(ERROR) << "cannot execv('" << args_[0]
                        << "'). See the 'Debugging init' section of init's README.md for tips";
        }

....
}

static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;

    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
        auto expanded_arg = ExpandProps(args[i]);
        if (!expanded_arg.ok()) {
            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
        }
        expanded_args[i] = *expanded_arg;
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    if (sigstop) {
        kill(getpid(), SIGSTOP);
    }

    return execv(c_strings[0], c_strings.data()) == 0;
}

service.cpp中的ExpandArgsAndExecv将args解析一下,然后调用execv执行。这里才是真正执行builtin命令的地方。

通过Linux执行execve函数来执行二进制可执行文件surfaceflinger(前面通过bp文件定义cc_binary编译的),从而启动了surfaceflinger这个service

Action执行的路径:


https://zhuanlan.zhihu.com/p/100189006

再简单一点理解就是,init进程解析init.rcsurfaceflinger.rc,然后执行/system/bin/surfaceflinger,从而启动了surfaceflinger服务进程。

执行ps -A | grep surfaceflinger,查看进程。存在对应信息,表示surfaceflinger已经启动。

二、SurfaceFlinger画面显示流程

https://www.jianshu.com/p/657336b545bd
surfaceflinger服务的main函数入口在main_surfaceflinger,主要操作有:
启动Hidl服务,主要是DisplayService
启动线程池
初始化SurfaceFlinger
将SurfaceFlinger和GpuService注册到ServiceManager
启动SurfaceFlinger线程

HIDL在Android10中已经废弃,功能整合到AIDL中了。


https://source.android.google.cn/docs/core/architecture?hl=zh-cn#hidl

https://source.android.google.cn/docs/core/graphics
官方文档中定义:
无论开发者使用什么渲染 API,一切内容都会渲染到 Surface 上。Surface 表示缓冲区队列中的生产方,而缓冲区队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕。
图像流消耗方
图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到屏幕。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。
SurfaceFlinger 接受缓冲区,对它们进行合成,然后发送到屏幕。WindowManager 为 SurfaceFlinger 提供缓冲区和窗口元数据,而 SurfaceFlinger 可使用这些信息将 Surface 合成到屏幕。

就类似这种:


绘图坐标映射到窗口坐标

AndroidO之前版本下的显示实现框架如下图所示:


https://blog.csdn.net/yangwen123/article/details/80221427

那么在AndroidO及以后版本,随着HAL进程独立化,显示系统实现框架也有所变化,如下图所示:

https://blog.csdn.net/yangwen123/article/details/80221427
https://blog.csdn.net/yangwen123/article/details/80221427

应用程序可以通过Skia来绘制2D图形,也可以用OpenGL来绘制3D图形,SurfaceFlinger通过OpenGL来混合图形到指定的Surface上,然后这个Surface与其他没有被SurfaceFlinger合成的图层一起送往HWC进行合成。

  1. Surface:上层图形绘制的画板,Canvas是画笔,上层通过调用Canvas的API向Surface上绘制图形(产生图元)。Surface内部存在多个缓冲区,形成一个BufferQueue

  2. SurfaceFlingerSurfaceFlinger消费了ApplicationSurfaceFlinger相对Application是消费者)生成的图元(基本图元有点、线、多边形等,图元就是指图形数据),通过HWC Service合成Layer, HWC Service把合成的数据交给DRM去输出完成app画面显示到屏幕上。

Layers and Displays

官方文档定义:
Layers and displays are two primitives that represent composition work and interactions with the display hardware.

出处:https://www.jianshu.com/p/f96ab6646ae3
APP绘画的画布是由SurfaceFlinger提供的,而画布是一块共享内存,APP向SurfaceFlinger申请到画布,是将共享内存的地址映射到自身进程空间。 App负责在画布上作画,画完的作品提交给SurfaceFlinger, 这个提交操作并不是把内存复制一份给SurfaceFlinger,而是把共享内存的控制权交还给SurfaceFlinger, SurfaceFlinger把拿来的多个应用的共享内存再送给HWC Service去合成, HWC Service把合成的数据交给DRM去输出完成app画面显示到屏幕上的过程。

DRM深入可参考这里。
DRM(Direct Rendering Manager)学习简介

APP的画面要显示到屏幕上大致上要经过如下图所示系统组件的处理:


https://www.jianshu.com/p/dcaf1eeddeb1

图元从Application产生,到SurfaceFlinger消费,关键点在BufferQueue的理解。
这一系列文章架构讲得非常详细,而且代码很少,大部分代码用省略号代替,阅读很方便,不会迷失在代码中。
Android画面显示流程分析(3)
另外,SurfaceFlinger中的SW Vsync模型,也是理解SurfaceFlinger工作原理中的重要一环。
Android SurfaceFlinger SW Vsync模型

而关于详细的代码跳转,可参考这一系列。
Android 重学系列 SurfaceFlinger的概述

重学系列的代码很详细,但是容易迷失,要记得抓住主线,重点,带着问题去看才行,看到后面,我都看晕了,因为涉及到的东西太多了。后面捋顺了再写一篇SurfaceFlinger代码流程分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容