學(xué)習(xí)IEEE 1588-2008標(biāo)準(zhǔn)文檔的一種方法是找一個PTP開源項目分瘦,通過閱讀源碼的方式加深對標(biāo)準(zhǔn)的理解路捧。linuxptp是Linux系統(tǒng)上最流行的PTP開源實現(xiàn)甩苛,本文對linuxptp進(jìn)行代碼分析勿锅,并給出代碼和IEEE 1588-2008文檔的映射早抠。
1腌巾、linuxptp項目簡介
linuxptp提供了以下工具實現(xiàn)時鐘同步:
- ptp4l:遵循IEEE 1588-2008標(biāo)準(zhǔn)文檔規(guī)范杠袱,實現(xiàn)了BC(Boundary Clock)昵济、OC(Ordinary Clock)和TC(Transparent Clock)智绸;
- phc2sys:用于同步當(dāng)前設(shè)備上的兩個時鐘,譬如讓System Clock與PHC (PTP Hardware Clock)保持同步访忿;
- pcm:在ptp4l運行期間對其進(jìn)行配置瞧栗。
ptp4l支持SW(軟件時間戳)與HW(硬件時間戳)。如果采用HW海铆,ptp4l實現(xiàn)PHC與BMC(Best Master Clock)的同步迹恐,而phc2sys實現(xiàn)System Clock與PHC的同步;如果采用SW卧斟,ptp4l實現(xiàn)System Clock與BMC的同步殴边,不需要運行phc2sys。
2珍语、ptp4l
圖1 ptp4l模型
2.1 數(shù)據(jù)結(jié)構(gòu)
//clock.c
struct clock {
struct defaultDS dds; //描述當(dāng)前clock的屬性
int time_source; //grandmaster時鐘的時間來源(如GPS)锤岸,該值僅用于信息展示,不參于BMC算法計算板乙,參考《IEEE 1588-2008V2》7.6.2.6小節(jié)
struct currentDS cur; //時鐘同步相關(guān)的屬性
struct parent_ds dad; //parent與grandmaster時鐘的屬性
struct timePropertiesDS tds; //timescale的屬性
LIST_HEAD(ports_head, port) ports; //port列表
}
//ddt.h
struct ClockQuality {
UInteger8 clockClass; //如果值<=127是偷,那么該clock不能成為slave,參考《IEEE 1588-2008V2》7.6.2.4小節(jié)
Enumeration8 clockAccuracy; //標(biāo)記時鐘的精度募逞,用于執(zhí)行BMC算法蛋铆,參考《IEEE 1588-2008V2》7.6.2.5小節(jié)
UInteger16 offsetScaledLogVariance; //時鐘的穩(wěn)定性,用于執(zhí)行BMC算法放接,參考《IEEE 1588-2008V2》7.6.3.2小節(jié)
};
//ds.h
struct defaultDS {
UInteger8 flags; //twoStepFlag和slaveOnly的標(biāo)記位
UInteger16 numberPorts; //當(dāng)前設(shè)備的port個數(shù)戒职,參考《IEEE 1588-2008V2》7.6.2.7小節(jié)
UInteger8 priority1; //用于執(zhí)行BMC算法,數(shù)值越小優(yōu)先級越高透乾,參考《IEEE 1588-2008V2》7.6.2.2小節(jié)
struct ClockQuality clockQuality;
UInteger8 priority2; //類似priority1,參考《IEEE 1588-2008V2》7.6.2.3小節(jié)
struct ClockIdentity clockIdentity; //時鐘id磕秤,參考《IEEE 1588-2008V2》7.6.2.1小節(jié)
UInteger8 domainNumber; //ptp域id乳乌,參考《IEEE 1588-2008V2》7.1小節(jié)
};
struct currentDS {
UInteger16 stepsRemoved; //當(dāng)前時鐘到gransmaster時鐘的距離市咆,參考《IEEE 1588-2008V2》8.2.2.2小節(jié)
TimeInterval offsetFromMaster; //當(dāng)前時鐘與master時鐘的時間差值,參考《IEEE 1588-2008V2》8.2.2.3小節(jié)
TimeInterval meanPathDelay; //當(dāng)前時鐘到master時鐘的傳播耗時磷瘤,meanPathDelay的計算可參考《IEEE 1588-2008V2》11.3與11.4小節(jié)
}
struct parentDs {
struct PortIdentity parentPortIdentity;
UInteger8 parentStats; //TRUE代表observedParentOffsetScaledLogVariance和observedParentClockPhaseChangeRate是有效值扳抽,參考《IEEE 1588-2008V2》7.6.4.2小節(jié)
UInteger16 observedParentOffsetScaledLogVariance; //對parent時鐘的精度的估計,參考《IEEE 1588-2008V2》7.6.4.3小節(jié)
Integer32 observedParentClockPhaseChangeRate; //對parent時鐘的相位變化頻率的估計,參考《IEEE 1588-2008V2》7.6.4.4小節(jié)
UInteger8 grandmasterPriority1; //grandmaster時鐘的priority1屬性,參考《IEEE 1588-2008V2》8.2.3.8小節(jié)
struct ClockQuality grandmasterClockQuality; //grandmaster時鐘的ClockQuality屬性,參考《IEEE 1588-2008V2》8.2.3.7小節(jié)
UInteger8 grandmasterPriority2; //grandmaster時鐘的priority2屬性沧竟,參考《IEEE 1588-2008V2》8.2.3.9小節(jié)
struct ClockIdentity grandmasterIdentity; //grandmaster時鐘的id,參考《IEEE 1588-2008V2》8.2.3.6小節(jié)
};
struct timePropertiesDS {
Integer16 currentUtcOffset; //TAI與UTC時間的差值,單位為秒,參考《IEEE 1588-2008V2》8.2.4.2小節(jié)
UInteger8 flags; //currentUtcOffsetValid、leap59、leap61、timeTraceable、frequencyTraceable和ptpTimescale標(biāo)記位流椒,參考《IEEE 1588-2008V2》8.2.4.3-8.2.4.8小節(jié)
Enumeration8 timeSource; //grandmaster時鐘的時間來源(如GPS),參考《IEEE 1588-2008V2》8.2.4.9小節(jié)
} ;
struct portDS {
struct PortIdentity portIdentity;
Enumeration8 portState; //port的當(dāng)前狀態(tài)鹉胖,可選值見下面Table 1
Integer8 logMinDelayReqInterval; //DelayReq的超時間隔的最小值寂诱,參考《IEEE 1588-2008V2》8.2.5.3.2小節(jié)
TimeInterval peerMeanPathDelay; //delayMechanism為P2P模式時音羞,當(dāng)前port到相鄰port的傳播耗時仓犬;如果delayMechanism為E2E模式,該值應(yīng)該為0舍肠,參考《IEEE 1588-2008V2》8.2.5.3.3小節(jié)
Integer8 logAnnounceInterval; //發(fā)送Announce消息的間隔搀继,參考《IEEE 1588-2008V2》8.2.5.4.1小節(jié)
UInteger8 announceReceiptTimeout; //接收Announce消息超時時長窘面,參考《IEEE 1588-2008V2》8.2.5.4.2小節(jié)
Integer8 logSyncInterval; //發(fā)送Sync消息的間隔,參考《IEEE 1588-2008V2》8.2.5.4.1小節(jié)
Enumeration8 delayMechanism; //測量傳播耗時的機制叽躯,可選值為E2E和P2P财边,參考《IEEE 1588-2008V2》8.2.5.4.4小節(jié)
Integer8 logMinPdelayReqInterval; //接收PdelayReq消息超時時長,參考《IEEE 1588-2008V2》8.2.5.4.2小節(jié)8.2.5.4.5小節(jié)
UInteger8 versionNumber; //PTP版本
} ;
Table1 PTP state
2.2 工作流程
圖2 ptp4l工作流程
初始化本地時鐘流程:
struct clock *clock_create()
{
if (phc_index >= 0)
c->clkid = phc_open(phc); //打開phc API点骑,用于操作PTP硬件時鐘酣难,參考上面圖1
/*servo根據(jù)offsetFromMaster調(diào)整本地時鐘的頻率。
由于延遲波動黑滴,計算出來的offsetFromMaster與實際值會有偏差憨募,servo算法用于減少這種偏差,默認(rèn)servo算法為PI Controller*/
c->servo = servo_create();
c->tsproc = tsproc_create(); //創(chuàng)建filter對offset進(jìn)行平滑袁辈,可選filter有移動平均濾波器菜谣、移動中值濾波器等,參考上面圖1
STAILQ_FOREACH(iface, &config->interfaces, list)
clock_add_port(c, phc_device, phc_index, timestamping, iface); //創(chuàng)建ports
LIST_FOREACH(p, &c->ports, list)
port_dispatch(p, EV_INITIALIZE); //初始化port
}
void port_dispatch(struct port *p)
{
p->dispatch(p); //如果是BC(Boundary Clock)或OC(Ordinary Clock)晚缩,執(zhí)行bc_dispatch
}
void bc_dispatch(struct port *p)
{
port_state_update(p); //事件導(dǎo)致state machine更新狀態(tài)尾膊,見下面圖3
port_e2e_transition(p); //根據(jù)當(dāng)前狀態(tài),更新定時器荞彼,參考《IEEE 1588-2008V2》7.7小節(jié)
}
int port_state_update(struct port *p)
{
if (PS_INITIALIZING == next)
port_initialize(p);
}
int port_initialize(struct port *p)
{
transport_open(); //如果是udp port冈敛,執(zhí)行udp_open
port_set_announce_tmo(); //設(shè)置接收announce message超時定時器,參考《IEEE 1588-2008V2》7.7.3.1小節(jié)
}
int udp_open()
{
efd = open_socket(name, mcast_addr, EVENT_PORT, ttl); //打開event interface句柄鸣皂,見上圖1
gfd = open_socket(name, mcast_addr, GENERAL_PORT, ttl); //打開general interface句柄抓谴,見上圖1
sk_timestamping_init(efd, interface_label(iface), ts_type, TRANS_UDP_IPV4,
interface_get_vclock(iface)); //通知內(nèi)核對event message要打時間戳
/*設(shè)置event message和general message的dscp優(yōu)先級,《IEEE 1588-2008V2》并未規(guī)定dscp签夭,但AES67與RAVENNA標(biāo)準(zhǔn)對此有要求*/
sk_set_priority(efd, AF_INET, event_dscp);
sk_set_priority(gfd, AF_INET, general_dscp);
}
處理事件流程:
int clock_poll(struct clock *c)
{
clock_check_pollfd(c); //更新c->pollfd隊列齐邦,將各個port的socket和timer文件描述符都加入到隊列中
poll(c->pollfd);
LIST_FOREACH(p, &c->ports, list) {
event = port_event(p); //處理事件
port_dispatch(p, event); //根據(jù)state machine更新狀態(tài),更新定時器
}
handle_state_decision_event(c); //執(zhí)行bmc計算
}
enum fsm_event port_event(struct port *p)
{
return p->event(p); //如果是BC(Boundary Clock)或OC(Ordinary Clock)第租,執(zhí)行bc_event
}
enum fsm_event bc_event(struct port *p)
{
switch (fd_index) {
case FD_ANNOUNCE_TIMER:
case FD_SYNC_RX_TIMER:
//定時器超時措拇,失去同步
fc_clear(p->best); //移除master的announce message
port_set_announce_tmo(p); //重置接收announce message超時定時器,參考《IEEE 1588-2008V2》7.7.3.1小節(jié)
delay_req_prune(p); //移除過期的delay req message
return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES; //事件導(dǎo)致state machine更新狀態(tài)慎宾,見下面圖3
case FD_DELAY_TIMER:
port_set_delay_tmo(p); //更新發(fā)送Delay_Req message或Pdelay_Req message的定時器丐吓,參考《IEEE 1588-2008V2》7.7.2.4小節(jié)和7.7.2.5小節(jié)
delay_req_prune(p); //移除過期的delay req message
port_delay_request(p); //如果是e2e機制,發(fā)送Delay_Req message趟据;如果是p2p機制券犁,發(fā)送Pdelay_Req message
return EV_NONE;
case FD_QUALIFICATION_TIMER:
return EV_QUALIFICATION_TIMEOUT_EXPIRES; //在PRE_MASTER等待了足夠的時間,狀態(tài)變?yōu)镸ASTER汹碱,見下面圖3
case FD_MANNO_TIMER:
port_set_manno_tmo(p); //更新發(fā)送Announce message的定時器粘衬,參考《IEEE 1588-2008V2》7.7.2.2小節(jié)
port_tx_announce(p); //發(fā)送Announce message
return EV_NONE;
case FD_SYNC_TX_TIMER:
port_set_sync_tx_tmo(p); //更新發(fā)送Sync message的定時器,參考《IEEE 1588-2008V2》7.7.2.3小節(jié)
port_tx_sync(p); //發(fā)送Sync message
return EV_NONE;
case FD_RTNL:
rtnl_link_status(); //監(jiān)控網(wǎng)卡狀態(tài)變化
if (網(wǎng)卡狀態(tài)變更)
return EV_FAULT_DETECTED;
else
return EV_NONE;
}
transport_recv(p->trp, fd, msg); //接收message,如果是event message稚新,記錄接收時間戳
switch (msg_type(msg)) {
case SYNC:
process_sync(p, msg); //處理Sync message勘伺,處理流程見下面圖4,參考《IEEE 1588-2008V2》9.5.4小節(jié)
break;
case DELAY_REQ:
process_delay_req(p, msg); //處理Delay_Req message褂删,處理流程見下面圖5飞醉,參考《IEEE 1588-2008V2》9.5.6小節(jié)
break;
case PDELAY_REQ:
process_pdelay_req(p, msg); //處理Pdelay_Req message,發(fā)送Pdelay_Resp message和Pdelay_Resp_Follow_Up message屯阀,參考《IEEE 1588-2008V2》9.5.14和9.5.15小節(jié)
break;
case PDELAY_RESP:
process_pdelay_resp(p, msg); //處理Pdelay_Resp message缅帘,如果是one-step模式,計算鏈路傳播耗時难衰,參考《IEEE 1588-2008V2》11.4小節(jié)
break;
case FOLLOW_UP:
process_follow_up(p, msg); //處理Follow_Up message钦无,處理流程見下面圖6,參考《IEEE 1588-2008V2》9.5.5小節(jié)
break;
case DELAY_RESP:
process_delay_resp(p, msg); //處理Delay_Resp message召衔,處理流程見下面圖7铃诬,參考《IEEE 1588-2008V2》9.5.7小節(jié)
break;
case PDELAY_RESP_FOLLOW_UP:
process_pdelay_resp_fup(p, msg); //處理Pdelay_Resp_Follow_Up message,計算鏈路傳播耗時苍凛,參考《IEEE 1588-2008V2》11.4小節(jié)
break;
case ANNOUNCE:
process_announce(p, msg); //處理Announce message趣席,處理流程見下面圖8,參考《IEEE 1588-2008V2》9.5.3小節(jié)
break;
case SIGNALING:
process_signaling(p, msg); //處理Signaling message醇蝴,參考《IEEE 1588-2008V2》16.1小節(jié)
break;
case MANAGEMENT:
clock_manage(p->clock, p, msg); //處理Management message宣肚,參考《IEEE 1588-2008V2》15.3小節(jié)
break;
}
}
圖3 ptp狀態(tài)機
圖4 接收Sync message的邏輯
圖5 接收Delay_Req message的邏輯
圖6 接收Follow_Up message的邏輯
圖7 接收Delay_Resp message的邏輯
圖8 接收Announce message的邏輯
BMC計算流程:
//BMC計算流程概述見《IEEE 1588-2008V2》9.3.2.2小節(jié)
void handle_state_decision_event(struct clock *c)
{
LIST_FOREACH(piter, &c->ports, list) {
fc = port_compute_best(piter); //port根據(jù)收到的Announce message選出最優(yōu)的foreign clock
if (c->dscmp(&fc->dataset, &best->dataset) > 0) //比較各個port的最優(yōu)foreign clock,選出全局最優(yōu)foreign clock悠栓,dscmp對比流程見下面圖9和圖10霉涨,參考《IEEE 1588-2008V2》9.3.4小節(jié)
best = fc;
}
c->best = best;
LIST_FOREACH(piter, &c->ports, list)
bmc_state_decision(c, piter, c->dscmp); //port根據(jù)新選出來的best clock更新各自的狀態(tài),更新邏輯見下面圖11惭适,參考《IEEE 1588-2008V2》9.3.3小節(jié)
}
圖9 Data set比較算法
圖10 Data set比較算法
圖11 bmc state decision算法