onps栈的移植涉及几个部分:1)系统配置及裁剪;2)基础数据类型定义;3)RTOS适配层实现;4)编写网卡驱动并注册网卡。本文作为onps栈移植的指导性文件将给出一般性的移植说明及建议,具体的移植样例工程及说明请移步码云下载:

关于onps栈的前世今生请移步上一篇博文《开源网络协议栈onps诞生记》。

1. onps栈的配置及裁剪

协议栈源码(码云/github)port/include/port/sys_config.h文件是协议栈的配置文件。它提供了一系列的配置宏用于裁剪、配置协议栈。我们可以根据目标系统的具体情况对协议栈进行裁剪,调整配置,以减少或增加对系统资源的占用率。配置文件主要涉及几方面的内容:

1)打开或关闭某个功能模块;

2)指定mmu(内存管理单元)管理的内存大小;

3)协议层相关配置项,如缺省ttl值、路由表大小、arp缓存表大小等;

onps栈在数据链路层支持两种类型的网络接口:ethernet有线以太网络接口;ppp点对点拨号网络接口。用户必须选择其中至少一个接口:

#define SUPPORT_PPP      1 //* 是否支持ppp:1,支持;0,不支持
#define SUPPORT_ETHERNET 1 //* 是否支持ethernet:1,支持;0,不支持

注意,你的目标系统要么支持ppp,要么支持ethernet,要么二者都支持,不能两个都选择不支持,否则协议栈将无法正常工作。另外协议栈还提供了几个常用的网络工具供用户选择使用,用户可以根据具体应用情形选择打开或关闭相关工具:

//* 网络工具配置项,0:不支持;1:支持,协议栈将编译连接工具代码到目标系统
//* ===============================================================================================
#define NETTOOLS_PING 1 //* ping工具,确定目标网络地址是否能到达
#define NETTOOLS_DNS_CLIENT 1 //* dns查询客户端,通过指定的dns服务器查询请求域名对应的ip地址
#define NETTOOLS_SNTP 1 //* sntp客户端,通过指定的ntp服务器进行网络校时
//* ===============================================================================================

考虑协议栈的目标系统可能无法提供pc下常见的文件存储系统,所以协议栈的调试日志等信息是通过标准输出提供的:

#define SUPPORT_PRINTF 1 //* 是否支持调用printf()输出相关调试或系统信息
#if SUPPORT_PRINTF
#define PRINTF_THREAD_MUTEX 1 //* 是否支持使用printf线程互斥锁,确保不同线程的调试输出信息不被互相干扰,值为1则支持互斥锁
#define DEBUG_LEVEL 1 //* 共5个调试级别:
//* 0 输出协议栈底层严重错误
//* 1 输出所有系统错误(包括0级错误)
//* 2 输出协议栈重要的配置、运行信息,同时包括0、1级信息
//* 3 输出网卡的原始通讯通讯报文(ppp为收发,ethnernet为发送),以及0、1、2级信息
//* 4 输出ethernet网卡接收的原始通讯报文,被协议栈丢弃的非法(校验和错误、通讯链路不存在等原因)通讯报文,以及0、1、2、3级信息(除ethernet发送的原始报文)
#endif

基本上所有单片机系统均会提供几个串行口,我们只需选择其中一个将其作为printf函数的标准输出口,我们就可以使能协议栈支持日志输出功能,通过printf()函数输出的日志信息对目标系统进行调试。如果你的目标系统支持某个串口作为printf()函数的标准输出口,建议将SUPPORT_PRINTF宏置1,打开协议栈的日志输出功能。PRINTF_THREAD_MUTEX宏用于解决多线程环境下日志输出的冲突问题。如果你的目标系统互斥资源够用,建议打开该功能,否则你在标准输出口看到的日志会出现乱序问题。

