contiki有三种分配内存的方式。
memb 内存块分配器,在contiki中使用最频繁
mmem 管理内存分配器,很少去使用
标准C库的malloc的栈内存分配器,在contiki这种受限系统中使有最危险。
有下列的API:
MEMB(name, structure, num) : 定义memory block.
void memb_init(struct memb *m) : 初始化memory block.
void *memb_alloc(struct memb *m) : 分配memory block.
int memb_free(struct memb *m, void *ptr) : 释放memory block.
int memb_inmemb(struct memb *m, void *ptr) : 判断ptr传入的地址是否是memory block.
MEMB做了哪些事情
#define MEMB(name, structure, num) \ static char CC_CONCAT(name,_memb_count)[num]; \ static structure CC_CONCAT(name,_memb_mem)[num]; \ static struct memb name = {sizeof(structure), num, \ CC_CONCAT(name,_memb_count), \ (void *)CC_CONCAT(name,_memb_mem)} struct memb { unsigned short size; unsigned short num; char *count; void *mem; };
主要是定义了mem block.
其中CC_CONCAT这个宏把两个参数连接成为一个字符串.
name表示要定义的这个memblock的名称.
num表示memblock的数量,但是在struct memb中把num存储的大小定义为unsigned short型,也就是说,最多存隼128个memblock的对象
strructure参数表示需要扩展内存的结构体.
先定义出记录mem block对象的指针,和内容.然后再把信息记录在memb的结构体中.记录内存的状态跟分配内存是在同一时间完成的.
其中定义了两个数组
count:表示这段内存空间有没有被使用(0表示没使用,1表示已经使用),首地址memb->count
mem:实际的结构体指针存放的数组,首地址memb->mem,其它元素访问memb->mem+i*memb->size
memb->size是存放的sizeof(structure)的值.
num就表示分配数量,实际上mem的总大小为memb->size * memb->num
另外要注意的是,MEMB开辟的内存空间都是用static定义的,count和mem这两个数组由于没赋初值,放在.bss段里,但是memb name赋了初值,放在.data段里.众所周知,传统的C库中的malloc一系列的函数,是在heap中存放开辟的空间的.
由于内存受限系统不能去开辟一段自由使用的空间,而且contiki中的轻量级线程也没有自己的堆和栈.所以放在data或bss段中是比较好的办法.
memb_init
把已经开辟的空间里的所有值都设为0.用memset实现.
memb_alloc
从memb->count[0]开始往后找大小为mem->size的内存块,
如果memb->count[i]标记为1,则这个区块已经使用,继续向后找.
如果标记为0,则把区块分配出去.返回memb->mem +i*memb->size
如果返回NULL,则标记为0.
memb_free
有两个参数,一个是memb结构体的参数,另一个是要free掉的指针.
函数的实现流程是,在memb->mem数组中去掉传入的指针.如果找到.则把memb->count[i]标记为0.然后返回0
没找到这个指针,则返回为-1.
memb_inmemb
去查找传入参数是否是memb分配的地址.也就是看传入参数是否落在memb->mem的区间中.若是返回1,不是返回0.
下面是一个调用API的示例
struct connection { int socket; }; MEMB(connections, struct connection, 16); struct connection * open_connection(int socket) { struct connection *conn; conn = memb_alloc(&connections); if(conn == NULL) { return NULL; } conn->socket = socket; return conn; } void close_connection(struct connection *conn) { memb_free(&connections, conn); }
mmem管理内存分配器
模型:
mmem(managed memory allocator)
实现的文件是core/lib/mmem.c及对应的头文件.
中间涉及一个结构体:
struct mmem { struct mmem *next; unsigned int size; void *ptr; };
大体的思路,还是用static类型的全局变量.是字符型的数组.数组大小默认4096,也就是4096字节.这个参数可用MMEM_CONF_SIZE进行配置.
然后mmem_init()初始化上图所示的链表.
mmem_alloc()则是分配一段连续的内存.然后在链表中记录下来.
其中用了一个技巧.
/* Check if we have enough memory left for this allocation. */ if(avail_memory < size) { return 0; } /* We had enough memory so we add this memory block to the end of the list of allocated memory blocks. */ list_add(mmemlist, m); /* Set up the pointer so that it points to the first available byte in the memory block. */ m->ptr = &memory[MMEM_SIZE - avail_memory]; /* Remember the size of this memory block. */ m->size = size; /* Decrease the amount of available memory. */ avail_memory -= size;avail_memory是一个全局变量.MMEM_SIZE是分配的内存空间的总大小.则MMEM_SIZE-avail_memory就是还没有分配的内存的起始的数组索引.
这也意味着,在这段内存空间里,已分配的内存始终在前面,未分配的内存始终在后面.这样做的话,也有合理性.在内存分配时,不存在内存碎片.
mmem->size是当前分配内存的大小. mmem->ptr是当前分配内存的起始地址.
mmem_free(),有一个参数,即当前分配内存的mmem结构体.实现是,利用memmove,把后面已分配内存的空间的数据覆盖掉刚才要free掉的内存.然后再从链表中删除要free的mmem的记录.
static struct mmem mmem; static void test_mmem(void) { struct my_struct { int a; } my_data, *my_data_ptr; if(mmem_alloc(&mmem, sizeof(my_data)) == 0) { printf("memory allocation failed\n"); } else { printf("memory allocation succeeded\n"); my_data.a = 0xaa; memcpy(MMEM_PTR(&mmem), &my_data, sizeof(my_data)); printf("Value a equals 0x%x\n", ((struct my_struct *)MMEM_PTR(&mmem))->a); mmem_free(&mmem); } }
示例代码,没有分析的必要了.
这种操作在中断,多线程及抢占式的代码存在的情况下,极不安全.所以一般只是在contiki的进程中使用.如果获取分配的内存地址,只能用MMEM_PTR()这个宏.
在contiki系统中,可以在platform下面的初始化代码时提供mmem_init.
platform/iris/contiki-iris-main.c有对应的例子.可以看一下.
第三种分配内存的方法即C库里的malloc, calloc等函数.但是在contiki内存受限.不会分配到连续的内存,而且也要开足heap区间.对contiki来说,不是一个好的选择.
memb的话,属于静态的分配内存.mmem属于动态分配内存.
但是现在的contiki系统中通用的做法是静态分配内存.好的方法应该是动态去分配.
对于NFC代码编译,目前为止,我见过两家不同方案商的书写方式,都是在device下的XXXX.mk文件中书写,XXXX不同的方案商不同的产品名称不同。下面具体来看:
方式A:
PRODUCT_COPY_FILES += \ frameworks/native/data/etc/android.hardware.nfc.xml:system/etc/permissions/android.hardware.nfc.xml
# NFC packages PRODUCT_PACKAGES += \ libnfc \ libnfc_jni \ Nfc \ Tag
方式B:
#NXP NFC PRODUCT_COPY_FILES += \ frameworks/native/data/etc/android.hardware.nfc.xml:system/etc/permissions/android.hardware.nfc.xml
PRODUCT_PACKAGES += \ nfc.XXXX \ libnfc \ libnfc_jni \ Nfc \ Tag
对比这两种方式,可以很明显发现其中的不同点nfc.XXXX,下面就两者的不同之处在分析:
nfc.XXXX 是nfc_hw.c 编译生成的nfc.XXXX.so包,在out/目录下。方式A中并没有把nfc.XXXX放进去,它放在modules.mk中编译。nfc_hw.c不同的方案也具体放在不同的位置。总之,它的目的是生成nfc.XXXX.so以供调用。
除了上述之外的东西,还需要在init.rc中配置:
# NFC setprop ro.nfc.port "I2C"至此,NFC编译部分都配置完毕了。
通过以上的分析,NFC 在android 中的code 结构都差不多出来了,下面分析下:
客户端:android提供了两个API包给apk,分别是android.nfc.tech、android.nfc,实现了NFC的应用接口,代码路径frameworks/base/core/java/android/nfc/tech、frameworks/base/core/java/android/nfc。
服务端:packages/apps/Nfc是一个类似电话本的应用,这个程序在启动后自动运行,并一直运行,作为NFC的服务进程存在,是NFC的核心。
在这个程序代码中,有个JNI库,供NfcService调用,代码路径是packages/apps/Nfc/jni/.编译文件中,还有个Tag部分,代码路径是packages/apps/Tag.
库文件:代码路径是external/libnfc-nxp,用C编写的库文件,有两个库,分别是libnfc.so和libnfc_ndef.so。libnfc.so是一个主要的库,实现了NFC stack的大部分功能,主要提供NFC的服务进程调用。libnfc_ndef是一个很小的库,主要是实现NDEF消息的解析,供framework调用
nfc_hw.c:这个文件的具体路径不确定。 pn544.c:这个文件是具体nfc芯片的驱动,一般都是drivers下。
由于本人初学,能力有限,有错误的地方欢迎指出。
参考文章:
NFC framework introduce(一)vlc从整体框架上来说,就是设计了一套module管理机制,将功能分类并抽象成modules。例如播放器相关的如demux decoder access output这些都成了vlc的modules。新添加一个功能(新的媒体容器格式demux、新的解码器、新的编码器)只要遵循这套机制,自己完成这个子module就可以了。所以说vlc依赖的是这些modules。没有modules其实啥也干不了。
libVLCcore: libvlc的核心,抽象出了一个libvlc_instance_t 对象。modules的装载/卸载机制。还有一套多媒体相关的操作接口。如:input, multiplexing, demultiplexing, audio output, video output.
modules:modules提供了具体的功能。根据编译配置modules数量200-400之间。根据功能的分类可分成输入输出(文件、网络、cd)、编解码(mp3、264
)、模块化的GUI(基于qt、mac)
External libraries:开源软件都在秉承着不重复造轮子的思想。所以vlc本身也依赖了大量的外部开源库,如知名的ffmpeg、qt、live555等。
外部库列表
VLC main:player的main。初始化libVLC 并加载用户界面,注意vlc的用户界面也是已module的形式存在。
基于libvlc 我们就能自己开发自己的播放器了。
modules
按照功能大致分类
vlc 也提供了一个模块列表 点击打开链接