剖析Linuxptp中ptp4l实现--OC

源码克隆地址:

git://git.code.sf.net/p/linuxptp/code

项目官网文档:

https://linuxptp.nwtime.org/documentation/

关于linuxptp的相关配置可以参考以下博文:

linuxptp/ptp4l PTP时钟同步配置选项

代码剖析

ptp4l的main函数在ptp4l.c中,命令行解析使用的是 getopt_long ,具体使用方法可以百度,这个是现成的命令行解析API。

可以看到解析不同命令行参数后都是调用的 config_set_int 函数设置,linuxptp中配置一般都是保存在 config.c 中的 config_tab 中:

关于配置项所代表的含义可以参考上文推荐的博文。

命令行中比较重要的是 -i ,也就是添加interface:

创建接口使用的是网卡名称,比如 -i eth0,此时就会创建一个名字是eth0的接口,源码如下:

在 interface_create 中注意,除了名字(name)还有ts_label也被设置为传入的网卡名称:

除去配置参数和接口创建,其实功能主体就是创建clock,和轮询创建clock时添加的文件描述符:

在 clock_create 中只看几个关键的地方,第一个是软硬件时间戳相关:

在clock创建的初始你会看到基本都是初始化 c->dds 这个结构体相关的配置,这里在协议原文中有:

其实dds就是defaultDS,这几个数据集都是协议明文规定的数据集,linuxptp中在ds.h中有所定义,详细内容可以参照协议原文第8章节PTP data sets。

在配置比如使用软件时间戳还是硬件时间戳,是onestep还是twostep时,会先根据设置得到一个网卡预期需要支持的模式,然后根据前面创建的interface,获取网卡的信息,再判断网卡是否支持:

再下面是确定使用哪个PHC(ptp hardware clock)的逻辑:

还有UDS(unix domain sockets)的配置:

剩下的就是clock本身一些杂项初始化,在这个函数末尾有最重要的port添加与初始化:

在添加port的时候,可以看到每个port申请了多少个fd:

从上图可以看到clock的port个数=interface个数+2

从上图可以看到,当没有添加过port的时候port个数是两个uds,每次添加一个port,实际是加了3个port,也就是添加一个port的时候,一共有5个port,每个port有 N_CLOCK_PFD 个文件描述符,这些文件描述符就是后续需要轮询的。N_CLOCK_PFD是12,其中除了包含下面11个fd,还有一个处理错误状态的定时器fd。

回到刚才的函数,port_open中还有一些port的参数设置,其中比较重要的有:

以及通过 transport_create 创建了传输实例:

根据传输类型有UDS/ETHERNET/IPV4/IPV6可选,最终trp就是一组包含发送接收等的函数指针合集:

比如IPV4:

port_open 中还有有限状态机(fsm)的设置:

需要注意,状态机的各种状态也是协议中所明文规定的:

具体内容请参照协议原文9.2.5章节。 

再有就是fault定时器也在这里被创建:

回到clock_create函数最后对port的初始化:

根据前面port_open中的源码,假如我们是E2E的OC,那么我们的 port_dispatch 函数是 bc_dispatch :

在 port_state_update 里我们根据 EV_INITIALIZE 事件对端口进行了初始化:

在 port_initialize 函数中,除了初始化一些参数配置,最重要的是创建了各种定时器fd:

拿IPv4来举例,319和320是固定的两个端口,它们就是通过 transport_open 函数打开:

这里event port用来接收event消息,general端口用来接收general消息:

详细信息可以参考协议原文7.3.3。

至此,所有配置都初始化完成了,后续就只剩下一直轮询之前添加的fd而已了。在main里有:

clock_poll 函数里面主要就是轮询fd,然后分发事件。 

假如还按照之前举的例子,E2E的OC的话,port_event实际是 bc_event 函数。

比如我们是master的话,sync同步包发送定时器时间到了我们就会:

在处理完定时器fd事件后,紧跟着就是接收来自两个fd的数据:

然后根据接收到的事件做不同处理,同时更新状态机状态。

E2E--master

其实master要做的事情很简单:

1.发送Announce报文

2.发送Sync(FollowUp)报文

