《从Cfgdemo项目来分析协议栈的启动.pdf》由会员分享,可在线阅读,更多相关《从Cfgdemo项目来分析协议栈的启动.pdf(12页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 从 Cfgdemo 项目来分析协议栈的启动 The manuscript was revised on the evening of 2021 从 Cfgdemo 项目来分析协议栈的启动 项目中静态创建的任务有两个:一个是空闲任务,一个是 StackTest 任务,main 函数是空的。任务 StackTest 的优先级(5)比空闲任务高。整个程序的初始化部分执行完之后,就会执行 StackTest 任务,从而执行 StackTest()函数。StackTest()函数首先调用了 NC_SystemOpen()函数,来完成协议栈系统的初始化工作。必须注意的是:在使用协议栈之前必须最先调用该函
2、数。接下来调用函数 CfgNew()来创建一个配置(Configuration)并获得该配置的句柄,接下来的工作就是在配置中增添配置项(Configuration Entry),增添配置选项是通过调用 CfgAddEntry()函数实现的。该项目中首先增添的配置项为 Host name:从 CFGDEMO 项目来分析协议栈的启动 接着增添的配置项为 Telnet 服务,那么协议栈系统在启动之后会启动 Telnet 服务(创建了一个名为 telnetd 的任务):?接着通过调用 efs_createfile()创建 5 个文件,其名字分别为:、;这 5 个文件中,前两个文件的数据分别存放在数组
3、DEFAULT、TIBUG中,而后三个文件实际是 cgi 程序,这三个 cgi 程序分别完成来之客户端的命令请求:View configuration、Change password、Submit configuration,与之同时,它们动态修改并发送了两个网页并 CONFIG、USERMSG。接着增添的配置项为 HTTP 服务,那么协议栈启动之后会启动 Http 服务(创建一个名为 http server 的任务)接着的增添的配置项为 CFGITEM_OS_DBGPRINTLEVEL,来选择打印的信息内容:接下来调用函数 CfgSave()来获取配置的大小并讲配置数据存入一个缓存中去,并释
4、放配置。接下来调用函数 NetBoot()来启动协议栈。这个函数是 NDK 的用户自己写的一个函数,在这个函数中,调用了协议栈启动函数 NC_NetStart(),这个函数属于 Network Control API,该函数的源代码可以在tiNDKsrcnetctrl中找到。其函数接口如下:NetBoot()在调用 NC_NetStart()之前调用 CfgNew()来创建一个新的空的配置,然后再调用 CfgLoad()来把之前存入缓冲区中的配置好的配置数据载入新的配置中去,并把它作为参数传入 NC_NetStart()中去,接着就调用 NC_NetStart()启动协议栈。NC_NetSta
5、rt()函数首先调用了 4 个硬件抽象层(HAL)的四个初始化函数,由它们来完成底层硬件的初始化(具体细节过程等待进一步研究):接着该函数调用 CfgSetDefault()把传入 NC_NetStart()函数中构建好的配置设置为默认配置。由于编程方法上的需要,协议栈就使用配置是统一为一个配置句柄指向的配置。这样 CfgSetDefault()的本质就是把该配置句柄指向传入 NC_NetStart()函数中构建好的配置。接着把传入 NC_NetStart()函数的三个函数指针赋值给三个全局变量,以方便后面适当的时候调用:接着调用协议栈的核心 API 函数 ExecOpen()来初始化协议栈的
6、 executive(自己意会这个概念)。接着动态创建一个名为 ConfigBoot 的任务,其优先级为 15(最高),其执行的函数是 NS_BootTask()。由于 DSP/BIOS 是占先式实时 OS,所以一旦任务高优先级的任务创建,OS 内核的调度模块就会自动切换到高优先级的任务执行。很显然,接下来执行的是函数 NS_BootTask()。这个函数的源码在tiNDKsrcnetctrl 中可以找到。NS_BootTask()函数首先调用 CfgSetService()来 Set Service CallBack Funtions for Every Configuration Tag,
7、其意思是为每个 Configuration Tag 设置一个回调函数,其目的是为了在修改完配置之后能及时更新协议栈系统,也就是使协议栈系统随着配置的改变而实时地改变。回调函数的接口定义如下:?在 NDK 的协议栈中,Configuration Tag 共有如下 8 个:?其中需要配置回调函数的有如下几个:CFGTAG_OS、CFGTAG_IP、CFGTAG_SERVICE、CFGTAG_IPNET、CFGTAG_ROUTE,它们的回调函数分别为:SPConfig()、SPConfig()、SPService()、SPIpNet()、SPRoute(),这些回调函数的实现源代码都在tiNDKsr
8、cnetctrl 可以找到。下面分析以下 SPConfig()函数是怎样实现实时更新系统的:SPConfig()函数是作为 CFGTAG_OS、CFGTAG_IP 的回调函数的,所以它必须负责处理增添 CFGTAG_OS、CFGTAG_IP 两种类型的 Configuration Entry 时的系统实时更新工作。系统在调 CfgAddEntry 函数来增添一个 CFGTAG_OS、CFGTAG_IP 类型的配置项后(注意:CfgAddEntry只把配置数据添加到配置中去),会调用与该种配置类型捆绑的的回调函数 SPConfig(CfgSetService 函数来完成捆绑工作的),SPConf
9、ig函数调用 CfgEntryInfo 来获取该配置项的数据缓冲区的指针并存放在变量 pi 中,接着更具 Configuration Tag 的类型来获取具体需要修改的系统配置参数结构体;CFGTAG_OS OSENVCFG _oscfg、oscfgcopy /CFGTAG_IP IPCONFIG _ipcfg、ipcfgcopy,这两个都是全局变量,它们的数据结构类型如下:?/Configuration Structure typedef struct _ipconfig uint IcmpDoRedirect;/Update RtTable on ICMP redirect(1=Yes)u
10、int IcmpTtl;/TTL for ICMP messages RFC1700 says 64 uint IcmpTtlEcho;TTL for ICMP echo RFC1700 says 64 uint IpIndex;/IP Start Index uint IpForwarding;/IP Forwarding(1=Enabled)uint IpNatEnable;/IP NAT Enable(1=Yes)uint IpFilterEnable;/IP Filtering Enable(1=Yes)uint IpReasmMaxTime;/Max reassembly time
11、in seconds uint IpReasmMaxSize;/Max reassembly packet size uint IpDirectedBCast;/Look for directed BCast IP addresses uint TcpReasmMaxPkt;/Max reasm pkts held by TCP socket uint RtcEnableDebug;/Enable Route Control Messages(1=On)uint RtcAdvTime;/Time in sec to send RtAdv(0=dont)&nbs p;uint RtcAdvLif
12、e;/Litetime of route in RtAdv int RtcAdvPref;/Preference Level(signed)in RtAdv uint RtArpDownTime;/Time 5 failed ARPs keep Rt down(sec)uint RtKeepaliveTime;/VALIDATED route timeout(sec)uint RtCloneTimeout;/INITIAL route timeout(sec)uint RtDefaultMTU;/Default MTU for internal rout es uint SockTtlDefa
13、ult;/Default Packet TTL uint SockTosDefault;/Default Packet TOS int SockMaxConnect;/Max Socket Connections uint SockTimeConnect;/Max time to connect(sec)uint SockTimeIo;/Default Socket IO timeout(sec)int SockBufMax;&nbs p;/Absolute max Socket buffer size int SockBufMinTx;/Min Tx space for able to wr
14、ite int SockBufMinRx;/Min Rx data for able to read uint PipeTimeIo;/Default Pipe IO timeout(sec)int PipeBufSize;/Pipe internal buffer size int PipeBufMinTx;/Min Tx space for able to write int PipeBufMinRx;/Min Rx data for able to read IPCONFIG;?大家应该注意到所有的成员都是 32 位的数据类型,所以这里的 pi 和pDst 指针都定义为指向 32 位类型
15、数据的指针。在做完必要性的检测之后,就会把 pi 指向数据缓冲中的数据直接拷贝到 pDst+Item 指向的数据缓冲中去。两个细节性的问题:1)为什么只拷一个 32 位因为这两种类型的 Configuration 的 Configuration Entry 都是 32 位类型的数据。2)为什么 Item 要事先减一因为 Tag=CFGTAG_OS/CFGTAG_IP,Item 的值都是从 1 开始的。这样系统的配置就被修改了,后面程序的执行就会根据新的配置去操作。这里只分析了 AddEntry 的过程,RemoveEntry 的过程基本上差不多,不同的是用系统默认配置的值去覆盖系统配置。/-/
16、SPConfig()-CFGTAG_IP and CFGTAG_OS Service Provider/-static int SPConfig(HANDLE hCfg,uint Tag,uint Item,uint Op,HANDLE hCfgEntry)uint*pi,*pdst,*pdef;&n bsp;(void)hCfg;?/Get the information if(CfgEntryInfo(hCfgEntry,0,(UINT8*)(&pi)CFGITEM_IP_MAX)return(-1);pdst=(uint*)&_ipcfg;pdef=(uint*)&ipcfgcopy;e
17、lse if(Tag=CFGTAG_OS)/Bound the value of Item if(Item CFGITEM_OS_MAX)return(-1);pdst=(uint*)&_oscfg;pdef=(uint*)&oscfgcopy;else return(-1);?/Verify Item if(!Item)return(-1);Item-;?/If this is an add,add the entry if(Op=CFGOP_ADD)*(pdst+Item)=*pi;/Else if remove,restore the default else if(Op=CFGOP_R
18、EMOVE)*(pdst+Item)=*(pdef+Item);?/Return success return(1);到这里大家可能觉察到一个问题:我们的 CfgAddEntry 函数在 StackTest 任务的开始就被调用,而我们的回调函数是在后来才安装上去的,那么这些添加的配置项是不是没有被更新到系统配置中去呢没错,的确没有!那我们怎么办呢由于一开始我们添加了多个配置项,那么这些配置项更新到系统应该有一个先后顺序(可能它们之间有什么依赖关系吧),因此,首先要调用函数 CfgSetExecuteOrder()来设置,需要注意的是这个函数不仅设定配置项更新到系统配置中的顺序,同时也设定了这些
19、配置项从系统配置中删除的顺序。做好这些准备工作之后,调用CfgExecute()(其中 fExecute 参数值为 1)来使配置项可以更新到系统配置中去,同也使能以后添加的配置项也能实时更新到系统配置中去。注意:这个函数必须调用,否则你用 CfgAddEntry 函数添加的配置项都不能更新到系统配置中去,即使你在安装好回调函数后调用 CfgAddEntry 也不能。至此,TCP/IP 协议栈系统已经启动,应用程序可以调用协议栈的API 函数来实现网络通信等应用。接着调用函数 NC_BootComplete(),该函数除了设置相应的标志外,主要是来执行一个用户程序。大家应该还记得 NC_NetS
20、tart 函数的接口吧,它有三个函数指针作为参数传入,其中第一个就在 NC_BootComplete()中调用:*NetStart,该函数指针所指向的函数是由用户来实现,从而向用户提供一个机会,由用户自己决定协议栈启动之后做什么工作。接着 NS_BootTask 中调用 TaskExit()来结束该任务(任务的状态由 RunningTerminated),那么接下来通过 DSP/BIOS 内核的调度,使得 StackTest 任务继续运行。StackTest 任务接着调用函数 NetScheduler,它是协议栈的调度器,用来检测并处理所有与网络相关的事件。该函数是一个无限循环,因此任务 St
21、ackTest 最终就成为了网络事件调度任务,也就是说它的角色发生了改变,因此其优先级需要做适当的调整。有关网络事件调度任务的具体细节后面再论。至此,整个系统就跑起来了,但是我们的应用程序怎样添加到这个系统中去并使用这个协议栈呢?大家应该还记得 NC_BootComplete 函数调用的 NetStart 函数指针指向的函数吧,它是由用户实现的一个函数,在协议栈启动之后调用。因此用户的应用程序(与网络相关的应用)可以在该函数中动态创建(应用程序作为任务的形式添加到系统中),CfgDemo 项目就是这么做的。CfgDemo 项目中 NetStart 函数指针指向的函数为 NetworkOpen()代码如下:由此可见该函数动态创建了五个任务,其优先级相同,均为 OS_TASKPRINORM(5),而且这五个任务的状态都为处于 Ready 状态。但是由于随后网络事件调度任务调整了自己的优先级,此四个任务都从 Ready 状态转为 Blocked 状态。一旦网络事件调度任务检测到网络事件就会通知相应的任务使之进入 Running 状态,处理网络事务。
限制150内