首页 文章 精选 留言 我的

精选列表

搜索[AI安全分析],共10000篇文章
优秀的个人博客,低调大师

深入理解Spark:核心思想与源码分析. 3.13 创建DAGSchedulerSource和BlockManagerSource

3.13 创建DAGSchedulerSource和BlockManagerSource 在创建DAGSchedulerSource、BlockManagerSource之前首先调用taskScheduler的post-StartHook方法,其目的是为了等待backend就绪,见代码清单3-53。postStartHook的实现见代码清单3-54。 创建DAGSchedulerSource和BlockManagerSource的过程类似于ExecutorSource,只不过DAGSchedulerSource测量的信息是stage. failedStages、stage. runningStages、stage. waiting-Stages、stage. allJobs、stage. activeJobs,BlockManager

优秀的个人博客,低调大师

深入理解Spark:核心思想与源码分析. 2.4 Spark基本架构

2.4 Spark基本架构 从集群部署的角度来看,Spark集群由以下部分组成: Cluster Manager:Spark的集群管理器,主要负责资源的分配与管理。集群管理器分配的资源属于一级分配,它将各个Worker上的内存、CPU等资源分配给应用程序,但是并不负责对Executor的资源分配。目前,Standalone、YARN、Mesos、EC2等都可以作为Spark的集群管理器。 Worker:Spark的工作节点。对Spark应用程序来说,由集群管理器分配得到资源的Worker节点主要负责以下工作:创建Executor,将资源和任务进一步分配给Executor,同步资源信息给Cluster Manager。 Executor:执行计算任务的一线进程。主要负责任务的执行以及与Worker、Driver App的信息同步。 Drive

优秀的个人博客,低调大师

深入理解Spark:核心思想与源码分析. 2.3 Spark基本设计思想

2.3 Spark基本设计思想 2.3.1 Spark模块设计 整个Spark主要由以下模块组成: Spark Core:Spark的核心功能实现,包括:SparkContext的初始化(Driver Application通过SparkContext提交)、部署模式、存储体系、任务提交与执行、计算引擎等。 Spark SQL:提供SQL处理能力,便于熟悉关系型数据库操作的工程师进行交互查询。此外,还为熟悉Hadoop的用户提供Hive SQL处理能力。 Spark Streaming:提供流式计算处理能力,目前支持Kafka、Flume、Twitter、MQTT、ZeroMQ、Kinesis和简单的TCP套接字等数据源。此外,还提供窗口操作。 GraphX:提供图计算处理能力,支持分布式,Pregel提供的API可以解决图计算中的常见问题。

优秀的个人博客,低调大师

《深入分析GCC 》——第1章 GCC概述1.1 GCC的产生与发展

第1章 GCC概述本章主要对GCC的发展过程及GCC的特点进行简介,并给出了本书的主要内容简介。 1.1 GCC的产生与发展 GCC(GNU Compiler Collection)是GNU工程(GNU Project)中的核心工具软件,其官方网址为https://gcc.gnu.org/。GCC支持多种前端的编程语言,包括C、C++、Java、Ada和Fortran等,其编译生成的目标代码可以在几乎所有的处理器平台上运行,是目前使用最广泛的编译系统之一。GCC遵循GNU GPL(GNU Public License)协议,由FSF(Free Software Foundation)发布。GNU和GCC的图标如图1-1所示。 初期的GCC仅仅作为C语言的编译器,即GNU C Compiler。1987年GCC 1.0发布,同年12月,GC

优秀的个人博客,低调大师

storm1.0节点间消息传递过久分析及调优

