嵌入式系统的启动速度因设备的性能和代码的质量而异,但总体而言,从消费者的角度考虑,系统的启动速度肯定是越快越好。因此,对嵌入式系统进行性能优化,加快设备的启动时间为项目后期必须进行的一项工作。需要注意的是:嵌入式Linux设备的优化不是一蹴而就的,而是一个不断优化,不断改进的过程。

现将自己掌握的嵌入式设备的性能优化策略进行总结,如有不对的地方,还望批评指正。


启动快慢的标准

设备启动的快慢目前还没有一个统一的标准。在项目中一般按照客户的标准。

性能的评测

对于开发人员来说,评价设备的性能一般是通过在代码中增加log的方式。这种方式具有以下几点优点:

  1. 精确度高。

通常能够精确到毫秒。有特殊需求的情况下,可以精确到毫秒,比如使用gettimeofday函数。

  • 灵活性强。

  • 可以测出代码中任意部分的代码运行所耗费的时间。

    导致性能低下的原因

    在嵌入式设备中,导入设备启动时间过长,性能低下的原因一般包括如下几个方面:

    1. 硬件的原因

    硬件的原因一般指的是设备的CPU及Flash性能。如果代码的运算量很大,碍于CPU和Flash的性能,会导致CPU过于繁忙。有些设备碍于成本的原因,Flash太小,很多东西都需要压缩存放,那么在设备启动过程中,解压也需要一定的时间。

  • 程序的原因

  • 代码需要进行大量的IO操作,比如读写文件,内存访问等等,CPU更多的时候处于等待状态。而有些代码,由于编写的原因,导师各个进程之间相互等待,CPU利用率低下,制约了设备的性能。

    优化的原则

    优化并不能盲目的优化,盲目追求性能,还要统筹考虑。一般要遵循以下原则:

    1. 等效性原则

    优化前后的代码实现的功能要完全一致

  • 有效性原则

  • 优化后的代码一定要比原先的代码运行速度快活着占用存储空间小,或者二者兼有,否则就是毫无意义的优化

  • 经济性原则

  • 很多代码性能低下的部分原因也是由于硬件性能的限制,比如将文件压缩存放以节约存储成本。优化要在现有的条件下考虑,不要以更换存储空间的大小来换取解压的时间。优化要付出较小的代价,很多程序员在做优化的时候,抱怨设备的性能有限,要求提高设备的性能,这样只能是本末倒置。

    优化的方法

    此处提出的优化的方法主要是从代码的角度考虑,不包括升级硬件。

    shell 脚本优化

    绝大多数的嵌入式设备都会使用busybox作为实现Linux命令的工具,因此BusyBox提供了一个比较完善的环境,可以适用于任何小的嵌入式系统。

    BusyBox 是一个集成了一百多个最常用linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将BusyBox称为Linux工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了Linux的许多工具和命令,也包含了Android系统的自带的shell。

    BusyBox包含三种类型的命令:

    APPLET

    即为人所熟知的applets,它由BusyBox创建一个子进程,然后调用exec执行相应的功能,在执行完毕后,返回控制给父进程。
    
    • 1

    APPLET_NOEXEC

    系统将调用fork创建子进程,然后执行BusyBox中相应的功能,在执行完毕后,返回控制给父进程。
    
    • 1

    APPLET_NOFORK

    它相当于builts-in,只是执行BusyBox的内部函数,不必创建子进程,所以其效率极高。
    
    • 1

    众所周知,在Linux中调用fork,exec是很耗费时间的,所以我们应该尽可能的使用APPLET_NOFORK命令,其次是APPLET_NOEXEC,最后是APPLET。

    在BusyBox1.9中,属于APPLET_NOFORK的功能有:

    basename,cat,dirname,echo,false,hostid,length,logname,mkdir,pwd,rm,rmdir,deq,sleep,sync,touch,true,usleep,whoami,yes
    
    • 1

    属于APPLET_NOEXEC的功能有:

    awk,chgrp,chmod,chown,cp,cut,dd,find,hexdump,ln,soort,test,xargs......
    
    • 1

    所以优化shell脚本的策略一般有:

    1. 去掉无用的脚本
    2. 尽可能的使用BusyBox内部的命令
    3. 尽量不要使用管道pipe
    4. 减少管道中的命令数目
    5. 尽量不要使用·
    
    • 1
    • 2
    • 3
    • 4
    • 5

    优化进程启动速度

    进程的启动过程如下:

    1 搜索其所依赖的动态库
    2 加载动态库
    3 初始化动态库
    4 初始化进程
    5 将程序的控制权移交给main函数
    
    • 1
    • 2
    • 3
    • 4
    • 5

    要加快的进程的启动速度,可以从以下几方面入手:

    1 减少加载的动态库的数量

    a) 使用dlopen,将启动时不需要的动态库延后加载b) 将一些动态库改为静态库优点:- 减少了加载动态库的数量- 在与其他动态库合并之后,动态库内部的函数之间不必再进行动态链接、符号查找,从而提高速度缺点:- 该动态库如果被多个动态库或进程所依赖的话,那么该动态库将被复制多份合并到新的动态库中,导致整体的文件大小增加,占用更多的Flash。- 失去了动态库原有的代码段内存共享,因此可能会导致内存使用上的增加
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2 优化加载动态库时的搜索路径

    a) 设置LD_HWCAP_MASK,禁掉一些不用的硬件特性b) 将所有的动态库都放在一个目录下,并且将目录放在LD_LIBRARY_PATH的开始c) 不能放在一个目录的,在进程中加入-rpath选项,指定搜索路径
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果做了之前的工作仍然无法满足进程启动速度的要求,那就从进程的调度上下功夫,可以:

    • 进程改为线程

    • 可以把原来的进程分割为两个部分:

    常驻内存部分:其为daemon进程,主要负责加载进程所需要的动态库,侦听用户信号,创建和销毁用户逻辑线程

    完成用户逻辑部分: 由daemon部分创建线程,按用户需求完成用户逻辑

  • 这样就节省掉了加载动态库、初始化动态库和全局变量部分,可以缩短进程的响应时间,来满足用户的需求

  • 还可以再引申一下,将原来的多个daemon进程的常驻内存部分进行合并,根据用户逻辑需求,创建不同的进程。


    • 优点:

    创建线程时,不需要重新加载动态库,故缩短了进程的响应时间

    多个业务逻辑共享动态库时,避免了系统为每个业务逻辑创建动态库的数据段,从而节省了大量的内存。

  • 缺点:

  • 由原来的进程改为线程,工作量比较大,代码修改上存在一定的风险

    多个业务逻辑线程之间共享动态库时,有可能会带来全局变量的冲突

    由于还是存在daemon进程部分,所以其堆栈内存不会被释放,多个业务逻辑线程所存在内存泄露会纠缠在一起,从而使问题更加复杂。

  • preload进程

    • 在进程的main函数中插入一行语句:

    pause();

  • 这样,当进程启动时,加载完动态库后,就会停在这里,不会运行用户逻辑。

  • 当我们需要相应用户时,向该进程发送一个信号,这样用户就会继续前进,处理用户逻辑,这样就节省了进程加载动态库的过程。

  • 这里需要一个信号处理函数:

  • 当用户逻辑执行完成后,就退出进程,同时再启动该进程,这是进程会在加载完动态库后,停留在那里

  •         void sigCont( int unused){return;}int main(int argc, char** argv){signal(SIGCONT,sigCont);pause();}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 提前加载,延后退出

    当进程启动需要较长时间时,很多程序员仅仅想到了将其提前加载(在开机时启动),却没有想到起退出条件,而导致进程中又多了一个daemon进程。 因此提前加载,延后退出需要更加精确的控制进程的生命周期。

  • 调整CPU频率

    • 嵌入式设备中,CPU一般有几个工作频率
    • CPU频率越高,运行速度越快,耗电量越高
    • 可以再启动前调高CPU频率,在完成后再调低CPU频率
    • 这种方法以耗电量增加为代价,在某些场合下不适用

    优化代码

    • if表达式

    从左到右对表达式求值,当结果确定后也就不在需要计算其他的表达式,也就是常说的“短路”机制,因此对于if语句可以做以下优化:

    • 删除冗余条件
    • 删除肯定不成立的条件
    • 利用短路机制,将计算速度最快的表达式放在左边
  • 循环语句的优化

    • 将不变的代码移到循环之外
    • 将分支语句提到循环的外面
    • 通过循环分支的展开,可以降低循环次数,从而减少分支语句对循环的影响
    • 用减1指令替代循环加1指令
    #将分支语句提到循环的外面的例子
    for (i=nloop; i>0; i--)
    {if(n == 1)j += 2;else j += 1;
    }#改为:if (n == 1)
    {for (i=nloop; i>0; i--){j += 2;}
    }
    else
    {for (i=nloop; i>0; i--){j += 1;}
    }############################################################################################################## 展开循环语句的例子#方式1
    for (n = 0; n < 1024*1024; n++)
    {n++;
    }#方式2
    for (n = 0; n < 1024*512; n++)
    {n++;n++;
    }#方式3
    for (n = 0; n < 1024*256; n++)
    {n++;n++;n++;n++;
    }#以上三种方法,方式三所用的时间最短,效率最高
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 寄存器的使用遵循ATPCS标准

    ATPCS标准是嵌入式开发应尽量遵循的标准,主要内容如下:

    • 子程序间通过寄存器R0——R3来传递参数。

    被调用的子程序在返回前无需恢复寄存器R0——R3的内容

  • 在子程序中,使用寄存器R4——R11来保存局部变量。

  • 如果在子程序中使用了寄存器R4——R11的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必进行这些操作。

  • R12用作子程序间scratch寄存器,记作ip。

  • 在子程序间的连接代码段经常使用这些规则

  • R13用作数据栈指针,记作sp。

  • 在子程序间寄存器R13不能用作其他用途。

  • R14成为连接寄存器,记作lr。

  • 它用来保存子程序的返回地址。

  • R15是程序计数器,记作pc。

  • 子程序返回结果为一个32位整数时,可以通过寄存器R0返回;结果为一个64位整数时,可以通过寄存器R0和R1返回,以此类推。

  • 函数参数优化

    • 函数的参数最好不超过4个

    • 4个以下的形参可以通过寄存器来传递,4个以上的参数,则需要通过栈来传递。

    • 同事如果参数小于4个,R0-R4中剩余的寄存器可以保存函数中的局部变量。

  • 减少局部变量的个数

    • 尽量限制函数内部循环所用的局部变量的数目,最多不超过12个,以便编译器能把变量分配到寄存器。
    • 如果没有局部变量保存到栈中,系统也将不必设置和恢复栈指针。
  • 当函数内部寄存器变量多于12个时,并不意味着只是将前面的12个临时变量分配寄存器,之后的临时变量都是通过栈内存来操作。

    • 当寄存器分配完内存后,遇到新的临时变量时,先查看已分配寄存器的局部变量是否有在后面的代码中不会被使用,则新的局部变量使用其所占用的寄存器。
    • 如果也纷纷寄存器的局部变量在后面的代码中都要使用,则要选择出一个临时变量,将其保存到栈中,之后将其使用的寄存器分配给局部变量。
  • 文件操作的优化
    • 读写文件时,缓冲区的buffer为2048或4096时,速度最快。
    • 利用mmap读写文件
      mmap的基本流程是:
      - 创建一个与源文件相同的目标文件
      - 使用mmap,分别将源文件和目标文件映射到内存中
      - 使用memcpy,将文件读写操作转换成内存的拷贝操作
  • 线程的优化

    • 线程的创建是要付出代价的,如果创建的线程只做很少的事情,而又频繁的创建和销毁线程,是得不偿失的
    • 使用异步IO,来取代多线程+同步IO的方式
    • 使用线程池取代线程的创建和销毁
  • 内存操作的优化

  • 内存访问流程
    + CPU试图访问一块内存
    + CPU首先确认该内存是否已经被加载到cache中
    + 如果加载到cache中,则直接在cache中定位
    + 如果未加载到cache中,则通过CPU和内存直接的地址总线,向内存发送地址的高27位地址
    + 当内存收到高27位地址后,利用SDRAM的突发交换模式,将连续的32个字节传送给CPU的cache,填充一个缓存行
    + CPU可以通过地址的高27位来定位cache的缓存行,利用地址的低5位定位到缓存行中具体的字节

    • 尽量使用占用内存少的算法
    • 利用流水线内存存取与计算并行的特点,组合内存访问与计算
  • 调整进程的优先级

    • linux支持两种进程:实时进程和普通进程
    • 实时进程的优先级是静态设定的,而且始终大于普通进程的优先级。对于实时进程来讲,其使用绝对优先级的概念,绝对优先级的取值范围是0——99,数字越大,优先级越高。
    • 普通进程的绝对优先级取值是0.在普通进程之间,其又具备静态优先级和动态优先级之分。静态优先级,我们可以通过程序来修改。同事系统在运行过程中,会在静态优先级基础上,不断动态计算出每个进程的动态优先级,拥有最高动态优先级的进程进程被调度器选中。一般来讲,静态优先级越高,进程所能分配的时间片越长。
    • 尽量不要把某些进程放到启动脚本中,尝试daemon进程在第一次使用时启动。

