• [技术干货] 【转】如何在win10 64位下搭载汇编环境(包含汇编dosbox和masm文件)
    网上的教程只能在win10中使用debug 命令 不能使用edit命令找了半天终于找到了可以在64位机下使用的edit源文件并插入到网上下载的MASM包中。首先,将文件下载安装DOSBox0.74注意不要安装在C盘将下载的MASM文件(如图)复制到一个根目录(D或E盘)的文件夹(自己新建,比如我在E盘中创建xuyi文件夹)新文件夹的内容此时打开DosBox会出现两个程序,使用前面一个程序,但后面的不要关此时盘符是Z(为虚拟磁盘)下面使用mount命令更改DOSBOX的映射指向(注意xuyi是文件夹的名字,你输入你创建的文件夹名称)输入:mount c e:\xuyi之后按回车键当出现Drive C is mounted as local directory e:\xuyi\ 是表明映射成功下面使用dos命令”c:”更改盘符进入虚拟盘C:这个虚拟盘C就是刚才创建的文件夹xuyi接下来就可以愉快得进行汇编程序设计啦!!!下图是使用debug中的-r命令使用edit命令注意点进去后可能光标会消失,此时调用任务管理器alt+ctrl+del即可释放光标。转自:https://blog.csdn.net/xyisv/article/details/69062382?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare 
  • [交流分享] gcc和mingw的区别
    ## Mingw MinGW全称Minimalist GNU For Windows,是个精简的Windows平台C/C++、ADA及Fortran编译器,相比Cygwin而言,体积要小很多,使用较为方便。MinGW提供了一套完整的开源编译工具集,以适合Windows平台应用开发,且不依赖任何第三方C运行时库。 MinGW包括: 一套集成编译器,包括C、C++、ADA语言和Fortran语言编译器 用于生成Windows二进制文件的GNU工具的(编译器、链接器和档案管理器) 用于Windows平台安装和部署MinGW和MSYS的命令行安装器(mingw-get) 用于命令行安装器的GUI打包器(mingw-get-inst) ## GCC GCC(GNU Compiler Collection,GNU编译器套件),是由 GNU 开发的编程语言编译器。它是以GPL许可证所发行的自由软件,也是 GNU计划的关键部分。GCC原本作为GNU操作系统的官方编译器,现已被大多数类Unix操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器,GCC同样适用于微软的Windows。 GCC是自由软件过程发展中的著名例子,由自由软件基金会以GPL协议发布。 GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。 ## 总结 mingw可以理解为gcc在windows平台下的实现。但是MinGW使用Windows中的C运行库,因此用MinGW开发的程序不需要额外的第三方DLL支持就可以直接在Windows下运行,而且也不一定必须遵从GPL许可证;这同时造成了MinGW开发的程序只能使用Win32API和跨平台的第三方库,而缺少POSIX支持,大多数GNU软件无法在不修改源代码的情况下用MinGW编译。
  • [技术干货] 【转载】C 与 C++ 40 年的爱恨情仇
    以下为译文:70年代初,贝尔实验室创建了C语言,它是开发UNIX的副产品。很快C就成为了最受欢迎的编程语言之一。但是对于Bjarne Stroustrup来说,C的表达能力还不够。于是,他在1983年的博士论文中扩展了C语言。于是,支持类的C语言诞生了。当时,Bjarne Stroustrup明白编程语言有许多组成部分,除了语言本身,还有编译器、链接器和各种库。提供熟悉的工具有助于语言被广泛接受。在这种历史背景下,在C语言的基础上开发C++也是有道理的。40年后,C和C++都在行业中得到了广泛使用。但是,互联网上的C开发人员认为C++是有史以来最糟糕的人类发明,而许多C++开发人员则希望有朝一日C语言灰飞烟灭。究竟发生了什么事?从表面上看,C和C++都可以满足相同的用例:高性能、确定性、原生但可移植的代码,可用于最广泛的硬件和应用程序。但是,更让C自豪的是它是一门低级语言,更接近汇编。而C++,从诞生第一天开始就充斥了各种奇怪的东西。例如析构函数这个黑魔法。自作主张的编译器。尽管很早C++就有了类型推断功能,但是80年代中期的开发人员还无法接受这个概念,因此Bjarne Stroustrup不得不删除了auto,直到C++ 11又重新添加回来。从那以后,C++就不断加入各种工具来实现抽象。很难说C++是一种低级语言还是高级语言。从设计目的上来说,C++两者都是。但是在不牺牲性能的情况下,建立高级抽象是很困难的。于是C++引入了各种工具来实现constexpr、move语义、模板和不断增长的标准库。从根本上讲,我认为C信任开发人员,而C++信任编译器。这是一个巨大的差异,单凭“两者的原生类型相同”、“while循环的语法相同”等简单一致是无法掩盖的。C++开发人员将有这些问题归咎于C,而C开发人员则认为C++过于疯狂。我觉得站在C的角度看C++,这种说法也很正确。作为C的超集,C++确实很疯狂。一个经验丰富的C开发人员面对C++可能没有熟悉的感觉。C++不是C,这就足以引发互联网上的激烈争论。然而,虽然我不喜欢C,但也没有权利取笑C。尽管我有一定的C++经验,但用C编写过的代码少之又少,而且肯定是很糟糕的代码。好的编程语言包括良好的实践、模式、惯用写法,这些都需要多年的学习。如果你尝试用编写C++的方式写C的代码,或者用C的方式编写C++的代码,那感觉一定很糟糕。即便你懂C,也不一定会C++,反之亦然,懂C++也不一定会用C编程。那么,我们是否应该停止说C/C++,为这两个不幸的命名而感到悲哀吗?也不至于。尽管C++的设计理念与C不一样,但是C++仍然是C的超集。也就是说,你可以在C++转换单元中包含C的头文件,这样依然可以通过编译。而这正是造成混乱的地方。C++不是C的扩展,它是由不同的委员会、不同的人独立设计的标准。从逻辑上讲,喜欢C++理念的人会参与C++社区以及C++标准化的过程,而其他人可能会尝试参与C。无论是C的委员会还是C++委员会,他们表达意图和方向的方式只能通过各自的最终产品:标准;而标准是众多投票的成果。然而,编译器很难知道它正在处理的是C头文件还是C++头文件。extern “C” 标记并没有得到广泛一致的使用,而且它只能影响修饰,而不会影响语法或语义。头文件仅对预处理器有影响,对于C++编译器而言,所有内容都是C++转换单元,因此也就是C++。然而,人们依然会在C++中包含C头文件,并期望它“正常工作”,而大多数时候也确实可以正常工作。那么,我们不禁想问:由不同地方的、不同的人开发的C++代码如何保持C的兼容性?恐怕很难。最近,一位同事让我想起了康威定律:"设计系统的架构受制于产生这些设计的组织的沟通结构。"根据这个逻辑,如果两个委员不互相合作,则他们创造的语言也不会互通。C++维护了一个与C及其标准库的不兼容列表。然而该列表似乎并未反映出许多C11和C18中添加、但在C++中不合法的功能。更清晰的介绍请参见这个维基本科页面(https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B)。然而,仅仅列出两种语言之间的不兼容性,并不足以衡量二者的不兼容性。那些存在于C++标准库中但主要声明来自C的函数,很难声明成constexpr,更难声明成noexcept。C的兼容性会导致性能成本,而C函数是优化的障碍。许多C的结构在C++中都是有效的,但无法通过代码审查(如NULL、longjmp、malloc、构造/析构函数、free、C风格的类型强制转换等)。在C看来,这些惯用写法可能问题不大,但在C++中可不行。C++具有更强大的类型系统,不幸的是,C的惯用写法在这个类型系统中凿了一个洞,因此实现C的兼容性需要在安全性方面付出代价。别误会,C++仍然关心C的兼容性,某种程度上。然而,有趣的是C也很关心C++,某种程度上。实话实说,C对C++的关心程度可能高于C++对C的关心。看来,每个委员会还是在乎另一个委员会的工作。但我们很不情愿。C++知道,许多基础库都是用C编写的,不仅包括libc,而且还有zip、png、curl、openssl(!)以及许多其他库,无数的C++项目都在使用这些库。C++不能破坏这些兼容性。但是最近,尤其是在过去的十年中,C++的规模已远远超过C。C++拥有更多的用户,并且社区更加活跃。也许这就是为什么如今C++委员会的规模是C委员会的10倍以上。C++是不可忽视的力量,因此C委员会必须考虑不破坏C++兼容性。如果非要说一个标准追随另一个标准对话,那么如今C++是领头者,而C是追随者。现在,C++处于稳定的三年周期中,无论是风雨还是烈日,抑或是致命的新疫情。而C每十年左右才发布一次主版本。不过这也很合理,因为作为一种较低级的语言,C不需要发展得那么快。C语言的环境也与C++完全不同。C多用于平台,更多地用于编译器。每个人(甚至他们的狗狗)都会编写C编译器,因为该语言的特性集很小,所以任何人都可以编写C编译器。而C++委员会真正考虑的实现只有四种,而且在每次会议上这四种实现都会出现。所以,C语言中的许多功能都是与实现有关的,或者是可选支持的,这样各种编译器不需要做太多努力就可以声称自己遵从了标准,据说这样委员会的人会比较高兴。如今,C++更加侧重于可移植性,而不是实现的自由。这又是一个理念的不同。因此,你的提议破坏了C的兼容性我提议的P2178的一部分理论上会影响与C的兼容性。这样的话所有方案都不会令人满意。有人可能会说,你可以先向C委员会提议你的新特性。这意味着需要召开更多会议。C会议的严格出席规则可能导致你无法参加会议,这就将那些不愿意花上数千美元成为ISO会员的个人拒之门外。这是因为C委员会必须遵守ISO的规则。而且,如果新的标准刚刚发布,那么可能还需要等待十年时间,你的提案才会被考虑。最重要的是,如果C委员不理解或不在乎你正在努力解决的问题,那么你的提案就石沉大海了。或者他们可能没有精力来处理这个问题。而且,可能你也没有精力来处理C。毕竟,你的本意是要改进C++。实际上,哪怕会议上无人反对你的提议(尽管不太可能发生),如果有人让你先去跟C委员会的人讨论,就等于给你的提议判了死刑。另一种可能的情况是,C委员会接受与C++中存在的版本略有不同的版本。true只能做一个宏来实现。char16_t需要通过typedef。char32_t不一定是UTF-32。static_assert对应的是 _Static_assert。这类的情况还有很多,我们应该责备C吗?可能不应该。他们的委员会只是在尽力将C语言做好。反之亦然。在C++20中,指定的初始化器就受到了C的启发,但采取了略微不同的规则,因为如果完全一样的话就不符合C++的初始化规则。对于这个问题,我也有责任。C有VLA。如果当时我在,我一定会反对在标准C++中采用它,因为它导致了太多安全性问题。我也会坚决反对将_Generic添加到C++中的提议。也许_Generic的目的是减少由于缺乏模板或缺乏重载而导致的问题,但是C++有这两个功能,从我的角度来看,_Generic并不适合我想象中的C++。这两个委员会似乎对于对方语言的关心程度也不一样。有时我们会遇到兼容性非常好的情况(std::complex),有时完全不在乎兼容性(静态数组参数)。这没有办法。别忘了每个委员会都是一群人,他们在不同的时间、不同的地点投票,而试图控制结果会导致投票毫无意义。将这些人放在同一个房间也不现实。ISO可能会反对,参与者的不平衡会导致C的人处于极大的劣势。C的兼容性不重要如果你是C开发人员,那么肯定会把C视为一种简洁的编程语言。但对于我们其他人而言,C的印象完全不同。C是通用的、跨语言的胶水,可以将一切紧密地结合在一起。对于C++用户而言,C就是他们的API。从这一点来看,C的价值在于其简单性。请记住,C++关心的那一部分C是出现在接口(头文件)中的C。我们关心的是声明,而不是定义。C++需要调用C库中的函数(Python、Fortran、Rust、D、Java等语言也一样,在所有情况下都可以在接口边界使用C)。因此,C是一种接口定义语言。向C添加的内容越多,定义接口就越困难。这些接口随着时间的推移保持稳定的可能性较小。那么,C++中缺少<threads.h>是否重要?可能并不重要,因为这不太可能出现在公共接口中。如今大家都在谈论C过去,C的兼容性是C++的一大卖点。但如今,每个人(甚至他们的金鱼)都懂C。Rust可以调用C函数,Python、Java、一切语言都可以!甚至怪异的Javascript都可以在WebAssemby中调用C函数。但是在这些语言中,接口是显式的。该语言提供的工具可以公开特定的C声明。当然,这比较麻烦。但这可以让接口非常非常清晰。而且还是有界的。例如,在rust中,调用C函数并不会迫使Rust牺牲某些设计来容纳C子集。实际上C是被包含进去的。mod confinment {use std::os::raw::{c_char};extern "C" {pub fn puts(txt: *const c_char);}}pub fn main() {unsafe {confinment::puts(std::ffi::CString::new("Hello, world!").expect("failed!").as_ptr());}}
  • [技术干货] 2020-09-28:内存屏障的汇编指令是啥?
    2020-09-28:内存屏障的汇编指令是啥?#福大大架构师每日一题#
  • [问题求助] 【LITE OS 系统】【在IAR的一些问题】
    Huawei_LiteOS_Kernel的软件定时器和STM32F411的GPIO在IAR跑通后,接下要测试F411的外设功能了,就用UART2来测吧,串口输出 输入功能,也趁这个机会试一下LITE OS的接管中断方法,开始吧,先建一个裸机工程,测试好所有功能:比较简单,随便改改就测试成功了。当我在OS测试时,奇怪的现象出现了,基本的STM32F4XX外设配置都测试通不过。当我不执行USART_Init(USART2, &USART_InitStructure)时,OS运行正常,我的10号任务也跑起来,看汇编的PC指针也正常。当我执行USART_Init(USART2, &USART_InitStructure)时,OS运行不正常了,我的10号任务也跑不起来,看汇编的PC指针也不正常。我的天呀,这是什么问题?是IAR的问题吗?我又花了点时间,将OS变成成裸机代码,一测试发现是OK的,然后又把OS加进来运行,重新试了一下,还是这个死机的问题!无语中。。。。。。本来打算在OS测试好UART2的收发功能后,就测试OS的UART2接收中断功能,现在好了,使用固件库基本功能都会使OS跑飞了,哈哈哈,有人知道是什么原因吗?或者专家出来解释一下,  如果这个LITE  OS在IAR上执行时这么多BUG,我想放弃了,继续玩弄之前学的CONTIKI NG!!!再见。。。。一步一步仿真发现,在执行los_task.c的osTaskEntry();接着执行los_hw.c的LOS_Schedule()就挂掉了,LITE_OS_SEC_TEXT VOID LOS_Schedule(VOID) {     UINTPTR uvIntSave;     uvIntSave = LOS_IntLock();     /* Find the highest task */     g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(LOS_PriqueueTop(), LOS_TASK_CB, stPendList);     /* In case that running is not highest then reschedule */     if (g_stLosTask.pstRunTask != g_stLosTask.pstNewTask)     {         if ((!g_usLosTaskLock))//开始断点         {             (VOID)LOS_IntRestore(uvIntSave);             osTaskSchedule(); //直接跑飞了,                                    return;         }     }     (VOID)LOS_IntRestore(uvIntSave); }如上所示,在某些地方,有执行固件库的USART_Init()时,为什么会在这里挂掉了,return直接退到异常地址了!!这个现象会延时这么久,到这里上才出现
  • [技术干货] 汇编语言中的.text, .data, .bss
    在GNU 汇编(Assembly)程序中,有三个基本的区域,即 .text, .data, .bss.   .text用来存放实际代码的区域。.data用来存放初始化的数据或者常量。这部分的数据在程序运行时不会改变的。.bss用来声明变量的在GNU ARM ASM中,还可以通过section来定义区域的。镜像在加载的时候,会按照区域有一些不同的加载处理方法。理解汇编,有助于深入理解Lite OS系统,会让移植更顺畅一些。欢迎分享讨论。
  • [技术干货] GNU ARM ASM 汇编语言 快速参考文件
    在深入分析系统内核时,有时候需要看一看有些.S文件的内容。类似例如下面.S文件部分内容附件中的参考快速参考资料,如下图所示,是一部分。这类资料有助于理解类似这样的.S文件内容。不同的编译器对于.S文件可能会有一些自己的特点。需要加以区别。
  • [技术干货] GCC工具链详解(下)
    总结:       gcc的参数:              -o 指定生成文件名              -E 只预处理,不编译,不汇编,不链接  文件名一般以.E结尾              -S 只编译,不汇编,不链接     文件名一般以.S结尾              -c 只编译(汇编),不链接       文件名一般以.o结尾        gcc工具链中的常用组件:              size:显示可执行文件或者二进制文件中各个段的大小,如下图:                            ar:用于处理库相关的指令,比如刚才提到的动态库,或者我们为了让程序保密,可以将自己写好的某个模块的代码制作库,只给别人一个头文件,库中都是二进制,无法看到源码,如果大家对制作库感兴趣,可以给我留言,改天专门说一说。              nm:列出一个二进制文件(可执行文件和库文件)中有哪些符号(函数和全局变量),实例如下图:                     as:该指令可以将汇编语言文件编译为二进制文件,其实就是gcc -c -o hello.o hello.S指令调用的工具示例如下图:              ld:链接器,就是将hello.o和动态库链接到一起的工具。              objdump:用于反汇编二进制文件为汇编代码的工具,使用示例如下图:       -D参数:反汇编所有段       >符号:因为objdump是直接打印输出信息,所以我们要把输出信息通过>重定向到hello.dis文件中方便查看。       我这里只截图main函数反汇编的代码块给大家看看:objdump工具在实际开发中作用挺大的,大家可以学习下,后面我还给出了一个利用objdump工具排错的案例。objcopy:该指令一般用于单片机的程序开发,当我们编译好了生成了一个elf文件,但是要求文件格式为hex或者bin才能进行烧录,我们这时就可以采用该指令进行转换,详细操作请参考:https://bbs.huaweicloud.com/forum/thread-58379-1-1.html  编译器优化导致错误的排错思路当程序出现错误时,我们怀疑是编译器的优化导致的错误,我们就可以去反汇编汇编后未链接的文件来看看。使用objdump,这里顺便说个小知识:为什么二进制文件永远无法还原为和原来的.c文件一模一样?就是因为编译器的优化导致的。什么是编译器的优化呢?比如我们执行如下语句,用于延时:       while(time)              ;特别聪明的编译器,就会认为这个语句没有用,自作主张地把这个语句删除了,导致我们的延时失效,如何解决呢?用volatile关键词修饰time,编译器就不敢帮我们优化这个语句了。再来举一个例子,比如我们定义一个指针,将GPIO的状态寄存器的地址赋值给该指针,我们去解析该指针指向地址处的内容时,就可以获取GPIO的状态,假设0为低电平、1为高电平,假设GPIO状态寄存器的地址为0x4000_0000,下面我用伪代码给大家展示:       LED_statr = *((char *)0x40000000)while(1)       {       if (LED_state)              printf(“LED ON\r\n”);       else              printf(“LED OFF\r\n”);}       编译时,如果编译器特别聪明,发现我们从0x4000_0000地址处读取数据特别慢,它就会把第一次读到的值写到一个局部变量中,获取LED_state时并不会从0x4000_0000地址去读,而是直接读取局部变量,所以无论LED的状态是什么LED_state永远是第一次获取到的值,这时我们可以用volatile去修饰,防止此类情况产生。
  • [技术干货] GCC工具链详解(上)
    摘要:GCC全称GNU Compiler Collection,是一套由 GNU 开发的编程语言编译器。它是一套以 GPL 及 LGPL 许可证所发行的自由软件。我们使用IoT Studio编译程序时,最终被调用的编译器也是GCC的一个分支,arm-none-eabi-工具链,注意这里所说的“链”,指一系列工具,编译器、汇编器、链接器等工具,被组装到了一起,形成了一个链子。本篇主要讲解他们的用法。 一个C文件到hex/bin文件的过程其实我们常说的“编译”一个程序,其中“编译“步骤包括了预处理、编译、汇编、链接等步骤,不过常见的IDE(Keil,IAR等)或者Makfile,为了方便用户的使用,将这些步骤封装起来了,我们只需点击“build”或者输入“make”,即可拿到烧录文件hex/bin,为了能提高大家在实际开发中的排错能力,我今天必须将这些知识都拿出来说一说。下面我就按照顺序“预处理à编译à汇编à链接à将elf文件转换为hex/bin文件”在Ubuntu系统下逐步向大家演示并讲解,我用gcc工具链替代arm-none-eabi-gcc工具链,因为ubuntu默认带有gcc工具链,如果你有兴趣可以跟着我做一做下面的实验从而加深理解和记忆。       首先创建两个文件:hello.c和hello.h。 预处理     预处理的作用就是将注释替换为空格和处理宏定义,你看到的#include,#ifndef,#define,#endif都是宏定义的标识符,这一步是将宏定义替换为编译器可以识别的符号,我们可以使用如下命令,让GCC工具链只进行预处理操作,将预后的数据生成到hello.E文件中:       gcc -E -o hello.E hello.c       我们现在可以来查看一下hello.E文件中的内容,我这里只截取最后15行的内容,因为前面的内容是#include <stdio.h>语句引入的内容,我们暂时不关心。              请注意第726行到732行,这是hello.h文件中的内容,被预处理器拷贝到了hello.c文件中,str的定义就有了。编译       编译是将预处理过的c语言文件编译为汇编语言的文件,等待汇编时转换为机器码,我使用如下指令来进行只编译不汇编不链接将汇编内容生成到hello.S文件中。       gcc -S -o hello.S hello.c       我们来查看一些hello.S文件中的内容:       汇编       将汇编代码汇编为二进制代码,这里的二进制代码并不能执行,因为还没有链接,没有运行时地址等等信息,所以无法运行,但是可以用于反汇编分析错误和问题。       我是用如下指令,让编译器只预处理、编译、汇编,不链接,将内容生成到hello.o文件中。       gcc -c -o hello.o hello.c       我们来看看文件中有些什么,注意:这里如果直接使用普通编辑器打开会出现乱码情况,如下图:       我这里有vim编辑器打开,首先使用vim -b hello.o以二进制形式打开该文件,在切换到“命令行模式”(按ESC并输入:),然后输入     %!xxd -g 1 切换为16进制,最终可以看到如下显示:              这是二进制数据和汇编指令一一对应(伪指令除外),最早的程序员就是直接编写这些二进制数据(纸上打孔),但是数字类的东西不便于人类处理,所以就有了“助记符”(汇编)来进行编程。       这里在引入一个小知识:如果你不知道当前文件是什么类型的文件,可以使用file指令查看:       我这里分别查看了.c C语言源文件,.S汇编语言文件,.o二进制待链接文件,hello可执行文件。链接       每个.c文件都会通过编译生成一个.o文件,最终的可执行文件由ld链接器根据链接脚本(没有指定链接脚本采用默认链接脚本)将多个.o文件和库文件(.so)链接并生成可执行文件。       使用如下指令即可完成链接操作,并将可执行文件名生成为hello。       gcc -o hello hello.o       这时,执行./hello,即可输出如下信息:              这里再说一个知识点:这样的链接叫做“动态链接”。       什么是动态链接?比如我们使用了printf函数,如果采用动态链接,printf函数的实现并未放到可执行文件中,而是等待执行hello可执行文件时(./hello),通过加载器去寻找printf所在的动态库并调用,从而输出。这样做的好处是可执行文件占用空间少,坏处是不便于移植,如果移植到没有支持printf函数动态库的机器上就无法运行了,所以单片机的程序都是采用静态链接。将elf文件转换为hex/bin文件       这里可以参考我的另外一篇帖子:https://bbs.huaweicloud.com/forum/thread-58379-1-1.html
  • [技术干货] 0x07 LiteOS内核详解--中断接管机制(下)
    三、中断接管机制涉及到的文件los_hwi.c文件解析       该函数的本质就是直接通过C语言中嵌入汇编代码,读取IPSR寄存器的值,__get_IPSR()函数声明如下图:IPSR寄存器的解析----转自M3内核手册这些函数是用于处理系统中断,系统中断并未被LiteOS中断管理器接管,而是采用传统方案,当发生中断后,直接由中断向量表跳转过来执行。       除系统中断以外的全部中断发生后,都是先跳转到osInterrupt()中执行。①    先保存PRIMASK的值,然后关闭全部可屏蔽中断,相当于进入临界区。②    恢复PRIMASK的值,相当于进入退出区。③    获取当前触发的中断号④    计算出当前中断的对应处理函数在存储初段处理函数的数组中的位置⑤    如果该位置有处理函数就开始调用处理函数来处理中断⑥    调用处理函数来处理中断⑦    如果没有处理函数的中断发生了,就执行LiteOS默认的中断处理函数,其作用就是打印当前发生中断的值。⑧    先保存PRIMASK的值,然后关闭全部可屏蔽中断,相当于进入临界区。⑨    恢复PRIMASK的值,相当于进入退出区。g_vuwIntCount这个值用于表示当前程序的状态,是否处于中断(异常)状态,如果处于异常状态有些操作不能进行,例如:获取互斥锁,调用系统延时函数等等,防止在中断处理中出现死锁的情况,导致一直无法退出中断。       还有一个问题需要注意:中断标志位的清除,因为由用户自己写在用户的中断处理函数中,LiteOS并未帮我们提供。       LiteOS通过将“中断处理函数指针”按照中断向量表中要求的顺序装入一个函数指针数组中,并通过链接脚本放置到向量表处(也就是0x8000_0000的地方),图中①处就是叫链接器将这块数组数据放到.isr_vector的地方。       到这里相信细心的同学一定发现了只有14个函数指针+一个栈顶地址,stm32中那么多的中断,14个函数指针怎么会够用?       现在就可以引出另外一个中断相关的文件----__vectors.h文件,②处在预处理时就会将该文件的内容替换到#incldue “__vectors.h”处。我这里只截取了该文件的部分代码,因为太多了,并且都是一样的,到底要在我们的中断向量表中添加多少个函数指针,取决于系统默认定义的OS_HWI_MAX_NUM宏,在Cortex-m3,Cortex-m4 和 Cortex-m7中该宏的为240,一般情况下用户不需要自己修改,使用默认的即可,因为最大中断数和CPU有着强相关。       初始化硬件中断,根据芯片内核来设置向量表偏移量寄存器,用于重定位中断向量,如果芯片是M3、M4等大于M3的内核还需要设置中断优先级组,默认是7。该函数用于创建LiteOS的中断,最终被osal_int_connect函数调用。uwHwiNum中断号(stm32xxx.h文件中有定义,例如USART1_IRQn)。usHwiPrio中断优先级(这是LiteOS接管中断后的优先级,后面也会设置为NVIC中的优先级)。usMode该参数暂时未被使用。pfnHandler中断处理函数的函数指针。uwArg假设我们的中断处理函数具有参数,可以通过该参数传入。该函数首先判断我们传入的参数是否处于合理的范围,如果都正确,就关闭所有中断,将我们填入的参数存储到m_pstHwiSlaveForm结构体数组中,并使能我们传入的中断和设置NVIC中该中断的优先级,最后开启恢复所有中断并返回OK。中断删除函数,本质上就是关闭该中断,并将该终端的信息从m_pstHwiSlaveForm结构体数组中移除即可。 与中断接管机制相关的文件和函数大概都介绍完了,大家如果有不明白的地方欢迎留言,我会给大家解答。四、中断向量表是如何被链接到”0”地址处__attribute__关键字       attribute有属性的含义,一般出现在代码中是用来定义“编译属性”的,当我们需要将C语言代码中的某个部分编译后链接到某个特定的段时,就需要在这个部分前面加上__attribute__((section(段名))),例如在LiteOS代码中使用了__attribute__ ((section(".isr_vector")))将实现的中断向量表数组设置为.isr_vector段的位置,等待链接器链接到.isr_vector段中。       因为中断向量表数组在los_hwi.c文件中,所以我们可以反汇编编译los_hwi.c后生成的los_hwi.o中间文件来一探究竟。       使用反汇编命令 arm-none-eabi-objdump -x -j.isr_vector。-x选项用于显示数据段的全部信息,如果不加该选项则看不到完整的数据。--j.isr_vector选项用于反汇编指定的段,我们只关心.isr_vector段。结果如下:              编译器将下图的这部分数组相关代码编译到了.isr_vector段中:·偏移量完全和中断向量表中要求的移植,等到链接时,就会将该段的内容(也就是这个数组中的内容)链接到.isr_vector处,下图为链接脚本(os.ld文件):这段数据被放到了0x8000_0000处,上电后stm32“硬件上”自动将0x0000_0000地址和0x8000_0000地址处关联,通俗的来说就是STM32访问0x0000_0000处的数据,实际**问到了0x8000_0000处的数据,也就读取到了栈顶地址、中断向量表。总结:中断向量表为什么会被链接到”0”地址处(这里的0是指0x8000_0000),一个是加载地址,一个是运行时地址。这里的“链接”比较片面,还包括了编译器进行编译时,将存放中断向量表的数据标记为了.isr_vector段,当链接器来链接生成elf文件时,将.isr_vector段内的数据放到了0x8000_0000地址处(加载时地址)。五、中断接管机制的好处       我们可以对比不采用中断接管的方式和采用了中断接管机制的方式实现的中断处理,采用了中断接管的方式:1、 可以实现中断统一管理,如果要使用某个中断,直接调用osal_int_connect函数创建该中断,并将中断处理函数指针与之绑定,中断处理函数的名称完全可以自定义,无需像之前一样必须按照系统要求设定。2、 便于移植,移植的时候无需考虑下层代码,因为不同芯片中断处理相关部分的差异完全被中断接管部分屏蔽。3、 便于维护,调用删除中断函数,即可马上删除某个中断。其他好处就不一一列举了。 
  • [技术干货] 浅析LiteOS启动过程-基于STM32L431
    摘要:本文简要的分析了基于STM32L431的智慧农业案例程序中的LiteOS的启动过程。从经典的main函数开始,到LiteOS正常启动,实现任务的调度。通过这种分析,可以很好的理解LiteOS的一些运行原理过程,有助于LiteOS移植过程中的问题排查,有助于在其他芯片上顺利移植LiteOS。1.从主函数main()进入在嵌入式C语言程序中,程序通常从main函数开始执行。在没有操作系统的时候,通常在主程序中会有一个无限循环。例如下面的这一段程序。在无限循环中,不断控制两个LED灯的亮灭控制。但是,当程序有了操作系统后,这个主函数还有吗?这个无限循环还会有吗,它的运行逻辑是什么?   下面是智慧农业的程序的main函数在这个main函数中62行,调用Hardware_Init进行硬件初始化。主要是时钟、GPIO、UART、LCD等的初始配置。63行,调用LOS_Kernelinit( )对Lite OS的内核进行初始化。70行,调用create_work_tasks(void) 创建了一个工作任务。77行,调用LOS_Start( )启动LiteOS系统。可以看出,在上面的程序中,没有出现while(1)这样的无限循环。那么77行,调用LOS_Start()后,接下来的要执行的程序代码去哪里了?难道就这样return0了吗?不应该。从哪里消失,从哪里找起。从LOS_Start( )“消失”,继续从LOS_Start( )看起。2. 进入LOS_Start( )LOS_Start( )函数在los_init.c文件中,代码如下在99行,调用osTickStart(); 配置Lite OS系统的时钟。Lite OS系统的时钟配置和刚开始的时钟的初始化配置不同。刚开始的时钟配置是基于STM32L431的系统进行的时钟配置。这里的时钟配置,是Lite OS系统中时钟配置。这里Lite OS系统时钟,类似于大家常用的桌面操作系统的时钟概念。   在121行,调用LOS_StartToRun( );启动Lite OS 运行。 LOS_StartToRun()函数,在Keil 环境中,通过右键->转到定义 的方法,找不到函数的实现文件。通过在整个项目中,查找LOS_StartToRun发现LOS原来的los_dispatch_keil.S文件中。3. 进入los_dispatch_keil.S los_dispatch_keil.S使用汇编语言编写的,主要实现了下面几个重要的函数及全局变量。其中有LOS_StartToRun 函数。这个LOS_StartToRun函数就应该是在LOS_Start()函数最后调用的那个LOS_StartToRun( )。 LOS_StartToRun函数代码如下这个代码用汇编语言写的。很难看懂,但是76-80行的注释,简要的说明了这个函数的调用形式以及功能:这个函数是没有返回值,没有参数的,它主要实现了 启动第一个任务,这个任务具有最高的优先级。其他的任务都是由任务调度来启动。任务调度在哪里?下面一会会说。这个函数中最后一行 BX R6 上面的注释说,直接跳转到了g_stLosTask.pstRunTask中存储的PC指针处了,这样main函数的相关新就丢失了,程序就不会返回到main函数中了。通常在嵌入式汇编程序中,调用子程序时,通常会保存号当前函数相关的信息,等调用完后,在恢复到调用的函数中。而这里的 BX R6 并没有让LOS_StartToRun函数返回到调用它的函数中,这样程序就不会在返回到main函数中了。至此,main函数结束了。整个系统的运行就全部依靠Lite OS系统了。不返回main函数了,那么程序从哪里开始执行了?从 PC of g_stLosTask.pstRunTask,开始执行了。这里的PC是程序计数器,里面存储着CPU下一次要执行的程序指令的地址。执行完BX R6后,CPU就取到了g_stLosTask.pstRunTask地址中存储的指令,开始执行了。这个g_stLosTask.pstRunTask里面存储的是什么?在整个项目中查找g_stLosTask.pstRunTask,发现有如下代码在osTaskInit()函数中,对g_stLosTask.pstRunTask进行了赋值。从分析赋值过程语句,可发现在g_stLosTask.pstRunTask中存储了要执行的任务相关的信息存储在里面了,即任务控制块(task control block structure即LOS_TASK_CB中的内容)的内容存储在g_stLosTask.pstRunTask中了。在汇编语言中,将g_stLosTask.pstRunTask中的相关信息存储到STM32L431的相关的工作寄存器中,例如R6 工作寄存器。这样程序就可以执行任务了。而任务的相关信息、入口函数等都可以用C语言来编写。至此,程序可以绕道执行任务相关的代码了。“绕道”,这里绕道可以理解为,终于绕开了那个main函数了,可以执行操作系统中的多个任务了!多个任务的调度,主要由LOS_IntLock,LOS_IntUnlock,LOS_IntRestore,osTaskScheldule,PendSV_Handler这些函数实现。这些函数也是用汇编语言写的。那么这里可以看出,针对不同芯片移植LiteOS的时候,由于不同的汇编语言可能需要不同的类似los_dipatch_keil.S这样的汇编语言编写程序文件。4.综述通过以上的分析,可以看出,在Lite OS在启动的时候,是从main函数中被调用开始,而在被调用的过程中,没有让PC指针再次跳转回main函数中,而是直接跳转到执行任务的命令的地址上去,从而开启了具有多任务执行能力的操作系统功能。
  • [技术干货] 0x04 LiteOS内核详解--一个elf文件中有什么?
    本章将带领大家从一个Huawei_LiteOS.ELF文件里面有什么开始分析,为后面的内核分析做铺垫,特别是“中断接管”以及“启动流程“和本章有这紧密的联系。 一、ELF文件和BIN文件有什么区别?先来看看同一个程序生成的ELF文件和BIN文件的大小:不看不知道,一看吓一跳,同一个程序的ELF文件居然比BIN文件大10倍!如果你拥有一个ELF文件可以通过objcpy指令生成BIN文件,但是BIN文件不能生成ELF文件,它俩的关系是ELF文件包含BIN文件,也就是说BIN文件中有的东西ELF文件中也有,现在我们先讨论一下ELF文件中有的东西但是BIN文件中没有的东西。“DEBUG”功能和“打断点”调式的功能相信大家都用过,可以一边看汇编代码、C代码,或者打上一个断电,让程序运行到断电处停止,这就是ELF文件的功能。ELF文件中包含加载地址、运行地址、重定位表、符号表、汇编语句、注释等等信息。BIN文件中只有机器码,STM32一次读取4字节执行。上图是我通过Hex Editor软件打开的BIN文件,第一帧数据0x0000_0000(16进制表示),代表栈顶地址设置为 0x0000_0000(16进制表示),对应我在stm32启动流程文章中说的“STM32上电后硬件会从0x0000_0000(0地址处会被映射到0x8000_0000,也就是Flash中)”地址处取处四字节数据,将这四字节数据设置为栈顶,大家可能想我问是不是搞错了?为什么是0x0000_0000,没错的,后面会重新设置的,不用担心。第二帧数据是0x0800_0441,这里的地址为0x0000_0004是复位中断向量,CPU会将这个数据读入PC寄存器,跳转到0x0800_0440地方执行复位中断处理函数,相信细心的同学注意到这里的最低位是1,但是我说的是跳转到0x0800_0440地方,这里涉及到ARM指令切换Thumb指令,所以PC的最低位需要写入1,大家只要记住这个数据减1就是需要跳转到的地址。第三帧数据是0x0800_8f01,这里的地址为0x0000_0008是NMI中断向量,CPU会将0x0800_8f01读入PC,跳转到0x0800_8f00的地方执行NMI中断处理函数。 我们就分析到这里吧,以上分析只想向大家说明一个问题,BIN文件中的每一条数据和FLASH中的数据一模一样,位置也是一一对应,最前面的就是中断向量表,LiteOS中通过设置一个数组,将这个数据链接到.isr_vector段中,也就是BIN文件最前面的那几条,到底有几条取决于LiteOS中配置了多少个中断。二、反汇编Huawei_LiteOS.ELF文件如果我们通过文本编辑器打开Huawei_LiteOS.ELF文件,将会看到全是数字,无法分析。这时需要用到objdump命令,将ELF文件反汇编为汇编文件便于分析。以下是我截取的部分具有代表性的段:  这些段构成一个STM32程序的基本单元.isr_vector      存放中断向量表,LiteOS用一个数组来替代并通过链接脚本,将这个数组链接到中断向量段,发生中断时,就会跳到这个段中的某个地址处,将这个地址处的值读到PC中,去执行相应的中断处理函数,这里是重点,后面讲硬件接管中断的时候就要来说了。.text        代码段,所有函数也就是代码都会被链接到.text段中存储,被链接到.text段中的数据在程序运行中一般是不会被修改的,也不可以被修改。 .bss        我们定义的初始值为0或者无初始值的全局变量和静态局部变量会被链接到这个段中保存,这样就不用为这个数据分配存储空间了,只要告诉单片机从xxx地址开始到xxx地址结束的数据为bss段,在复位中断中,通过汇编代码将这块数据清0即可,大大减小了BIN文件的大小。 .comment 注释段,这个段中存放一些注释信息,生成链接文件时,这个段中的信息不会被保存下来。显示编译器的一些信息,所以没必要放到BIN文件中。.data 数据段,有初值并且初值不为0的全局变量和静态局部变量放到这个段中。.rodata 只读数据段,被const修饰的数据会被放到这个段中。 本篇文章点到为止,不过多的深入讲解,因为只是为了后面做LiteOS中断接管机制的一个铺垫,如果大家有兴趣了解更多相关知识,可以留言或者私信我你想听的内容,感谢!
  • [技术干货] 0x02 LiteOS内核详解--浅谈程序编译
    摘要:编译一个程序时,看似点击IDE中的build按钮或者输入一句gcc xx.c -o xx就完成了编译,其实这中间发生了很多有趣的步骤,下面我们以GCC工具链为例,让我带着大家一起来看看吧,这样有助于后面理解LiteOS中的中断接管机制以及driver层设备注册等等。 一个xxx.c文件编译为xxx.hex/.bin文件经历了哪些步骤? 预处理下图是hello.c文件内容,打印A的值,A变量定义在A.h文件中下图是A.h文件内容:我现在使用如下命令gcc -E hello.c -o hello.E,让hello.c文件只预处理,得到hello.E文件,其中已经用在A.h文件中的“int A = 18;”语句对hello.c文件中的#include ”A.h”进行了替换,如图:编译编译的本质就是将C语言代码翻译为汇编代码,gcc -S hello.c -o hello.SHello.S文件的部分内容: 汇编        汇编就是将汇编代码转换为机器码的过程:gcc -c hello.S -o hello.o,因为机器码文件里面全部都是由0\1二进制组成,所以我们无法直接看懂,我这里使用nm hello.o查看其中的符号:        上面的”00000000000”是指这些符号所处的地址,因为没有链接所以地址为0,D是指在该符号定义在的初始化数据段,U是指该符号没有在该文件中定义,需要等链接的时候去其他文件中寻找,T是指该符号位于代码区。链接链接就是将Hello.c文件中用到的外部函数和其本身的函数根据链接脚本(这里用的是默认链接脚本,例如LiteOS中的链接脚本是os.ld)的要求链接到一起,生成可执行文件,我这里使用gcc hello.o -o hello,链接生成可执行文件hello,我们用到了printf库函数,链接器就会去库中帮我们寻找prinf函数并链接在一起。执行./hello,就可以得到如下打印:用nm查看hello可执行程序,这次就能得到所有数据和函数的地址了:  
  • [问题求助] neon汇编指令Error: unknown mnemonic
    asm volatile (                "neoncopypld: \n\t"                "PLD [ %[src] , #0xC0] \n\t"                "VLDM  %[src]! , {d0 - d7} \n\t"                "VSTM  %[dst]! , {d0 - d7} \n\t"                "SUBS  %[sz], %[sz] , #0x40 \n\t"                "BGE neoncopypld \n\t"                : [dst]"+r"(dst), [src]"+r"(src)                : [sz]"r"(sz)                :"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc","memory");这是在网上找到的neon memcpy加速的代码,我在鲲鹏上用g++编译会报错“Error: unknown mnemonic ",不能识别 pld vldm vstm, 汇编小白求助该怎样修改
  • [产品体验官] 【工具体验】鲲鹏软件栈Dependency Advisor/Porting Advisor/Tuning Kit三款工具体验报告
    华为云鲲鹏云专业工具验证报告背景介绍    为了进一步提高鲲鹏云的软件移植效率与工作量评估以及性能调优能力深化;针对华为云鲲鹏云,鲲鹏软件栈的开发工具中提供的三款分析迁移与性能优化工具进行了研究;并给出了验证报告;具体工具如下: Dependency Advisor一、工具介绍一款可以简化客户应用迁移到TaiShan服务器过程的工具。该工具安装在X86服务器上,当客户有软件需要移植到TaiShan服务器上时,可先用该工具分析可移植性和移植投入。该工具解决了客户软件移植评估分析过程中人工分析投入大、准确率低、整体效率低下的痛点,通过该工具能够自动分析并输出指导报告。工具支持的功能特性如下:检查用户软件资源包(RPM、JAR、TAR、zip、gzip文件)中包含的SO依赖库,并评估SO依赖库的可移植性。检查指定的用户软件安装路径下的SO依赖库,并评估SO依赖库的可移植性。检查用户软件C/C++软件构建工程文件,并评估该文件的可移植性。检查用户软件C/C++源码,并评估软件源文件的可移植性。向用户提供软件移植报告,提供移植工作量评估。支持命令行方式和Web两种工作模式。二、安装按照《华为鲲鹏分析扫描工具 用户指南.pdf》在linux-x86_64安装web模式。三、进行测试案例一以hadoop安装包为例分析发布版本二进制软件包迁移可行性及迁移投入该发布包中部分包含so文件的jar包需要重新编译迁移,hadoop相关的多个二进制文件以及so动态库需要重新编译。分析结果如下:从分析报告可知迁移需要重新编译上述5个库文件,但该库的路径不够明确。案例二以mysql-clustre源码包为例分析源码包迁移可行性及迁移投入该源码包迁移过程中遇到C库不兼容导致的C代码修改40行以及汇编未适配导致的20行左右内嵌汇编需要重写。分析结果如下:从分析报告可知无需代码量修改可直接重新编译进行迁移。四、总结1. 优点1)安装、使用简单,方便,可在迁移前进行分析,工具无需安装到迁移环境上;2)支持RPM、JAR、TAR、zip、gzip文件等文件的分析;3)根据分析报告给出具体的工作量。2. 缺点1)只能分析出so文件是否需要重新编译,并且未给出so所在路径以便查找所属包;2)对C/C++源码分析不够准确,存在遗漏项,导致工作量遗漏;3)工具未评估so的可移植性。4)源码编译时的工作量评估不够准确。3. 预测使用场景1)业务二进制发布包迁移评估工作量时可使用该工具进行评估作为参考,但不可过度依赖该工具评估的工作量。Porting Advisor一、工具介绍该工具是一款可以简化客户应用迁移到TaiShan服务器的过程的工具。当客户有X86平台上源代码的软件要移植到TaiShan服务器上时,可用该工具自动分析出需修改的代码内容,并指导用户如何修改。该工具解决了用户代码兼容性人工排查困难、移植经验欠缺、反复依赖编译调错定位等痛点。支持的功能特性如下:检查用户C/C++软件构建工程文件,并指导用户如何移植该文件。检查用户C/C++软件构建工程文件使用的链接库,并提供可移植性信息。检查用户C/C++软件源码,并指导用户如何移植源文件。检查用户软件中X86汇编代码,并指导用户如何移植。支持命令行方式和Web两种工作模式。二、安装按照《华为鲲鹏代码迁移工具 用户指南.pdf》在linux-x86_64安装web模式。三、进行测试案例一以mysql-clustre源码包为例分析源码包迁移可行性该源码包迁移过程中遇到C库不兼容导致的C代码修改40行以及汇编未适配导致的20行左右内嵌汇编需要增加。分析结果如下:从分析结果看未扫出任何需要修改的代码,与实际不符。四、总结1. 优点1)安装、使用简单,方便,可在迁移前进行分析,工具无需安装到迁移环境上;2. 缺点1)内嵌汇编检查不够完善。2)对库调用检查不完整(如:syscall(__NR_epoll_create, size));3)当工具无法扫描到,实施时人工识别到时,无法从工具页面获取如何修改的案例。4)用户手册中的白名单不知从哪里获取更新,并且其格式是什么,资料中未给出。5)用户对自己识别到的常用迁移问题无法扩展。3. 预测使用场景1)业务代码或开源代码迁移前先用该工具扫描,可以识别到部分迁移过程可能会遇到的问题。Tuning Kit一、工具介绍是针对TaiShan服务器的性能分析和优化工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析出系统性能指标,定位到瓶颈点及热点函数。华为鲲鹏性能优化工具支持的功能特性如下:支持采集整个系统或指定进程(包括运行中的进程或直接启动的进程)的CPU Cycles性能事件,能够快速定位到热点函数,包括应用程序函数、模块函数与内核函数,甚至能够定位到热点指令。支持热点函数按照CPU核/线程/模块进行分组,支持查看热点函数调用栈。支持通过火焰图查看热点函数及其调用栈。支持代码映射功能,即查看函数内的热点指令及该指令对应的高级语言文件及行号。支持显示汇编代码的控制流图。支持分析Java代码的热点函数及热点指令。二、安装按照《华为鲲鹏性能优化工具 用户指南.pdf》在鲲鹏云linux-aarch64实例上进行安装。三、进行测试案例一以ls为例检查工具功能可用性分析结果如下:从分析结果可以看到热点函数、火焰图等信息进行分析系统瓶颈。四、总结1. 存在如下问题1)安装资料2.3章节步骤8安装nginx:./auto/configure文件不存在,需要修改为./configure;2)安装资料2.3章节步骤9安装python3:发布包未包含python3包,需要自行下载,资料未提供下载路径;4)安装资料2.3章节步骤11修改端口号:端口号不可修改为8000端口,该端口为gunicron监听端口。2. 优点:1)安装部署方便、除第三方工具外支持一键部署;2)工具分析比较全面,可分析正在运行以及未运行的C、JAVA进程的热点函数、指令以及调用栈、火焰图;3)可分析整体系统的热点函数、指令以及火焰图。3. 缺点:1)需要部署在待分析环境上进行分析;2)需要安装的第三方软件包python3、nginx、sqlite3需要自行编译安装,比较麻烦。4. 预测使用场景1)有助于开发人员分析客户应用以及开源软件迁移导致的性能变化的原因;2)有助于开发人员进行软件及系统性能的调优;3)由于需要部署到待测环境上,所以在生产环境上可能存在使用限制问题。
总条数:50 到第
上滑加载中