序:最近对storm平台系统进行性能检测发现偶尔会出现oncebolt向另一个twobolt发送数据后,twobolt要500毫秒后才接收到进行处理。这里简单说增大twobolt的并行度即可解决,但是究其内部原因是因为storm的通信机制所导致的问题。 先介绍背景:一个拓扑的结构,spout(并行度:1)[处理性能:capacity 0.04],oncebolt(并行度:20)[处理性能:capacity 0.2],twobolt(并行度:100)[处理性能:capacity 0.6];整个拓扑就我预估最大的处理量就是一秒一千条。 原文和作者一起讨论:http://www.cnblogs.com/intsmaze/p/6544017.htmll 微信:intsmaze 避免微信回复重复咨询问题,技术咨询请博客留言。 最近对系统进行性能检测,统计整个storm系统中一条消息处理中各个IO耗时的时间,找出性能瓶颈。发现除了活动匹配中会有分布式锁以及大量的redis的IO操作,导致最多会耗时30ms,以及从Hbase中查询数据时由于hbase集群当时正在跑任务导致耗时1~2s。唯一出现的问题就是onebolt向twobolt发送数据后,某些数据耗时几百毫秒才会被twobolt接收到。这就引起了我的注意。先上一下伪代码: public class OnceBolt extends BaseRichBolt{ private static final long serialVersionUID = -5283595260540124273L; private OutputCollector collector; public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; } public void execute(Tuple input) {long intsmazeTime=System.currentTimeMillis(); collector.emit(input,new Values(intsmazeTime)); } public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("intsmaze")); } } public class TwoBolt extends BaseRichBolt{ private static final long serialVersionUID = -5283595260540124273L; private OutputCollector collector; public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; } public void execute(Tuple input) {long intsmazeTime=input.getLong(0); System.out.println("耗时:"+(System.currentTimeMillis()-intsmazeTime)); } public void declareOutputFields(OutputFieldsDeclarer declarer) { } } 这个问题从storm内部通信来说: 每个executor有自己的接收队列和输出队列。 每个worker进程有一个独立的接收线程将外部发送过来的消息移动到对应的executor线程的接收队列中。 每个worker存在一个独立的发送线程负责从worker的传输队列中读取消息,并通过网络发送给其他worker。 每个executor有单独的线程分别来处理spout/bolt的业务逻辑,业务逻辑输出的中间数据会存放在输出队列中,当executor的输出队列中的tuple达到一定的阀值,executor的发送线程将批量获取输出队列中的tuple,并发送到work中的传输队列中。 因为oncebolt任务向自己的发送队列生产过快,且向twobolt任务的接收队列发送数据过多,导致twobolt的接收队列满了,twobolt处理不过来了。[简单说就是oncebolt生产数据的速度快于twobolt的消费速率]。这个时候就会出现twobolt处理一个oncebolt的消息要几百毫秒。这个情况是因为twobolt的处理一条消息平均要50毫秒,twobolt接收队列长度是10,刚好twobolt在从队列拉取一条消息处理时,twobolt的接收队列满了,这个时候队列中第10条消息等被处理就会阻塞10*50毫秒的。 同时因为接收队列满了,oncebolt就会阻塞到,等twobolt接收队列有空了再去发送(很多文章说会导致消息丢失,但是我测试发现没有这种情况,只会阻塞到,这种就是流量洪峰下,storm会出现的一种情况)。这种情况是某几秒消息量过大导致产生,所以这种情况只是偶尔发送,过一会就会正常了,但是如果交易量一直很大,这个时候我们就要进行调优了,最简单的就是增大twobolt的并行度以及work数量。 个人认为的最优并行度设置:我们可以参照每一个节点的capacity的性能指标,比如我们这里spout的指标是0.04所以就不需要再增加它的并行度和kafka的分区保持一致。oncebolt的指标是0.2,而twobolt的指标是0.6。很明显是oncebolt资源被浪费了或者twobolt的速率跟不上oncebolt,我们给oncebolt的并行度可以减少一半,比如10个。这种方式是减少资源的浪费。或者就目前的问题,增大twobolt的并行度来提示消费的速度。 还有一个问题我说一下:storm的性能提升我们是增加work数量还是增加节点的并行度。 这个是一个调优的过程,如果我们只启动一个work,一昧的在这个work中增加并行度,这样会导致频繁的full GC,因为一个work的2G资源供所有的任务一起用;或者我们启动10个work,每个work只启动一个任务,先不说浪费资源,首先在任务间传递消息时就一定会走网络通信这也是速率的消耗。所以是一句话,一个work中的任务数量要合理,不要太多,也不要太少,这是一个调优的过程。 作者: intsmaze(刘洋) 出处: http://www.cnblogs.com/intsmaze/ 老铁,你的--->推荐,--->关注,--->评论--->是我继续写作的动力。 微信公众号号:Apache技术研究院 由于博主能力有限,文中可能存在描述不正确,欢迎指正、补充! 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

