首页 文章 精选 留言 我的

精选列表

搜索[智能解析],共10000篇文章
优秀的个人博客,低调大师

Cortex-M3启动深度解析

Cortex-Mx启动,备忘,以免将来忘记。 中断向量表不用说,从重置中断开始吧 LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 这里一共就执行了两个函数,SystemInit和__main。 我们一般在SystemInit里面配置系统时钟,这里跳过。 __main是我们今天的重点。 __main: 0x080000C0 4804 LDR r0,[pc,#16] ; @0x080000D4 0x080000C2 4685 MOV sp,r0 #设置栈顶 _main_scatterload: 0x080000C4 F000F956 BL.W __scatterload (0x08000374) #分散加载(拷贝初始值、清零变量) __main_after_scatterload: 0x080000C8 F000F8D4 BL.W __cpp_initialize__aeabi_ (0x08000274) #C++初始化(全局对象构造函数执行) _main_cpp_init: 0x080000CC 4800 LDR r0,[pc,#0] ; @0x080000D0 0x080000CE 4700 BX r0 #进入C++的main函数 分散加载这里将会初始化我们的全局静态变量 .text:00000000 __scatterload_rt2 .text:00000000 LDR R4, =Region$Table$Base .text:00000002 MOVS R5, #1 .text:00000004 LDR R6, =Region$Table$Limit .text:00000006 B loc_14 .text:00000008 ; --------------------------------------------------------------------------- .text:00000008 .text:00000008 loc_8 ; CODE XREF: __scatterload_rt2+16j .text:00000008 LDR R3, [R4,#0xC] .text:0000000A LDMIA R4!, {R0-R2} .text:0000000C ORRS R3, R5 .text:0000000E SUBS R4, #0xC .text:00000010 BLX R3 .text:00000012 ADDS R4, #0x10 .text:00000014 .text:00000014 loc_14 ; CODE XREF: __scatterload_rt2+6j .text:00000014 CMP R4, R6 .text:00000016 BCC loc_8 .text:00000018 BL __main_after_scatterload 可能汇编看起来不是很直观,我们配合看F5得到的反编译C代码 void _scatterload_rt2() { _DWORD *i; // r4@1 for ( i = &Region__Table__Base; (_UNKNOWN *)i < &Region__Table__Limit; i += 4 ) ((void (__fastcall *)(_DWORD, _DWORD, _DWORD))(i[3] | 1))(*i, i[1], i[2]); _main_after_scatterload(); JUMPOUT(&Region__Table__Base); } 从上面看出,有一张区域表,每4个int作为一段,每一段前三个为参数,第四个为函数指针。 经过调试得知: 第一个函数是__scatterload_copy,从某个Flash地址把数据拷贝给RAM里面的变量,也就是带有初始值的全局变量; 第二个函数是__scatterload_zeroinit,顾名思义,就是全局变量清零,只要没有初始值的全局变量,都清零,堆空间和栈空间一块清。 而在汇编文件里面我们有AREAHEAP,NOINIT, READWRITE, ALIGN=3。显然其中的NOINIT并没有生效 ER_IROM1:0800D704 Region$Table$Base DCD 0x800D72C ; #要拷贝的初始值来源,紧跟在固件源代码后面 DATA XREF: ER_IROM1:__scatterload_rt2o ER_IROM1:0800D704 ; ER_IROM1:off_8000390o ER_IROM1:0800D708 DCD 0x20000000 #要复制初始值的起始地址 ER_IROM1:0800D70C DCD 0xCC #带有初始值的区域大小 ER_IROM1:0800D710 DCD __scatterload_copy ER_IROM1:0800D714 DCD 0x800D7F8 ER_IROM1:0800D718 DCD 0x200000CC #要清零的变量起始地址 ER_IROM1:0800D71C DCD 0x132C #要清零的变量大小 ER_IROM1:0800D720 DCD __scatterload_zeroinit ER_IROM1:0800D724 EXPORT SHT$INIT_ARRAY$Base 因此,针对__scatterload_zeroinit而言,为了加快启动速度,建议堆空间设得尽量小,然后在SmartOS系统初始化的时候再重新设置堆空间 我不相信神话,我只相信汗水!我不相信命运,我只相信双手! 分类: 物联网 本文转自大石头博客园博客,原文链接:http://www.cnblogs.com/nnhy/p/cortex_m3_boot.html,如需转载请自行联系原作者

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

Storm的BaseBasicBolt源码解析ack机制

我们在学习ack机制的时候,我们知道Storm的Bolt有BaseBasicBolt和BaseRichBolt。在BaseBasicBolt中,BasicOutputCollector在emit数据的时候,会自动和输入的tuple相关联,而在execute方法结束的时候那个输入tuple会被自动ack。在使用BaseRichBolt需要在emit数据的时候,显示指定该数据的源tuple要加上第二个参数anchor tuple,以保持tracker链路,即collector.emit(oldTuple, newTuple);并且需要在execute执行成功后调用OutputCollector.ack(tuple), 当失败处理时,执行OutputCollector.fail(tuple);那么我们来看看BasicBolt的源码是不是这样的,不能因为看到别人的帖子说是这样的,我们就这样任务,以讹传讹,我们要To see is to believe。 为了方便看源代码,我先上我们的继承类: public class SplitSentenceBolt extends BaseBasicBolt { public void prepare(Map stormConf, TopologyContext context) { super.prepare(stormConf, context); } //5:执行我们自己的逻辑处理方法,接收传入的参数。 public void execute(Tuple input, BasicOutputCollector collector) { String sentence = (String)input.getValueByField("sentence"); String[] words = sentence.split(" "); for (String word : words) { word = word.trim(); word = word.toLowerCase(); collector.emit(new Values(word,1));//这个地方就是调用OutputCollector的包装类,来发消息 } } public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word","num")); } } 通过打断点,我们发现,bolt的task会创建这个类下面会标准执行顺序 public class BasicBoltExecutor implements IRichBolt { public static Logger LOG = LoggerFactory.getLogger(BasicBoltExecutor.class); private IBasicBolt _bolt; private transient BasicOutputCollector _collector; //1:创建该对象,然后把我们写的SplitSentenceBolt对象赋给父类IBasicBolt。 public BasicBoltExecutor(IBasicBolt bolt) { _bolt = bolt; } public void declareOutputFields(OutputFieldsDeclarer declarer) { _bolt.declareOutputFields(declarer);//这里就是调用SplitSentenceBolt对象的方法了。 } //2:给BasicOutputCollector _collector字段赋值,BasicOutputCollector就是对OutputCollector类的包装。 public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { _bolt.prepare(stormConf, context); _collector = new BasicOutputCollector(collector); } //3:然后程序执行该方法,input的值source: spout1:4, stream: default, id: {}, [+ - * % /] public void execute(Tuple input) { _collector.setContext(input);//把接收到的tuple值设置给BasicOutputCollector中inputTuple字段。 try { _bolt.execute(input, _collector);//这个地方是调用我们实现类SplitSentenceBolt的ececute方法。 _collector.getOutputter().ack(input);//这个地方就是响应 } catch(FailedException e) { if(e instanceof ReportedFailedException) { _collector.reportError(e); } _collector.getOutputter().fail(input);//这个地方就是响应 } } public void cleanup() { _bolt.cleanup(); } public Map<String, Object> getComponentConfiguration() { return _bolt.getComponentConfiguration(); } } public class BasicOutputCollector implements IBasicOutputCollector { private OutputCollector out; private Tuple inputTuple; public BasicOutputCollector(OutputCollector out) { this.out = out; } //4:把收到的tuple数据赋值给inputTuple,这个时候BasicOutputCollector对象的字段都具有值了。 public void setContext(Tuple inputTuple) { this.inputTuple = inputTuple; } //6:这里我们发送新的(转换后的)tuple数据,看他内部的调用,其实他也会发送一个anchor tuple来保持tracker链路,而这个anchor tuple就是bolt接收到转换前的源tuple数据。 public List<Integer> emit(List<Object> tuple) { return emit(Utils.DEFAULT_STREAM_ID, tuple); } public List<Integer> emit(String streamId, List<Object> tuple) { return out.emit(streamId, inputTuple, tuple); } public void emitDirect(int taskId, String streamId, List<Object> tuple) { out.emitDirect(taskId, streamId, inputTuple, tuple); } public void emitDirect(int taskId, List<Object> tuple) { emitDirect(taskId, Utils.DEFAULT_STREAM_ID, tuple); } protected IOutputCollector getOutputter() { return out; } public void reportError(Throwable t) { out.reportError(t); } } 这里大家不要纠结bolt的启动时从哪里开始的,我后面会讲的,这里我们关注的是,BasicBoltExecutor对象创建后的执行过程,以这我们来看执行的过程。在BasicBoltExecutor的execute方法中,我们看到了ack和fail方法会被自动调用的,当我们的程序抛出异常则会执行fail方法的。 这个 作者: intsmaze(刘洋) 出处: http://www.cnblogs.com/intsmaze/ 老铁,你的--->推荐,--->关注,--->评论--->是我继续写作的动力。 微信公众号号:Apache技术研究院 由于博主能力有限,文中可能存在描述不正确,欢迎指正、补充! 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

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

registry逻辑解析:启动与配置加载

Docker社区中有丰富的文档介绍,registry如何使用,但对于registry内部的处理逻辑却鲜有人知。下面对于registry的代码进行了一些阅读,下文将讲述registry的一些关键逻辑。 首先,是启动过程。参照下图: registry的启动,主要是由通用的 cmd启动 registry.go进行启动的,registry.go中会引用yaml的parser去加载confiy.yml文件(configuration.go)是配置文件schema,加载config的之后会对照schema并忽略不在schema中的配置节点。 之后,会去读系统ENV,在系统配置中读出通过docker-compose(或者命令行加入的环境变量)。会按照环境变量名称找到对应了configration,并且“覆盖”第一次读出的config配置。 **从这里可

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

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

Rocky Linux

Rocky Linux

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

用户登录
用户注册