3.应答DelayReq报文,也就是回发DelayResp报文。

正常对时的两个设备报文如下:

ptp4l中是如何实现的呢?

首先,master是如何当上grandmaster的,这里我讲一下只有一个设备时,自身是怎么被选举为grandmaster的,在port初始化函数 port_initialize 中,有初始化两个参数:

在 port_open 函数中有初始化另一个参数:

这几个参数又在 port_initialize 中被使用:

上图函数中:

M = p->announceReceiptTimeout = 3 

S = p->announce_span = 1

N = p->logAnnounceInterval = 1

所以设置的超时时间范围是6~8s

注意,周期性的报文的周期值最好不是一个定值。比如现在要以周期为2秒发送一个X报文,那最好的办法不是每隔2秒就发送一个X报文,而是在2秒的基础上加一个随机值(比如+1~-1之间的一个数),这样可以减少碰撞。 

设置完 FD_ANNOUNCE_TIMER 后,当定时器超时后:

会返回一个Announce报文接收超时的状态,这个状态在clock_poll中有:

可以看到会改变clock的sde的值(state decision event,状态决策事件) ,当这个值置1后,会处理一次状态,还是在这个函数内:

其中历遍所有port的时候会用类似冒泡法去对比,当只有一个port的时候这个port自然会被选为master:

当选举完master clock后,会更新所有port的状态:

bmc_state_decision 中关于状态决策可以参考协议原文9.3.5中的相关描述。

然后生成了一个 EV_RS_GRAND_MASTER 事件,被分发到相关端口:

OC/BC使用的分发函数是 bc_dispatch  在这个函数中有更新port状态:

port_state_update 中使用的状态机 state_machine 是在 port_open 函数中初始化时赋值的:

 这里master使用的是ptp_fsm,如果是slave则是ptp_slave_fsm。然后根据这个函数得到下个状态:

我们的当前状态是 PS_LISTENING,事件是 EV_RS_GRAND_MASTER ,所以下个状态就是 PS_GRAND_MASTER:

拿到这个状态后,根据选择的是E2E测量方式,所以会使用 port_e2e_transition 函数处理:

在这个函数中,当处于这个状态时,则会设置两个定时器:FD_MANNO_TIMER 和 FD_SYNC_TX_TIMER。注意之前 port_initialize 函数中有初始化 p->inhibit_announce = 0:

在这里第一次设置了 FD_MANNO_TIMER ,第一次设置的超时时间很短,后续设置是在 bc_event 中的:

默认情况下,超时时间是2秒:

而发送报文的内容可以查看 port_tx_announce 函数。

和设置 Announce 报文发送时间差不多,发送 Sync 报文的超时时间是1秒: 

发送Sync报文的函数可以查看 port_tx_sync 。

E2E--slave

一般指定当前设备为slave时使用 -s选项:

这里我主要讲一下slave收到报文后怎么计算offset和pathDelay的。

上面已经介绍过,对于接收报文的处理在 bc_event 函数内,对于E2E的slave主要是处理以下几种报文:

首先看对于sync报文的处理:

这里我们假如先收到的是Sync报文,则事件是 SYNC_MISMATCH ,在Sync和FollowUp报文处理状态机函数 port_syfufsm 里面有:

我们再看假如在Sync后收到FollowUp报文该如何处理:

 至此当前状态是 SF_HAVE_SYNC ,处理的事件是 FUP_MATCH:

在函数 port_synchronize 中就可以拿到当前的t1、t2、c1(Sync的修正域)、c2(FollowUp),此时即可计算:

offset = t2-(t1+c1+c2) 代码中如下所示:

在函数 clock_synchronize 中有:

这里的 delay 也就是 filtered_delay 又是从何而来的呢?

在 bc_event 中对与发送DelayReq报文的定时器超时后会发送此报文:

port_delay_request 函数里关键的有以下处理: 

还是在 bc_event 里收到 DelayResp 报文后 使用 process_delay_resp 函数处理:

原始pathdelay的计算过程如下:

上图中可以看到我添加了一些关键位置的打印,现在实际运行起来,slave打印如下:

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值