优秀的个人博客,低调大师

iOS系统分析(二)Mach-O二进制文件解析

原文出自【听云技术博客】:http://blog.tingyun.com/web/article/detail/1341 0x01 Mach-O格式简单介绍 Mach-O文件格式是 OS X 与 iOS 系统上的可执行文件格式,类似于windows的 PE 文件 与 Linux(其他 Unix like)的 ELF 文件,如果不彻底搞清楚Mach-O的格式与相关内容,那么深入研究 xnu 内核就无从谈起。 Mach-O文件的格式如下图所示: 有如下几个部分组成: 1. Header:保存了Mach-O的一些基本信息,包括了平台、文件类型、LoadCommands的个数等等。 2. LoadCommands:这一段紧跟Header,加载Mach-O文件时会使用这里的数据来确定内存的分布。 3. Data:每一个segment的具体数据都保存在这里,这里包含了具体的代码、数据等等。 0x02 FAT二进制数据 ,数据结构定义在 \<mach-o/fat.h\> 1. 第一段为magic 魔数,这里注意大小端,读出来之后需要看下是0xCAFEBABE还是 0xBEBAFECA(否则即为thin),需要根据这个来转后续读取的字节的字节序。 可以看出来 前4byte 为 0xBEBAFECA ,说明为fat。 2. 第二段为arch count,也就是该App或dSYM中包含哪些CPU架构,比如armv7、arm64等,这个例子中为2(后4byte 0x 00 00 00 02),表示包含了两种cpu架构。 `sizeof(structfat-header)=8byte` 3. 后续段中包含cputype(0x 0C 00 00 01)、cpusubtype (0x 00 00 00 00)、offset (0x 00 10 00 00)、size(0x 00 F0 27 00)等数据,根据fat中的结构定义,依次读取,这里需要说明的是,如果只包含一种CPU架构的话,是没有这段fat头定义的,可以跳过这部分,直接读取Arch数据。 `sizeof(structfat-arch)=20byte` 4. 根据fat头中读取的offset数据,我们可以跳到文件对应的arch数据的位置,当然如果只有一种架构的话就不需要计算偏移量了。 下图给出解析的函数 0x03 Mach Header二进制数据 通过magic我们可以区分出是32-bit还是64-bit,64-bit多了4个字节的保留字段,这里同样需要注意字节序的问题,也就是判断magic,来确定是否需要转换字节序。 `sizeof(structmach-header-64)=32byte`;`sizeof(structmach-header)=28byte` 根据mach-header与mach-header_64的定义,很明显可以看出,Headers的主要作用就是帮助系统迅速的定位Mach-O文件的运行环境,文件类型。 FileType 因为Mach-O文件不仅仅用来实现可执行文件,同时还用来实现了其他内容 1. 内核扩展 2. 库文件 3. CoreDump 4. 其它 下面是一些精彩用到的文件类型 1. MH-OBJECT 编译过程中产生的 obj文件 (gcc -c xxx.c 生成xxx.o文件) 2. MH-EXECUTABLE 可执行二进制文件 (/usr/bin/ls) 3. MH-CORE CoreDump (崩溃时的Dump文件) 4. MH-DYLIB 动态库(/usr/lib/里面的那些共享库文件) 5. MH-DYLINKER 连接器linker(/usr/lib/dyld文件) 6. MH-KEXT-BUNDLE 内核扩展文件 (自己开发的简单内核模块) flags Mach-O headers还包含了一些很重要的dyld的加载参数。 1. MH-NOUNDEFS 目标没有未定义的符号,不存在链接依赖 2. MH-DYLDLINK 该目标文件是dyld的输入文件,无法被再次的静态链接 3. MH-PIE 允许随机的地址空间(开启ASLR -\>Address Space Layout Randomization) 4. MH-ALLOW-STACK-EXECUTION 栈内存可执行代码,一般是默认关闭的。 5. MH-NO-HEAP-EXECUTION 堆内存无法执行代码 0x04 LoadCommands Load Commands 直接就跟在Header后面,所有command占用内存的总和在Mach-O Header里面已经给出了。在加载过Header之后就是通过解析LoadCommand来加载接下来的数据了。定义如下: cmd字段 根据cmd字段的类型不同,使用了不同的函数来加载。简单的列出一张表看一看在内核代码中不同的command类型都有哪些作用。 1. LC-SEGMENT;LC-SEGMENT-64 在内核中由load-segment 函数处理(将segment中的数据加载并映射到进程的内存空间去) 2. LC-LOAD-DYLINKER 在内核中由load-dylinker 函数处理(调用/usr/lib/dyld程序) 3. LC-UUID 在内核中由load-uuid 函数处理 (加载128-bit的唯一ID) 4. LC-THREAD 在内核中由load-thread 函数处理 (开启一个MACH线程,但是不分配栈空间) 5. LC-UNIXTHREAD 在内核中由load-unixthread 函数处理 (开启一个UNIX posix线程) 6. LC-CODE-SIGNATURE 在内核中由load-code-signature 函数处理 (进行数字签名) 7. LC-ENCRYPTION-INFO 在内核中由 set-code-unprotect 函数处理 (加密二进制文件) UUID 二进制数据 128byte UUID是16个字节(128bit)的一段数据,是文件的唯一标识,前面提到的符号化时,这个UUID必须要和App二进制文件中的UUID一致,才能被正确的符号化。dwarfdump查看的UUID就是这段数据。读取这部分数据时通过Command结构读取的,也就是第一段(0x0000001B)表示接下来的数据类型,第二段(0x00000018)数据的大小(包含Command数据)。 SymTab 二进制数据 1. 符号表数据块结构,前二段依然是Command数据。后边4段分别为符号在文件中的偏移量(0x001DF5E0)、符号个数(0x001DF5E0)、字符串在文件中的偏移量(0x0020C3A0)、字符串表大小(0x000729A8)。 2. 接下来就是读取Segment和Section数据块了,和上面读取数据块结构一样是根据Command结构读取,下图展示的Segment数据和Section数据,它们在二进制文件中它们是连续的,也就是每一条Segment数据后面会紧跟着多条对应的Section数据,Section的数据总数是通过Segment结构中的nsects决定的。 3. 这里我写了一个简单地Mach-O解析工具[https://github.com/liutianshx2012/Tmacho](https://github.com/liutianshx2012/Tmacho) Segment数据 加载数据时,主要加载的就是LC-SEGMET活着LC-SEGMENT_64。其他的Segment的用途在这里不做深究。 LCSEGMENT以及LC-SEGMENT-64 定义如下图。 可以看出,这里大部分的数据是用来帮助内核将Segment映射到虚拟内存的。 nsects 字段,标示了Segment中有多少secetion ,section是具体有用的数据存放的地方。 TEXT的vmaddr也就是程序的加载地址; —DWARF中表明了DWARF数据块的信息,表示dSYM是DWARF格式的数据结构。 `sizeof(structsegment-command)=56byte;sizeof(structsegment-command-64)=72byte` Section数据 从Section数据中,我们可以找到—debug-info、—debug-pubnames, —debug-line等调试信息,通过这些调试信息我们可以找到程序中符号的起始地址、变量类型等信息。如果我们要符号化的话,就可以通过解析这些数据得到我们想要的信息。 Symbol 数据 通过SymTab中的数据可以得到Symbol在文件中的位置和个数,Symbol块数据中包含了符号的起始地址、字符串的偏移量等数据,这部分数据结构可以参考\<nlist.h\> 和 \<stabl.h\>。在这部分数据全部读取后,就可以读取所有的符号数据了,也就是接下来的数据。 Symbol String 数据 1. 通过SymTab和Symbo中的数据可以得到每个符号字符串在文件中的偏移量和大小,每个符号数据是以0结尾的字符串。 2. 我们通过以上两部分数据的组合就可以得到每个symbo在程序中的加载地址了。这些数据对于以后做符号工作都非常的有帮助。 3. 到此,关于dSYM文件中头部数据读取就完成了。头部数据都有相应的数据结构定义,读取时相对会比较容易些,解析数据时要注意字节序的问题,32-bit和64-bit数据结构的差异、字节长度的差异,DWARF版本的差异,每个数据块之间都是紧密联系的,一个字节的读取偏差就会造成后续数据的读取错误,正所谓差之毫厘,失之千里。

