1.CSPL架构简述
CSPL的定位和目的:
- CSPL位于业务与操作系统之间
- 屏蔽操作系统的复杂接口,提供统一的操作方式,使得业务进程只需要关心本身业务,从而加速协议栈的开发
- CSPL核心是以lib库(动态库、静态库)的形式提供给业务,同时为了支撑业务,提供多个进程支撑;DAEMON、LOG、CLI、HA
CSPL | Core Stack Porting Layer | 核心系统接口层 |
---|---|---|
OSAL | Operating System Adapter Layer | 操作系统适配层 |
任务管理 | 任务调度管理等功能,提供统一的运行调度机制 | |
内存管理 | 提供内存池,高端内存等功能 | |
消息通信 | 提供任务间的消息通信处理等功能,如UDP、TCP、消息队列等 | |
系统调试 | 提供Log、信令跟踪、黑匣子、运行时调试,性能统计等功能 | |
事件管理 | 提供事件服务器,支持事件订阅、发布操作 | |
其他功能 | 提供定时器,数据库,CLI调试等功能 |
2.CSPL设计思路
2.1模型抽象
2.1.1数据结构
QMANIFEST
每一个协议栈实例(模块)由QMANIFEST数据结构来表示,CSPL通过此数据结构来和协议栈交互, CSPL_RegisterModule函数使用此结构体向CSPL注册module
1 | typedef struct QMANIFEST{ |
cspl_address_t
每一个协议栈路由信息用cspl_address_t数据结构来表示,CSPL_Open内部通过此数据结构来配置本地接收和发送的路由(即创建相应的fd, 并加入到epoll中)
1 | typedef struct{ |
qvars
qvars结构体用于描述driver,当模块未设置立即发送标志时,hold队列用于存放所有的发送消息,当本driver调度运行时,会将所有hold消息都发送出去。
1 | typedef struct qvars{ |
QSHELL
QSHELL结构体用于适配不同的操作系统, 属于OSAL层。使用多路复用IO模型,内部使用epoll完成对多路事件的监控。
1 | struct QSHELL{ |
QWAIT
QWAIT结构体用于适配不同的操作系统, 属于OSAL层。使用条件变量+互斥锁,完成driver的睡眠与唤醒操作。
1 | struct QWAIT{ |
walltime内部使用clock_gettime(CLOCK_MONOTONIC, &ts)获取系统时间,此时间为单调递增时间,避免系统时间修改后, 对定时器造成影响。
newchannel中创建的条件变量使用函数pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);来避免系统时间修改所造成的影响
QSYSOP
QSYSOP结构体用于适配不同的操作系统, 属于OSAL层。使用linux线程局部存储设施来存放每个线程都具有的信息
1 | typedef struct{ |
2.1.2运行模式
单线程
一个domain(进程)只有一个driver,所有module(协议栈模块)运行在一个driver内
driver驱动协议栈运行,同时负责对外通信(收发消息)
多线程
一个domain有多个driver,module(协议栈模块)分散运行在多个driver上
domain内有一个主driver负责对外通信(收发消息)
多线程+IndependDriver
一个domain有多个driver,module(协议栈模块)分散运行在多个driver上
domain内有一个主driver负责对外通信(收发消息)
domain内还有独立的driver可以单独接收外部数据
2.2通信机制
CSPL提供消息循环,各协议模块(module)之间基于消息进行通信:
- 内部模块(进程内)之间通过线程调度通信。
基于链表实现,直接传递消息指针,消息处理支持零拷贝,引用计数等特性。 - 与外部模块通信基于OS的IPC机制,比如socket。
采用钩子注册机制,可以选择UDP、TCP、Linx、消息队列等具体机制。 - 对每个外部模块可以单独注册通信函数。
对每个外部模块可以选择不同的通信方式,比如板内选择消息队列,板间选择UDP socket。
CSPL通讯抽象为driver内通信,driver之间通信,domain(进程)之间通信。
driver有发送消息(hold)和接收消息(queue)优先级队列,qvSchdule运行时,从接收消息队列上取出消息进行处理(qmodule->messageservice), 从发送队列取出消息发送出去(qmodule->dispatch,仅在非立即发送模式下)
2.3消息格式
有效载荷长度:loadSize = payload + CSPL_HEADER_SIZE
2.4内存管理
CSPL_AllocMsg:用于申请cspl消息内存结构
- 接收cspl消息
使用此函数申请内存,将接收到的cspl消息(cspl header + payload)打包后, 投递到目的module所对应的driver接收优先级队列中 - 发送cspl消息
在此函数的返回值位置后面构造cspl header、填充消息payload后, 使用CSPL_SendMsg将消息发送到目的module
2.5启动流程及默认模块
CSPL_Init初始化时,会创建一个独立的driver,注册到此driver上的模块及功能如下:
monitorEntity模块
初始化定时器,每隔5s遍历所有driver,获取每个driver上发送、接收队列中所有消息的数目;若有超时的消息,则回调msgTimeoutCallback接口函数;若业务有消息或者定时器消息阻塞,则回调userDeadLoopCallback接口函数;若接收队列消息数目>maxPendRecvMsg,则回调driverMsgOverLoadCallback接口函数;最后回调totalMsgOverLoadCallback接口函数。debugEntity模块
调用以DebugInfo结尾的调试函数,并回传执行结果(重定向)到cliDebug进程;全局变量打印及修改(宏和const变量除外)。haEntity模块
用于主备切换。eventEntity模块
用于实现事件初始化、发布、订阅、分发功能。
事件客户端、服务器代码运行于此模块中。
2.6版本信息
- 查看版本号
1
2strings libcspl_interface.a | grep V10
V10R02CA1.B002 - 查看编译时间
1
2strings libcspl_interface.a | grep csplBuildTime
csplBuildTime: 2017-03-28 14:16:07
3.CSPL组件
3.1事件服务
CSPL提供一种事件服务机制,任何业务都可以作为事件发布者和事件订阅者。
事件初始化
业务使用CSPL_EventInit初始化事件,事件服务端记录所有的事件订阅信息。事件订阅
业务使用CSPL_EventSubscribe订阅事件,当事件发布者在某个时候发布此事件后,
订阅者就会eventEntity模块中回调已注册的回调函数。事件发布
业务使用CSPL_EventPublish发布事件,当事件类型不为进程内类型时,事件会被发送到事件服务器,之后事件服务器再转发此事件到订阅此事件的所有业务进程中。事件服务器
位于lte_log进程中的eventEntity模块内,负责管理事件的订阅、取消订阅、事件转发操作。
心跳机制及订阅恢复机制: 事件服务器每隔3s就会向所有已经注册的事件客户端发送一次心跳事件;若客户端连续超过3次没有收到心跳,重新向事件服务端注册业务地址。当重新注册成功,恢复心跳,则遍历客户端中LocalDatalist链表,将已订阅的事件重新向服务端订阅。(事件客户端、服务器均运行在cspl中的eventEntity模块内)
3.2日志服务
日志和注册日志;日志信息保存为本地文件,支持日志文件加密,压缩
CSPL_Log输出普通日志信息,保存到公用的日志文件中,如log.dat
CSPL_Slog输出的注册日志信息,保存到指定的日志文件中,由cspl_config.xml中registerLogCfg标记配置支持设置日志过滤条件
可按照模块ID、日志级别、进程名称来过滤日志实时输出到本地终端、tcp client显示(远端实时显示)
PC端日志服务器可通过tcp连接到lte_log上,实时获取日志信息,并保存到磁盘上自定义类型日志-内存日志
CSPL_MemLog可自定义日志的消息格式,满足业务特殊的需求,由cspl_config.xml中memLogCfg标记配置当日志文件大小超出配置值时,自动创建对应的压缩文件,并控制压缩文件的数量在配置值范围内
事件服务器目前部署在日志进程内
dataProcess | 此线程处理tcp的链接,客户端请求获得日志实时数据 |
---|---|
cfgProcess | 此线程处理tcp的链接,解析传递过来的命令,实现查询日志记录;查询、设置当前过滤规则;查询、设置实时开关等配置功能 |
loopProcess | 处理CSPL_Log、CSPL_Slog等相关函数产生的日志、注册日志,并依据实时开关配置决定是否实时输出日志信息;保存日志信息到磁盘上 |
mem_log_proc | 处理CSPL_MemLog内存日志信息;保存内存日志到磁盘 |
log_tarSystemCmd | 当日志文件大小大于配置值时,压缩日志文件,并记录压缩文件个数,删除超出maxSaveCounts(压缩保存文件最大个数)之外的压缩文件 |
cspl默认线程 | 处理cspl相关的一些操作, 如事件服务器等 |
clidebug默认线程 | 处理clidebug相关的一些操作,启动clidebug服务器 |
3.3cliDebug
clidebug用于在被调试进程运行时调试此进程,获取被调试进程的调试信息、内部运行状态等相关信息;clidebug配置文件为cspl_cliDebug_config.xml
- clidebug在连接被调试进程时,被调试进程必须已经在运行状态,必须明确被调试进程函数名称、调试ip、端口信息(若未指定,则使用配置文件中的配置值)
1
clidebug -t 127.0.0.1:62001 -p lte_oam
- 调用基本命令
1
如ps、ls、ifconfig、pwd、cd、ping、quit、file、kill、help 、log_tool等
- 历史记录,可调试状态下支持20条命令记录,上下键切换
- 全局变量打印及修改(宏和const变量除外)
- 调用cspl自身维测接口(尚在开发中)
1
cspl_timer、cspl_event等
- 调用业务进程通过CSPL_CliDebug_RegCmd自行注册的函数或程序中以DebugInfo结尾的函数
- clidebug可以以多实例的方式
3.4黑盒子
主要用于业务进程异常崩溃时记录现场堆栈信息,为定位问题提供分析依据。
支持的异常信号如下:
信号 | 类型 |
---|---|
SIGSEGV | 段错误 |
SIGFPE | 运算错误 |
SIGILL | 非法指令 |
SIGABRT | 终止 |
SIGBUS | 总线访问异常,部分CPU在访问未对齐地址报错 |
输出内容如下:
- 异常原因(信号名)。
- 进程标示(进程pid,进程名)
- 异常发生时间
- 异常指令地址,异常访问的非法地址(仅对段错误有效)
- 寄存器数据,寄存器显示寄存器名字,如EAX,EBX等
- 异常调用栈,以及函数行号(优先使用dladdr 解析,若dladdr 解析失败,则使用addr2line解析行号)
- 栈中部分数据
3.5守护进程
lte_daemon进程配置文件为cspl_confing.xml, 其功能如下:
启动所有启动类型不为START_BY_SELF的SYS_APP(如lte_log)、USER_APP(业务)进程
等待子进程进入运行状态(最长等待时间: 500 ms * 120)
收集所有进程的CSPL_SIG_RUN_SYNC信号,进而判断进程是否已经进入运行态(APP_RUNNING);并发送CSPL_SIG_RUN_SYNC给所有进程,完成进程状态双向确认。进程保活(心跳信号)状态监控
注册daemonEntity模块监控进程心跳信号,若超出配置3次没有连续接收到心跳信号(CSPL_SIG_KEEPALIVE),则依据restartMode的配置进行相应处理。喂狗
看门狗使能的前提下创建喂狗线程daemon_feedWatchDog(高优先级线程), 每隔2 s进行一次喂狗操作:bsp_watchdog_feed(g_daemonConfig.feedDogInternel);监控所有运行的进程
创建监控线程daemon_monitorApp,执行daemon_WaitApp函数,一旦有业务进程退出,依据restartMode的配置采取动作:- 重启此进程
- 重启所有进程
- 重启系统
- 忽略不做处理。
记录重启进程的次数,当重启次数 > maxRestartNum时, 强制重启系统
lte_daemon中的信号及日志输出
信号 | 说明 |
---|---|
CSPL_SIG_KEEPALIVE | 进程保活心跳信号; |
CSPL_SIG_START_SYNC | 进程启动同步信号; 若进程配置项startSync打开,则lte_damon在拉起此进程后,会循环检测此进程的startFinishFlag标志是否置位; 被拉起的进程在启动后通过向lte_damon进程发送CSPL_SIG_START_SYNC信号来置位此标志(CSPL_SendStartSyncAck函数) |
CSPL_SIG_RUN_SYNC | 进程运行同步信号; lte_daemon进程与所拉起的进程在启动后,互相发送此信号,同步双方进程运行状态 |
CSPL_SIG_DAEMON_REBOOT | daemon重启信号; lte_daemon接收到此信号后,调用daemon_reboot()函数重启系统 |
3.6主备倒换
Opensaf简介: Opensaf是一个高可靠性分布式系统中间件,介于操作系统和用户应用层之间,屏蔽下层操作系统接口,向上提供各种服务。
CSPL集成Opensaf
- Amf作为CSPL进程中的一个线程执行,对业务代码不可见。
- CSPL只向业务提供有限的对外API和数据结构来实现Amf功能。
- CSPL需要重新封装Amf和checkPoint功能,将两者结合起来使用,使用状态机的方式来处理。
基站主备倒换场景
主控板通过浮动IP与基带板交互,如上图,主控板的浮动IP为10.11.1.130,开始在CB1上设置,当发生主备倒换,CB1上关闭浮动IP,在CB2上设置浮动IP。相应的OAM会通知基带板的薄平台,基带板上有两个网卡,薄平台需要控制始终与当前主用主控板连接的网卡设置同一个IP地址,比如开始时设置的Eth2网卡IP地址为192.168.1.4,此时Eth3关闭,当收到OAM的切换通知,关闭Eth2,在Eth3上设置IP 192.168.1.4。
3.7常用算法
CSPL中常用算法库位于目录cspl/src/core/ylib/src中:
位图算法
位于ylib-bitmap.c文件中;cspl中定时器的超时时间排序使用此算法,用于获取距离当前时间最短的定时器超时时间间隔。crc算法
位于ylib-crc.c文件中;8位、16位、32位crc算法哈希算法
位于ylib-hash.c文件中;根据关键码值(Key value)而直接进行访问的数据结构。即通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。双链表算法
位于ylib-list.c文件中;cspl内部大量使用,如在qvars.modules双链表用于遍历此driver下所有的module信息单链表算法
位于ylib-pool.c文件中;cspl中内存池qpool. list单链表用于链接所有qbuf结构体优先级队列算法
位于ylib-squeue.c文件中;cspl中driver中的接收、发送队列中压入、弹出cspl消息时均使用此算法。红黑树算法
位于ylib-tree.c文件中;一种自平衡二叉查找树,典型的用途是实现关联数组,获得较高的查找性能。cspl中QMODULE qvGetService( unsigned long name )函数使用了此算法,通过对qvcontext. services(YTREE)红黑树遍历来获取name所指向的qmodule结构体指针
3.8数据库
基于SQlite数据库。 SQLite是一个嵌入式数据库,和其他嵌入式DB(FastDB, extremeDB) 相比,DML、DDL操作均支持SQL语句,即最终提供给SQLite执行的是一个SQL语句,而不是一个数据结构。
SQlite数据类型:NULL、INTEGER、REAL、TEXT、BLOB
基站数据类型:8/16/32/64位有无符号整型、时间/IP/数组/枚举多选/密码/数据/文本类型、枚举/布尔型
数据库标识:数据库链接
表标识:表名和表ID(映射关系)
字段标识:字段名和字段ID(映射关系)
应用程序和DBS的数据传递一条记录的时候,仅仅包括应用指定的字段值。
包括以下场景:插入,查询,删除,更新。