协议栈在很多情形下需要动态申请不同大小的内存以供接下来的逻辑处理过程使用。所以,为了最大限度地提高协议栈运行过程中的内存利用率并尽可能地减少内存碎片,我们还单独设计了一个独立的内存管理单元(mmu)。考虑协议栈的目标系统为资源受限的单片机系统,这种系统的内存资源往往都是极度紧张的,因此我们提供了配置宏让用户决定分配多少字节的内存空间给协议栈的mmu:

//* 内存管理单元(mmu)相关配置项,其直接影响协议栈能分配多少个socket给用户使用
//* ===============================================================================================
#define BUDDY_PAGE_SIZE 32 //* 系统能够分配的最小页面大小,其值必须是2的整数次幂
#define BUDDY_ARER_COUNT 9 //* 指定buddy算法管理的内存块数组单元数量 #define BUDDY_MEM_SIZE 8192 //* buddy算法管理的内存总大小,其值由BUDDY_PAGE_SIZE、BUDDY_ARER_COUNT两个宏计算得到:
//* 32 * (2 ^ (9 - 1)),即BUDDY_MEM_SIZE = BUDDY_PAGE_SIZE * (2 ^ (BUDDY_ARER_COUNT - 1))
//* 之所以在此定义好要管理的内存大小,原因是buddy管理的内存其实就是一块提前分配好的静态存储时期的字节型
//* 一维数组,以此来确保协议栈不占用宝贵的堆空间
//* ===============================================================================================

协议栈的内存管理单元采用了buddy伙伴算法。上述三个宏的关系参见BUDDY_MEM_SIZE宏的注释。前面说过,mmu管理的内存用于协议栈的不同业务情形,其中最核心的一种业务情形就是socket,用户分配的内存大小直接决定了用户编写网络应用时能够申请的socket数量。如果你在申请分配一个新的socket时报ERRREQMEMTOOLARGE(The requested memory is too large, please refer to the macro definition BUDDY_MEM_SIZE)或ERRNOFREEMEM(The mmu has no memory available)错误,则意味着内存已经不够用了,需要你增加内存或者检视你的代码看是否存在未及时释放的socket句柄。另外,决定内存利用效率的关键配置项是BUDDY_PAGE_SIZE宏,因为mmu分配内存的最小单位就是“页”。这个宏设置单个内存页的大小,单位为字节,其值必须是2的整数次幂。如果你的通讯报文不大,建议把页面大小调整的小一些,比如16字节、32字节等,以尽量减少单个页面的空余字节数。

sys_config.h文件的其余宏均为协议层相关的配置项。这其中有几个与底层网络接口相关的配置项需要特别关注:

#define SUPPORT_PPP 1 //* 是否支持ppp模块:1,支持;0,不支持,如果选择支持,则系统会将ppp模块代码加入到协议栈中
#if SUPPORT_PPP
#define APN_DEFAULT "4gnet" //* 根据实际情况在这里设置缺省APN
#define AUTH_USER_DEFAULT "card" //* ppp认证缺省用户名
#define AUTH_PASSWORD_DEFAULT "any_char" //* ppp认证缺省口令 #define PPP_NETLINK_NUM 1 //* 协议栈加载几路ppp链路(系统存在几个modem这里就指定几就行)
#define SUPPORT_ECHO 1 //* 对端是否支持echo链路探测
#define WAIT_ACK_TIMEOUT_NUM 5 //* 在这里指定连续几次接收不到对端的应答报文就进入协议栈故障处理流程(STACKFAULT),这意味着当前链路已经因严重故障终止了
#else
#define PPP_NETLINK_NUM 0
#endif #define SUPPORT_ETHERNET 1 //* 是否支持ethernet:1,支持;0,不支持
#if SUPPORT_ETHERNET
#define ETHERNET_NUM 1 //* 要添加几个ethernet网卡(实际存在几个就添加几个)
#define ARPENTRY_NUM 32 //* arp条目缓存表的大小,只要不小于局域网内目标通讯节点的个数即可确保arp寻址次数为1,否则就会出现频繁寻址的可能,当然这也不会妨碍正常通讯逻辑,只不过这会降低通讯效率
#else
#define ETHERNET_NUM 0
#endif

