2 onps栈移植说明——编译器及os适配层移植

2. 字节对齐及基础数据类型定义协议栈源码(码云/github)port/include/port/datatype.h中根据目标系统架构(16位 or 32位)及所使用的编译器定义基础数据类型及字节对齐方法 。这个文件中最重要的移植工作就是依据目标编译器手册定义字节对齐方法 。因为网络协议栈最关键的地方就是底层通讯报文结构必须字节对齐,而不是通常情形下的缺省四字节对齐 。
#define PACKED __attribute__((packed)) //* 缺省提供了gcc编译器的字节对齐方法#define PACKED_FIELD(x) PACKED x#define PACKED_BEGIN#define PACKED_END协议栈源码提供了常用的gcc编译器的字节对齐方法 。PACKED宏及PACKED_BEGIN/PACKET_END组合体宏通常用于结构体字节对齐定义 。二者选其一实现即可 。PACKED_FIELD宏用于定义单个变量字节对齐 。注意,字节对齐定义是整个协议栈能否正常运转的关键 。所以,必须确保该定义能正常工作 。
协议栈源码提供了32位系统下的基础数据类型定义样例 , 具体移植时可参考该样例进行调整:
//* 系统常用数据类型定义(不同的编译器版本,各数据类型的位宽亦不同,请根据后面注释选择相同位宽的类型定义)typedef unsigned long long ULONGLONG;//* 64位无符号长整型typedef long longLONGLONG;//* 64位有符号长整型typedef signed longLONG;//* 32位的有符号长整型typedef unsigned longULONG;//* 32位的无符号长整型typedef floatFLOAT;//* 32位的浮点型typedef doubleDOUBLE;//* 64位的双精度浮点型typedef signed intINT;//* 32位的有符号整型typedef unsigned intUINT;//* 32位的无符号整型typedef signed shortSHORT;//* 16位的有符号短整型typedef unsigned shortUSHORT;//* 16位的无符号短整型typedef charCHAR;//* 8位有符号字节型typedef unsigned charUCHAR;//* 8位无符号字节型typedef unsigned intin_addr_t;//* internet地址类型其中in_addr_t比较特殊,用于socket编程,其为IPv4地址类型,其必须是无符号4字节整型数 。
3. OS适配层对于 os 适配层,主要的移植工作就几块:1)提供多任务(线程)建立函数;2)提供系统级的秒级、毫秒级延时函数及运行时长统计函数;3)提供同步(互斥)锁相关操作函数;4)提供信号量操作函数;5)提供一组临界区保护也就是中断禁止/使能函数 。os 适配层的移植工作涉及 os_datatype.h、os_adapter.h、os_adapter.c 三个文件 。
3.1 os_datatype.h这个文件负责完成与目标操作系统相关的数据类型定义,主要就是互斥锁、信号量、tty这三种数据类型的定义 。互斥锁用于线程同步,信号量用于线程间通讯,tty则用于ppp模块 。我们需要在这个文件里定义能够唯一的标识它们的访问句柄供协议栈使用 。
typedef INT HMUTEX;//* 线程互斥(同步)锁句柄#define INVALID_HMUTEX -1 //* 无效的线程互斥(同步)锁句柄#if SUPPORT_PPPtypedef INT HTTY;//* tty终端句柄#define INVALID_HTTY -1//* 无效的tty终端句柄#endiftypedef INT HSEM;//* 信号量,适用与不同线程间通讯#define INVALID_HSEM -1//* 无效的信号量句柄注意 , 上面给出的只是一般性定义 , 使用时请依据目标os的实际情形进行调整 。另外,如果你的目标系统不需要ppp模块,HTTY及INVALID_HTTY无须定义 。
源码工程提供的os_datatype.h文件为样例文件 。基于协议栈的通用性考虑,样例文件提供的与os相关的数据类型定义存在冗余 。除上述三种数据类型必须定义外,其它预留的类型如目标系统已提供,建议直接使用目标系统的定义 , os_datatype.h文件中的冗余定义直接注释掉即可;如不存在,则直接使用样例文件中的通用定义即可 。
3.2 os_adapter.h协议栈业务逻辑的完成离不开os的支持,这个文件的主要作用就是提供与os相关的接口函数声明 , 然后在os_adapter.c中实现这些函数 。所以,这个文件中要调整的地方并不多,只有两处 。一个是协议栈内部工作线程控制块:
typedef struct _STCB_PSTACKTHREAD_ { //* 协议栈内部工作线程控制块,其用于线程建立 void(*pfunThread)(void *pvParam); //* 线程入口函数 void *pvParam;//* 传递给入口函数的用户参数} STCB_PSTACKTHREAD, *PSTCB_PSTACKTHREAD;这个结构体与目标os高度相关 , 其用于保存协议栈内部工作线程列表 。协议栈内部设计了一个one-shot定时器 。该定时器被用于一些需要等待一小段时间才能进行后续处理或定期执行的业务模块 。这个定时器是以线程的方式实现的 。协议栈的核心业务逻辑均与这个one-shot定时器线程有关 。协议栈被目标系统加载时该线程将由os_thread_onpstack_start()函数自动启动 。这个函数要启动的线程列表就被保存在STCB_PSTACKTHREAD结构体数组中 。这个数组是一个静态存储时期的变量 , 变量名为lr_stcbaPStackThread,在os_adapter.c中定义 。STCB_PSTACKTHREAD结构体需要定义哪些成员变量由目标os提供的线程启动函数的入口参数决定 。我们会将线程启动用到的入口参数值定义在lr_stcbaPStackThread数组中,然后由os_thread_onpstack_start()将这些参数值传递给线程启动函数启动相应工作线程 。

推荐阅读