微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 教你轻松控制uClinux 嵌入式开发过程

教你轻松控制uClinux 嵌入式开发过程

时间:04-19 来源:互联网 点击:

page_alloc2能解决缺省的分配方法造成的浪费问题。虽然它也是使用“2的幂”的分配方法,但它是按页(每页4096字节,即4KB)分配的,分配的内存大小如果已经满足了要求,则只是将当前的一页分配出去,其它的就不再分配。在前面的例子中,如果使用这种方法,就只是分配36KB (≥33KB,且为整页)即可,这样就能节省28KB的空间。

  page_alloc2还采取了一些避免内存碎片的方法。它将所有的两页(8KB)或更少的内存需求从空闲内存开始部分向上分配,所有大的内存需求从剩余内存的末尾部分开始向下分配。这样防止了网络缓存等的临时分配,避免了内存碎片的出现。

  一旦开发者理解了内核内存分配的区别,应用程序中就会出现变化。

  1.没有动态栈的问题

  在使用虚拟内存的Linux上,当一个应用程序试图冲销栈顶单元时,会被标记异常,同时系统会映射新的内存到栈顶以便让栈增长。在 uClinux下,由于必须在编译阶段给栈分配好内存,所以不会有这样的增长。当出现莫名其妙的崩溃或者新移植的应用程序出现怪异行为时,开发者首先应该考虑到的是给栈分配的内存大小问题。缺省情况下,uClinux为栈分配4KB的内存空间,开发者可以用下面提到的方法之一来增加栈的空间。

 ◆ 应用程序build之前

  应用程序build之前,可以在Makefile文件中增加以下两行代码:

  FLTFLAGS = -s

  export FLTFLAGS

  ◆ 应用程序build之后

  应用程序build之后,可以运行以下命令:

  flthdr -s executable

  其中,stacksize 就是为栈增加的内存空间。

  2.没有动态堆的问题

  堆是C语言中malloc及相关函数分配内存的区域。在有虚拟内存的Linux上,应用程序可能通过动态堆在运行过程中改变进程的大小。这个功能是通过在底层使用sbrk()和brk()系统调用来实现的。sbrk()是在进程的末尾增加内存空间,所以调用sbrk()能够使应用程序获得额外的内存。

  brk()可以把任意位置设置为进程空间的末尾,因此,可以通过调用brk()减少或增加内存空间的占用。由于uClinux不能实现brk()和sbrk(),它采用了一个全局的内存池,就是内核的空闲内存池。使用全局内存池的方法有一些优点。

  首先,此方法只会给进程分配使用时真正需要的内存。其次,内存用完后就会被归还给全局内存池,而且可以利用已经存在的内核中的分配器来分配内存,这样可以减少应用程序的代码量。但这个方法是有缺陷的,比如,一个失控的进程可以用完系统全部的可用内存。

  新手普遍会遇到丢失内存的问题。系统会显示大量的可用内存,但是应用程序却不能得到。这正是由于内存碎片的存在,uClinux几乎不可能完全利用内存,现有的解决方法中都存在这个问题。这个问题可用一个例子很好地说明。

  假设一个系统有500KB的空闲内存,为了装载一个应用程序需要分配100KB的空间。大家可能觉得这个需要肯定能得到满足,然而,应该知道,必须有 100KB连续的内存空间才能满足这个需要。如果有500KB的空闲空间,但是最大的连续内存块的大小只有80KB,这样是没有办法分配给这个应用程序的。造成这种情况有很多原因。上面讲到的page_alloc2内核分配器有一个配置选项可以用来识别这个问题,在内核源代码page_alloc2.c 文件中可以获得更多的信息。

  经常有人会问为什么不能进行内存的碎片整理,以便实现刚才的例子中的要求?原因是uClinux没有虚拟内存,所以不能移动程序正在使用的内存。在使用虚拟内存的情况下,只要重新定位就能实现内存的移动,从而实现内存碎片的整理。

  在没有虚拟内存的情况下,由于程序经常会引用已经分配给它的内存区域,这样,如果移动程序的内存,程序就会崩溃。在uClinux下,现在还没有解决这个问题的办法。开发者需要自己注意这个问题,如果有可能的话,尽量使用小的内存块。

  掌控进程和应用程序

  1.进程

  有虚拟内存的Linux和uClinux的另一个区别在于后者没有fork()系统调用。这就要求开发者在移植时对使用了fork()的应用程序做一些工作。uClinux下惟一的选择是使用vfork()。尽管vfork()与fork()有很多共同点,但是它们之间的区别影响很大。

  对于不熟悉fork()和vfork()的人来说,这两个系统调用都是允许将一个进程分裂成一个父进程和一个子进程。当一个进程调用 fork()时,子进程是父进程的一个完全拷贝,但是它不共享父进程的任何东西,并且能够单独执行,就和父进程一样。vfork()调用就不同了,首先,父进程被挂起直到子进程调用exec(),或者子进程退出才能继续。

由此可见,这个系统调用是用来启动一个新的应用程序。其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top