优秀的个人博客,低调大师

android emulator虚拟设备分析第五篇之pipe上的opengles

一、概述 据说qemu的gpu的实现,运行起来非常慢。所以android emulator提供了一种use host gpu的方式,guest os可以使用host机器的opengl库去画图,速度快很多。 guest os把画图的命令通过pipe传递给emulator(encode, send via pipe, decode),然后emulator将opengles的画图命令转为opengl的画图命令(translate),并执行。 二、opengles —— pipe上的另一个service 老规矩,看文档,opengles是使用tcp实现的,tcp也是pipe service ANDROID-QEMU-PIPE.TXT:306 opengles Connects to the OpenGL ES emulation process. For now, the implementation is equivalent to tcp:22468, but this may change in the future. 代码是hw-pipe-net.c,需要先看第二篇pipe的实现 初始化代码如下,初始化了opengles,tcp和unix三种 static const GoldfishPipeFuncs openglesPipe_funcs = { openglesPipe_init, netPipe_closeFromGuest, netPipe_sendBuffers, netPipe_recvBuffers, netPipe_poll, netPipe_wakeOn, NULL, /* we can't save these */ NULL, /* we can't load these */ }; void android_net_pipes_init(void) { Looper* looper = looper_newCore(); goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs ); #ifndef _WIN32 goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs ); #endif goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs ); } int android_init_opengles_pipes(void) { /* TODO: Check that we can load and initialize the host emulation * libraries, and return -1 in case of error. */ _opengles_init = 1; return 0; } openglesPipe_init在guest往/dev/qemu_pipe里面写"opengles"后,由pipeConnector_sendBuffers函数调用,返回一个NetPipe,和CHANNEL一一对应的 netPipe_initUnix ornetPipe_initTcp建立了opengles pipe service和emulator中画图服务端的socket连接 static void* openglesPipe_init( void* hwpipe, void* _looper, const char* args ) { NetPipe *pipe; if (!_opengles_init) { /* This should never happen, unless there is a bug in the * emulator's initialization, or the system image. */ D("Trying to open the OpenGLES pipe without GPU emulation!"); return NULL; } char server_addr[PATH_MAX]; android_gles_server_path(server_addr, sizeof(server_addr)); #ifndef _WIN32 if (android_gles_fast_pipes) { pipe = (NetPipe *)netPipe_initUnix(hwpipe, _looper, server_addr); D("Creating Unix OpenGLES pipe for GPU emulation: %s", server_addr); } else { #else /* _WIN32 */ { #endif /* Connect through TCP as a fallback */ pipe = (NetPipe *)netPipe_initTcp(hwpipe, _looper, server_addr); D("Creating TCP OpenGLES pipe for GPU emulation!"); } if (pipe != NULL) { // Disable TCP nagle algorithm to improve throughput of small packets socket_set_nodelay(pipe->io->fd); // On Win32, adjust buffer sizes #ifdef _WIN32 { int sndbuf = 128 * 1024; int len = sizeof(sndbuf); if (setsockopt(pipe->io->fd, SOL_SOCKET, SO_SNDBUF, (char*)&sndbuf, len) == SOCKET_ERROR) { D("Failed to set SO_SNDBUF to %d error=0x%x\n", sndbuf, WSAGetLastError()); } } #endif /* _WIN32 */ } return pipe; } netPipe_initUnix ornetPipe_initTcp都会调用到netPipe_initFromAddress函数,里面的loopIo_init和asyncConnector_init需要注意一下,大概意思是有个公用的循环,使用LoopIo把fd包装起来,循环里面会检查哪些fd可读,或者可写,并且调用callback函数netPipe_io_func void* netPipe_initFromAddress( void* hwpipe, const SockAddress* address, Looper* looper ) { NetPipe* pipe; ANEW0(pipe); pipe->hwpipe = hwpipe; pipe->state = STATE_INIT; { AsyncStatus status; int fd = socket_create( sock_address_get_family(address), SOCKET_STREAM ); if (fd < 0) { D("%s: Could create socket from address family!", __FUNCTION__); netPipe_free(pipe); return NULL; } loopIo_init(pipe->io, looper, fd, netPipe_io_func, pipe); status = asyncConnector_init(pipe->connector, address, pipe->io); pipe->state = STATE_CONNECTING; if (status == ASYNC_ERROR) { D("%s: Could not connect to socket: %s", __FUNCTION__, errno_str); netPipe_free(pipe); return NULL; } if (status == ASYNC_COMPLETE) { pipe->state = STATE_CONNECTED; netPipe_resetState(pipe); } } return pipe; } netPipe_io_func是刚才那个callback函数,就是用来唤醒等待读写的线程的,如果未连接,那么会先连接一下(connect) /* This is the function that gets called each time there is an asynchronous * event on the network pipe. */ static void netPipe_io_func( void* opaque, int fd, unsigned events ) { NetPipe* pipe = opaque; int wakeFlags = 0; /* Run the connector if we are in the CONNECTING state */ /* TODO: Add some sort of time-out, to deal with the case */ /* when the server is wedged. */ if (pipe->state == STATE_CONNECTING) { AsyncStatus status = asyncConnector_run(pipe->connector); if (status == ASYNC_NEED_MORE) { return; } else if (status == ASYNC_ERROR) { /* Could not connect, tell our client by closing the channel. */ netPipe_closeFromSocket(pipe); return; } pipe->state = STATE_CONNECTED; netPipe_resetState(pipe); return; } /* Otherwise, accept incoming data */ if ((events & LOOP_IO_READ) != 0) { if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) { wakeFlags |= PIPE_WAKE_READ; } } if ((events & LOOP_IO_WRITE) != 0) { if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { wakeFlags |= PIPE_WAKE_WRITE; } } /* Send wake signal to the guest if needed */ if (wakeFlags != 0) { goldfish_pipe_wake(pipe->hwpipe, wakeFlags); pipe->wakeWanted &= ~wakeFlags; } /* Reset state */ netPipe_resetState(pipe); } 通过/dev/qemu_pipe写东西,最终会调用到netPipe_sendBuffers函数(evil switch in pipeConnector_sendBuffers),然后通过刚才建立的socket发送给画图服务端 static int netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) { NetPipe* pipe = opaque; int count = 0; int ret = 0; int buffStart = 0; const GoldfishPipeBuffer* buff = buffers; const GoldfishPipeBuffer* buffEnd = buff + numBuffers; ret = netPipeReadySend(pipe); if (ret != 0) return ret; for (; buff < buffEnd; buff++) count += buff->size; buff = buffers; while (count > 0) { int avail = buff->size - buffStart; int len = socket_send(pipe->io->fd, buff->data + buffStart, avail); /* the write succeeded */ if (len > 0) { buffStart += len; if (buffStart >= buff->size) { buff++; buffStart = 0; } count -= len; ret += len; continue; } /* we reached the end of stream? */ if (len == 0) { if (ret == 0) ret = PIPE_ERROR_IO; break; } /* if we already wrote some stuff, simply return */ if (ret > 0) { break; } /* need to return an appropriate error code */ if (errno == EAGAIN || errno == EWOULDBLOCK) { ret = PIPE_ERROR_AGAIN; } else { ret = PIPE_ERROR_IO; } break; } return ret; } 从/dev/qemu_pipe读东西,最终会调用到netPipe_recvBuffers函数(evil switch inpipeConnector_sendBuffers),然后通过刚才建立的socket从画图服务端接收数据 static int netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) { NetPipe* pipe = opaque; int count = 0; int ret = 0; int buffStart = 0; GoldfishPipeBuffer* buff = buffers; GoldfishPipeBuffer* buffEnd = buff + numBuffers; for (; buff < buffEnd; buff++) count += buff->size; buff = buffers; while (count > 0) { int avail = buff->size - buffStart; int len = socket_recv(pipe->io->fd, buff->data + buffStart, avail); /* the read succeeded */ if (len > 0) { buffStart += len; if (buffStart >= buff->size) { buff++; buffStart = 0; } count -= len; ret += len; continue; } /* we reached the end of stream? */ if (len == 0) { if (ret == 0) ret = PIPE_ERROR_IO; break; } /* if we already read some stuff, simply return */ if (ret > 0) { break; } /* need to return an appropriate error code */ if (errno == EAGAIN || errno == EWOULDBLOCK) { ret = PIPE_ERROR_AGAIN; } else { ret = PIPE_ERROR_IO; } break; } return ret; } netPipe_poll调用loopIo_poll判断是否POLLIN, POLLOUT static unsigned netPipe_poll( void* opaque ) { NetPipe* pipe = opaque; unsigned mask = loopIo_poll(pipe->io); unsigned ret = 0; if (mask & LOOP_IO_READ) ret |= PIPE_POLL_IN; if (mask & LOOP_IO_WRITE) ret |= PIPE_POLL_OUT; return ret; } netPipe_wakeOn设置想等待什么事件 static void netPipe_wakeOn( void* opaque, int flags ) { NetPipe* pipe = opaque; DD("%s: flags=%d", __FUNCTION__, flags); pipe->wakeWanted |= flags; netPipe_resetState(pipe); } 三、使用host gpu 老规矩,先看文档,文档在模拟器的repo中可以找到,我在android的repo里面没找到 重点看external/qemu/distrib/android-emugl/DESIGN libEGL.so、libGLESv1_CM.so和libGLESv2.so是纯软件方面的通用的代码,在模拟器上不需要任何特殊的处理 libEGL.so会根据egl.cfg的配置,dlopen几个库,在模拟器上是libEGL_emulation.so、libGLESv1_CM_emulation.so和libGLESv2_emulation.so,这几个库是硬件相关的 It is important to understand that the emulation-specific EGL/GLES libraries are not directly linked by applications at runtime. Instead, the system provides a set of "meta" EGL/GLES libraries that will load the appropriate hardware-specific libraries on first use. More specifically, the system libEGL.so contains a "loader" which will try to load: - hardware-specific EGL/GLES libraries - the software-based rendering libraries (called "libagl") The system libEGL.so is also capable of merging the EGL configs of both the hardware and software libraries transparently to the application. The system libGLESv1_CM.so and libGLESv2.so, work with it to ensure that the thread's current context will be linked to either the hardware or software libraries depending on the config selected. For the record, the loader's source code in under frameworks/base/opengl/libs/EGL/Loader.cpp. It depends on a file named /system/lib/egl/egl.cfg which must contain two lines that look like: 0 1 <name> 0 0 android The first number in each line is a display number, and must be 0 since the system's EGL/GLES libraries don't support anything else. The second number must be 1 to indicate hardware libraries, and 0 to indicate a software one. The line corresponding to the hardware library, if any, must always appear before the one for the software library. The third field is a name corresponding to a shared library suffix. It really means that the corresponding libraries will be named libEGL_<name>.so, libGLESv1_CM_<name>.so and libGLESv2_<name>.so. Moreover these libraries must be placed under /system/lib/egl/ The name "android" is reserved for the system software renderer. The egl.cfg that comes with this project uses the name "emulation" for the hardware libraries. This means that it provides an egl.cfg file that contains the following lines: 0 1 emulation 0 0 android libEGL_emulation.so、libGLESv1_CM_emulation.so和libGLESv2_emulation.so对应了GUEST SYSTEM LIBRARIES,它们的函数最终都会通过<function_name>_enc进行encode,然后通过opengles pipe serice给到emulator,emulator再通过tcp socket发送给画图服务端,服务端的libOpenglRender.so使用几个decode的库去进行decode,然后分发给libEGL_translator.so、libGLES_CM_translator.so和libGLESv2_translator.so,它们将opengles的调用转为opengl的调用,然后使用host系统的opengl库去执行 _________ __________ __________ | | | | | | |EMULATION| |EMULATION | |EMULATION | GUEST | EGL | | GLES 1.1 | | GLES 2.0 | SYSTEM |_________| |__________| |__________| LIBRARIES ^ ^ ^ | | | - - - | - - - - - - - - - | - - - - - - - - - | - - - - - | | | ____v____________________v____________________v____ GUEST | | KERNEL | QEMU PIPE | |___________________________________________________| ^ | - - - - - - - - - - - - - -|- - - - - - - - - - - - - - - - | | PROTOCOL BYTE STREAM _____v_____ | | | EMULATOR | |___________| ^ | UNMODIFIED PROTOCOL BYTE STREAM _____v_____ | | | RENDERER | |___________| ^ ^ ^ | | | +-----------------+ | +-----------------+ | | | ____v____ ___v______ ____v_____ | | | | | | |TRANSLATOR |TRANSLATOR| |TRANSLATOR| HOST | EGL | | GLES 1.1 | | GLES 2.0 | TRANSLATOR |_________| |__________| |__________| LIBRARIES ^ ^ ^ | | | - - - | - - - - - - - - - | - - - - - - - - - | - - - - - | | | ____v____ ____v_____ _____v____ HOST | | | | | | SYSTEM | GLX | | GL 2.0 | | GL 2.0 | LIBRARIES |_________| |__________| |__________| (NOTE: 'GLX' is for Linux only, replace 'AGL' on OS X, and 'WGL' on Windows). qemu和ui是两个不同的进程,ui里面的是画图的服务端,qemu里面的是socket client,也就是opengles pipe service中建立的socket连接。 再看看external/qemu/docs/GPU-EMULATION.TXT GPU emulation is controlled by the followind AVD hardware properties: hw.gpu.enabled Boolean indicating whether GPU emulation is enabled. If 'false', the builtin guest software renderer will be used instead. Note that the latter is very slow and only supports GLES 1.x, so most applications will not run with it properly. hw.gpu.mode Only used when hw.gpu.enabled is on. This is a string that describes which emulation mode to support. At this time, the following modes are supported: 'host' The default mode, uses specific translator libraries to convert guest EGL/GLES commands into host desktop GL ones. This requires valid OpenGL drivers installed on the development machine, and of course a display connection. Note that on some platforms (Windows in particular), OpenGL drivers can be buggy, resulting in poor performance, rendering artefacts or even crashes. To work-around this, use the 'mesa' mode instead. 'mesa' Software-based desktop GL renderer, based on the Mesa3D library. This is slower than 'host' mode by a large margin, but works equally well on all supported platforms. This is recommended if there are issues with 'host' mode. Another benefit of 'mesa' mode is that it can be run on headless servers which do not have a GPU, or OpenGL libraries installed. 'auto' Uses 'host' mode if not remoting (NX or CRD), 'mesa' otherwise. 'guest' Use a guest-side OpenGL ES implementation.

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册