嵌入式linux启动时间优化相关推荐

  1. 嵌入式linux的调试时间,嵌入式LINUX启动时间优化

    目录: 1. 实践过程 2. 参考帖子 2.1 嵌入式Linux-启动时间优化: 要点如下: 优化第一步: ?? 修改u-boot,把QSPI的读取速度进行提升 优化第二步: ??? 对内核进行裁剪, ...

  2. Linux内核启动速度优化,嵌入式Linux启动时间优化的秘密之五-Bootloader

    描述 本文主要讲述嵌入式Linux启动时间优化的秘密,我们继续上篇没有讲完的嵌入式Linux启动时间优化方法,本文主要会讲Bootloader.想看上一篇的请查看本文结尾的链接. Bootloader ...

  3. Linux脚本5秒后启动程序,嵌入式Linux启动时间优化的秘密之四-启动脚本

    本文主要讲述嵌入式Linux启动时间优化的秘密,我们继续上篇没有讲完的嵌入式Linux启动时间优化方法,本文主要会讲启动脚本.想看上一篇的请查看本文结尾的链接. 启动脚本 1.优化初始化脚本和系统启动 ...

  4. 嵌入式Linux性能优化

    什么是嵌入式系统? 嵌入式系统(Embedded System),是一种嵌入机械或电气系统内部.具有专一功能和实时计算性能的计算机系统.[1][2]嵌入式系统常被用于高效控制许多常见设备,被嵌入的系统 ...

  5. 嵌入式Linux内核优化裁剪

    嵌入式Linux内核优化裁剪                                       优化1.取消虚拟内存的支持 General setup  --->      [ ] S ...

  6. 嵌入式linux+io+优化,嵌入式Linux系统内存优化使用方法研究

    [摘要] 嵌入式系统功能的提高,占用了较大内存空间,继而时常出现运行无响应.基于用户方面看,由于系统内存问题影响运行,针对系统内存与进程应用状态研究,可以调整系统数值与执行文件elf分析,进行系统优化 ...

  7. 端如何访问rc_如何进行 Linux 启动时间优化

    快速启动嵌入式设备或电信设备,对于时间要求紧迫的应用程序是至关重要的,并且在改善用户体验方面也起着非常重要的作用.这个文章给予一些关于如何增强任意设备的启动时间的重要技巧.-- B Thangaraj ...

  8. Linux 启动时间优化实战,2.41 秒启动应用!

    来源于:老吴嵌入式 今天看了一个关于启动优化的讲座,简单总结一下. 本文的目标是尝试一些比较简单有效的方法,并不会覆盖所有的优化技巧. 目标系统 硬件: Beagle Bone Black (Cort ...

  9. Linux启动时间优化-内核和用户空间启动优化实践

    小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货.我有一些面试题.架构.设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复 ...

最新文章

  1. html滚动条样式自定义,CSS3自定义滚动条样式
  2. 使用Ubuntu挂载NTFS格式分区
  3. access找不到输入表或者dual_在Access窗体中显示指定路径的图片
  4. 移动游戏加载性能和内存管理全解析 学习
  5. 和在c语言,?和:在C语言中的详解
  6. 面试被吊打 - Redis原理
  7. oracle技术之Oracle 物化视图(一)
  8. STM32基础定时器详解
  9. eclipse 编辑 python 中文乱码的解决方案
  10. 利用python对图像进行傅里叶变换_Python 实现图像快速傅里叶变换和离散余弦变换...
  11. centos安装python环境_Centos7 安装python3 环境 并使用pip安装docker-compose
  12. submit 读取mb52数据
  13. 小程序对wxParse 使用
  14. Tor源码 -- 启动模块
  15. html中fixed属性,CSSposition属性中:fixed使用详解
  16. 什么时候可以用到强化学习?强化学习怎么用?
  17. cad导出pdf_如何使用CAD手机看图软件将DWG格式图纸文件转换成PDF格式?
  18. 移动互联网草根狂欢:三四线城镇用户价值凸显
  19. Echart柱状图中数据显示在图上方
  20. 【计算机网络】计算机网络

热门文章

  1. 数独 ( 二 ) ——生成数独终局
  2. 计算机c盘一直减小咋办,C盘空间越来越小怎么办有效解决方案
  3. 电脑之间快速传输超大文件(100GB以上)的方法
  4. Android开源框架PowerfulViewLibrary——PowerfulEditText的介绍和源码解析
  5. Word里的图片显示不全,只显示最下面的一小部分的解决办法
  6. Deepin 15.4 如何使用 罗技无线键盘/鼠标(采用优联技术)
  7. 力扣(88.53)补8.31
  8. 随机验证码生成(生成一个含有n位随机数字的字符串)
  9. 安卓投屏神器scrcpy
  10. 给div添加漂亮的边框背景图