![v2-32265432f9ac167fe52abaaeb660f518_1440w.jpg?source=172ae18b](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-32265432f9ac167fe52abaaeb660f518_1440w.jpg?source=172ae18b)
FIDL(Fuchsia Interface Definition Language)是fuchsia系统上的远程调用机制, 相当于android上的基于binder的AIDL。
fuchsia里很多设计和android,chrome os有相似性, 可以类比学习。
FIDL的主要任务是允许客户端用不同的语言实现 和 服务端交互,底层数据传递时依赖zircon的IPC机制channel。并且通过自动生成代码来简化开发工作。
![v2-da466b7c86f763f7b160629ca33b9976_b.jpg](http://img-03.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-da466b7c86f763f7b160629ca33b9976_b.jpg)
它包含四个部分:
- FIDL的数据格式,远程函数的调用涉及两个进程之间的参数传递和返回值接收,参数和返回值可以使基本数据类型也可以是对象,所以怎么序列化和反序列化
- FIDL语言: 编写.fidl文件,定义远程接口,包括 接口的函数名,传输的参数和返回值。
- FIDL编译器:对fidl文件进行编译,生成代码
- FIDL binding:运行时的库,提供FIDL的操作API。支持不同的编程语言有c++/go/rust
我们开发者需要掌握的是 编写FIDL语言保存为.fidl文件,以及熟悉FIDL binding使用
阅读源码中的FIDL实例
参考官方文档 FIDL tutorials,以及源码树里实例, 源码位置比较散,最开始看有点晕。
examples/fidl目录下有部分代码
![v2-471d98d5c329bb57447c8aa7d56fe474_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-471d98d5c329bb57447c8aa7d56fe474_b.jpg)
garnet/examples/fidl下也有相关代码,还涉及到不同语言,不同层次的fidl使用
![v2-ed239cfc7206d69d8a55381c90cd274c_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-ed239cfc7206d69d8a55381c90cd274c_b.jpg)
把这部分所有代码都浏览一遍,最终以garnetexamplesfidl下的 echo为例分析.
实例代码分析
包括编译脚本BUILD.gn,fidl文件,客户端,服务端实例代码. 分别位于
- garnetexamplesfidlservicesecho.test.fidl : fidl文件,定义远程调用的接口函数EchoString,参数是string,返回值是string
![v2-b645ed3484204768a4e8debee746c126_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-b645ed3484204768a4e8debee746c126_b.jpg)
- garnetexamplesfidlecho_client_cpp: 客户端代码
int
- 其中EchoClientApp封装
class
- garnetexamplesfidlecho_server_cpp: 服务端代码
int
- 编译脚本BUILD.gn
fidl的编译garnetexamplesfidlservicesBUILD.gn
import("//build/fidl/fidl.gni")
fidl("echo") {
# TODO(fxb/35879): Remove lint exclusions by fixing known FIDL lint violations in this target
# 免去lint检查中 string参数长度限制
excluded_checks = [ "string-bounds-not-specified" ]
name = "fidl.examples.echo"
sources = [ "echo.test.fidl" ]
fuzzers = [
{
protocol = "fidl.examples.echo.Echo"
},
]
}
service的编译以及注释,文件在garnetexamplesfidlecho_service_cpp
import("//build/test/test_package.gni")
import("//build/testing/environments.gni")
source_set("lib") { #定义lib模块
sources = [ # 源文件
"echo_server_app.cc",
"echo_server_app.h",
]
public_deps = [ # 依赖sdk中的fidl和sys模块, 以及自定义的garnetexamplesfidlservicesecho.test.fidl
"//garnet/examples/fidl/services:echo",
"//sdk/lib/fidl/cpp",
"//sdk/lib/sys/cpp",
]
}
executable("bin") { #可执行文件
output_name = "echo_server_cpp"
# 源文件
sources = [ "echo_server.cc" ]
# 依赖的系统库 和 上面定义的lib模块
deps = [
":lib",
"//zircon/public/lib/async-default",
"//zircon/public/lib/async-loop-cpp",
"//zircon/public/lib/async-loop-default",
]
}
package("echo_server_cpp") {# 包
deps = [ ":bin" ]# 依赖上面的可执行文件bin
binaries = [# 可执行文件
{
name = "echo_server_cpp"
dest = "echo_server"
},
]
meta = [# 运行时配置文件
{
path = rebase_path("../meta/echo_server.cmx")
dest = "echo_server_cpp.cmx"
},
]
}
- 客户端的编译脚本在文件在garnetexamplesfidlecho_client_cpp
import("//build/package.gni")
source_set("lib") {#定义lib库
sources = [
"echo_client_app.cc",
"echo_client_app.h",
]
public_deps = [
# 依赖于自定义的fild文件garnetexamplesfidlservicesecho.test.fidl, 以及sdk中fidl和sys模块
"//garnet/examples/fidl/services:echo",
"//sdk/lib/fidl/cpp",
"//sdk/lib/sys/cpp",
]
}
executable("bin") { # 定义要编译的二进制
output_name = "echo_client_cpp"
sources = [ "echo_client.cc" ] # 源文件
deps = [ # 依赖于本文件中的lib库, 自定义的fild文件echo.test.fidl,以及sdk中的fidl,sys模块,以及zircon中的异步事件库
":lib",
"//garnet/examples/fidl/services:echo",
"//sdk/lib/fidl/cpp",
"//sdk/lib/sys/cpp",
"//zircon/public/lib/async-default",
"//zircon/public/lib/async-loop-cpp",
"//zircon/public/lib/async-loop-default",
]
}
package("echo_client_cpp") { #定义包
deps = [ ":bin" ] # 依赖于上面的bin
binary = "echo_client_cpp" # 可执行文件
meta = [ # 运行时配置文件
{
path = rebase_path("../meta/echo_client.cmx")
dest = "echo_client_cpp.cmx"
},
]
}
编译执行
在源码树里用 fx 设置编译选项添加这两个包。 --with后面接的相对路径, 冒号后面是要编的包名
set core.x64 --with //garnet/examples/fidl/echo_client_cpp:echo_client_cpp --with //garnet/examples/fidl/echo_server_cpp:echo_server_cpp
rm -f log.txt
![v2-49ad0143591a7f42e4b1570fb71d6b4d_b.png](http://img-02.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-49ad0143591a7f42e4b1570fb71d6b4d_b.png)
![v2-ce9a101c664adad74bedd30bd9111f42_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-ce9a101c664adad74bedd30bd9111f42_b.jpg)
运行
首先在fuchsia的shell里查 两个包的url
![v2-f5244d1531dbe5bffb0c32793e339157_b.png](http://img-02.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-f5244d1531dbe5bffb0c32793e339157_b.png)
然后运行client,用run命令 接参数 url
![v2-24e25e65b06c54b974efb1a7f8a2c4de_b.png](http://img-03.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-24e25e65b06c54b974efb1a7f8a2c4de_b.png)
不需要手动启动服务端,因为客户端去连接服务时,系统会把服务启动起来. 用 fx log看下log
![v2-9378cb5621fa6707a0ecbab92a6fdca5_b.png](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-9378cb5621fa6707a0ecbab92a6fdca5_b.png)
手动实现一个FIDL实例
光看不练假把式, 我们手动自己实现一个FIDL实例.
为了方便,直接复用exampleshello_word这个工程.
编写fidl文件
在hello_world目录下编写如下的call.fidl文件
![v2-54e2dff315f122f790f9cfd0511d2f4f_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-54e2dff315f122f790f9cfd0511d2f4f_b.jpg)
FIDL支持四种模式的通信
- blocking call: client端发送给server端请求,并且阻塞等待server端的回复. 上面的CallString实现了这种类型.
- fire and forget: client发送给server端请求,不要求回复。上面的SendString是这种类型.
- callback: client端发送给server端请求,但是不阻塞等待,server会有异步的回复消息,上面的CallString有实现这种类型,后面实例会讲
- event: cleint不做任何请求,server端会主动通知过来.OnReceiveString是这种类型
我遇到的坑
- fidl开头一定要加Copyright,按照规则写,否则编译不过
- namespace要指定,并且以fuchsia开头,namespace不能超过3层
编译fidl
直接修改exampleshello_word下的BUILD.gn, 添加一个fidl模块,写法也是仿照示例
(
编写服务端
编写一个实现Call.fidl里定义的Call的类
class
服务端主函数
int
编写客户端
构建脚本BUILD.gn
我把client,server端,fidl的编译都写到一个BUILD.gn文件里.
# Copyright 2019 The Fuchsia Authors. All rights reserved.
编译执行
编译
set core.x64 --with //examples/hello_world/cpp:hello_world_cpp
fx build
运行,先用locate查包的url
![v2-c35fd4832d4c50cd8ca890f5ed36358c_b.png](http://img-01.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-c35fd4832d4c50cd8ca890f5ed36358c_b.png)
分别起两个窗口运行
![v2-42faaeb5fe12d4b0fda789b8a6aab61a_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=08589d07-1730-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-42faaeb5fe12d4b0fda789b8a6aab61a_b.jpg)
客户端 收到服务端的回复,通过pinntf打印出来,但是奇怪的是server端的printf输出没有打印到terminal,而是到log里。
printf输出到log的原因分析
怀疑是标准输出流stdout被重定向到 log,应该是async事件库干的,先留个坑,以后在分析。