如果目标系统需要用到ppp拨号,我们在打开协议栈对ppp模块的支持后还需要设置缺省的拨号参数值,比如apn、拨号账号及密码等。当然你也可以不用设置,后面我们在编写os适配层接口的时候也会设置这几项。系统会使用os适配层的设置值代替缺省值。另外协议栈在设计之初即考虑支持多路ppp同时拨号的情形,目标系统支持几路ppp,宏PPP_NETLINK_NUM值置几即可。SUPPORT_ECHO宏指定ppp链路是否启用echo回显探测功能。某些ppp接入服务商可能会关闭此项功能,如果你不确定,建议缺省情况下关闭此功能。因为echo链路探测功能一旦被启用,协议栈会每隔一小段时间发送探测报文到对端。对端如果不支持此功能会丢弃该探测报文不做任何响应,这将导致协议栈判定ppp链路故障,从而主动结束链路、重新拨号。

协议栈同样支持多路ethernet网卡,ETHERNET_NUM宏用于指定目标系统存在几路ethernet网卡。这里需要特别注意的是ARPENTRY_NUM宏,这个宏用于指定ethernet网络环境下进行通讯时mac地址缓存表的大小。如果缓存表过小,进行通讯的目标地址并不在缓存表中时,协议栈会先发送arp查询报文,得到对端的mac地址后才会发送实际的通讯报文。虽然这一切都是协议栈自动进行的,但通讯效率会受到影响。如果目标系统的内存够用,建议放大缓存表的容量,最合理的大小是等于计划通讯的目标地址的数量。

其余协议层相关的配置项均属于ip及其支持的上层协议:

//* ip支持的上层协议相关配置项
//* ===============================================================================================
#define SUPPORT_IPV6 0 //* 是否支持IPv6:1,支持;0,不支持
#define SUPPORT_SACK 0 //* 系统是否支持sack项,sack项需要协议栈建立发送队列,这个非常消耗内存,通用版本不支持该项 #define ICMPRCVBUF_SIZE_DEFAULT 128 //* icmp发送echo请求报文时指定的接收缓冲区的缺省大小,注意,如果要发送较大的ping包就必须指定较大的接收缓冲区 #define TCPRCVBUF_SIZE_DEFAULT 2048 //* tcp层缺省的接收缓冲区大小,大小应是2^n次幂才能最大限度不浪费budyy模块分配的内存
#define TCPUDP_PORT_START 20000 //* TCP/UDP协议动态分配的起始端口号
#define TCP_WINDOW_SCALE 0 //* 窗口扩大因子缺省值
#define TCP_CONN_TIMEOUT 30 //* 缺省TCP连接超时时间
#define TCP_ACK_TIMEOUT 3 //* 缺省TCP应答超时时间
#define TCP_MSL 15 //* 指定TCP链路TIMEWAIT态的最大关闭时长:2 * TCP_MSL,单位:秒
#define TCP_LINK_NUM_MAX 16 //* 系统支持最多建立多少路TCP链路(涵盖所有TCP客户端 + TCP服务器的并发连接数),超过这个数量将无法建立新的tcp链路 #if SUPPORT_ETHERNET
#define TCPSRV_BACKLOG_NUM_MAX 10 //* tcp服务器支持的最大请求队列数量,任意时刻所有已开启的tcp服务器的请求连接队列数量之和应小于该值,否则将会出现拒绝连接的情况
#define TCPSRV_NUM_MAX 2 //* 系统能够同时建立的tcp服务器数量
#define TCPSRV_RECV_QUEUE_NUM 64 //* tcp服务器接收队列大小,所有已开启的tcp服务器共享该队列资源,如果单位时间内到达所有已开启tcp服务器的报文数量较大,应将该值调大
#endif #define UDP_LINK_NUM_MAX 4 //* 调用connect()函数连接对端udp服务器的最大数量(一旦调用connect()函数,收到的非服务器报文将被直接丢弃)
#define SOCKET_NUM_MAX 16 //* 系统支持的最大SOCKET数量,如实际应用中超过这个数量则会导致用户层业务逻辑无法全部正常运行(icmp/tcp/udp业务均受此影响),其值应大于等于TCP_LINK_NUM_MAX值
#define IP_TTL_DEFAULT 64 //* 缺省TTL值
#define ROUTE_ITEM_NUM 8 //* 系统路由表数量
//* ===============================================================================================

目前协议栈暂不支持ipv6也不支持tcp sack选项(后续版本会支持),所以SUPPORT_IPV6和SUPPORT_SACK两个宏不要做任何改动,始终为0即可。ICMPRCVBUF_SIZE_DEFAULT宏与ping工具有关,如果你不想使用ping工具可以将这个值设小一些以节省内存。TCP_WINDOW_SCALE宏建议不要做任何调整,对于内存空间有限的单片机系统tcp窗口直接使用指定值即可。TCP_ACK_TIMEOUT宏用于指定tcp报文发送到对端后等待对端回馈tcp ack报文的超时时间,单位:秒。UDP_LINK_NUM_MAX宏决定了目标系统在使用udp通讯时,能够建立的udp客户端的最大数量。比如目标系统需要建立5个udp客户端,由于UDP_LINK_NUM_MAX值为4,那么只有4个客户端能正常调用connect()函数,第5个客户端在调用connect()函数时会报ERRNOUDPLINKNODE(the udp link list is empty)错误。ROUTE_ITEM_NUM宏用于指定系统缓存的路由条目数量,你可以根据实际网络情形调整这个值,但不能低于目标系统注册的网卡数量。协议层相关的其它配置项请根据注释自行依据实际情况进行调整即可。

onps栈移植说明(1)——onps栈的配置及裁剪的更多相关文章

  1. 栈的java实现和栈的应用

    [例子和习题出自数据结构(严蔚敏版), 本人使用java进行实现.  转载请注明作者和出处,  如有谬误, 欢迎在评论中指正. ] 栈的实现 栈是一种先进后出的数据结构, 首先定义了栈需要实现的接口: ...

  2. Activity栈与任务管理探究1——栈与任务的概述

    Activity栈与任务管理探究1--栈与任务的概述 内容概览 1. 前言 2. Activity中的Stack 3. Activity中的Task 4. Activity栈与任务管理基本原则 1. ...

  3. 剑指Offer——栈的java实现和栈的应用举例

    剑指Offer--栈的java实现和栈的应用举例 栈是一种先进后出的数据结构, 栈的实现如下: 首先定义了栈需要实现的接口: public interface MyStack<T> { / ...

  4. C语言实现链栈的初始化&进栈&出栈&读取栈顶元素

    /*链表实现栈的一系列操作*/ #include<stdio.h> #include<stdlib.h> #define OK 1 #define ERROR 0 typede ...

  5. C语言实现顺序栈的初始化&进栈&出栈&读取栈顶元素

    /*顺序表实现栈的一系列操作*/ #include<stdio.h> #include<stdlib.h> #define Stack_Size 50 //设栈中元素个数为50 ...

  6. 20151028整理罗列某种开发所包括对技术(技术栈),“较为全面”地表述各种技术大系的图表:系统开发技术栈图、Web前端技术栈图、数据库技术栈图、.NET技术栈图

    ———————————— 我的软件开发生涯 (10年开发经验总结和爆栈人生) 爆栈人生 现在流行说全栈.每种开发都有其相关的技术.您是否觉得难以罗列某种开发所包括对技术(技术栈)呢?   您是否想过: ...

  7. 如何仅用递归函数和栈操作逆序一个栈——你要先用stack实现,再去改成递归——需要对递归理解很深刻才能写出来

    /** * 如何仅用递归函数和栈操作逆序一个栈 * 题目: * 一个栈依次压入1,2,3,4,5,那么从栈顶到栈底分别为5,4,3,2,1. * 将这个栈转置后,从栈顶到栈底为1,2,3,4,5,也就 ...

  8. struts框架值栈问题二之值栈的内部结构

    2. 问题二 : 值栈的内部结构 ? * 值栈由两部分组成 > root -- Struts把动作和相关对象压入 ObjectStack 中--List > context -- Stru ...

  9. 常见面试算法题JS实现-仅用递归函数和栈操作逆序一个栈

    前言: 因为JAVA和JS语言特性的不同,有些东西在JAVA中可能需要一些技巧和手段才能实现的复杂程序,但是在JS中可能就是天然存在的,所以这套书里面的题目不会全部用JS去实现一遍,因为可能JS的实现 ...

随机推荐

  1. 如何保证遍历parent的时候的task的存在性

    在一次crash的排查过程中,有这么一个内核模块,他需要往上遍历父进程, 但是在拿父进程task_struct中的一个成员的时候,发现为NULL了, 具体查看父进程,原来它收到信号退出中. 那么怎么保 ...

  2. JavaScript基础回顾知识点记录6-操作元素样式和事件对象(介绍基本使用)

    js 中 操作元素样式 通过js修改元素内联样式(设置和读取的都是内联样式) 获取当前元素显示的样式 <html> <head> <meta charset=" ...

  3. 【NOI P模拟赛】(要素过多的标题)(容斥原理)

    题面 0 题目背景 [ 数   据   删   除 ] _{^{[数\,据\,删\,除]}} [数据删除]​ 1 题目描述 在执行任务时,收集到了 n n n 份能源,其中第 i i i 份的能量值是 ...

  4. CCF NOI Online 2021 提高组 T3 岛屿探险(CDQ 分治,Trie 树)

    题面 凇睦是一个喜欢探险的女孩子,这天她到一片海域上来探险了. 在这片海域上一共有 n 座岛屿排成一排,标号为 1, 2, 3, . . . , n.每座岛屿有两个权值,分别为劳累度 ai 和有趣度 ...

  5. 第九十八篇:Web的储存机制LocalStorage

    好家伙 1.什么是LocalStorage? LocalStorage 是一种 web 端的存储机制, 它使得由 JavaScript 编写的网站或者应用可以无限期的在浏览器中存储并访问数据. Loc ...

  6. 【loj2538】 【PKUWC 2018】Slay the Spire dp

    我们不难发现,假设抽了x张攻击牌,y张强化牌,那么肯定是打出尽可能多张的强化牌后,再开始出攻击牌(当然最少要一张攻击牌) 我们设G(i,j)表示:所有(抽到的攻击牌牌数为i,打出的攻击牌牌数为j)的方 ...

  7. CAP 6.2 版本发布通告

    前言 今天,我们很高兴宣布 CAP 发布 6.2 版本正式版,在这个版本中我们主要做了一些功能优化,以及针对目前已经发现的几个 BUG 进行了修复了. 那么,接下来我们具体看一下吧. 总览 可能有些人 ...

  8. Kibana:Canvas入门

  9. Git Review + Gerrit 安装及使用完成 Code-Review

    转载自:https://cloud.tencent.com/developer/article/1010615 1.Code Review 介绍 Code Review 代码评审是指在软件开发过程中, ...

  10. PAT (Basic Level) Practice 1007 素数对猜想 分数 20

    让我们定义dn​为:dn​=pn+1​−pn​,其中pi​是第i个素数.显然有d1​=1,且对于n>1有dn​是偶数."素数对猜想"认为"存在无穷多对相邻且差为2的 ...