顧燕飛, 鄒維軍, 王子劍, 張慶松
(中國電子科技集團公司第三十二研究所, 上海 201808)
Netlink作為一種進程間通信機制, 除了提供內核不同模塊間通信, 還能實現內核態與用戶態進程間通信.相較于傳統的用戶進程與內核通信的機制(如ioctl方法和proc文件系統)的單向性, Netlink可以實現全雙工通信[1].
1553B總線全稱為數字時分命令/響應型多路傳輸數據總線.最早應用于美軍航空電子綜合系統之上, 各種航電設備基于MIL-STD-1553B總線完成設備間通信[2].但隨著其不斷的發展, 線性局域網絡結構使得1553B總線成為航空系統或地面車輛系統中分布式設備的理想連接方式.與點對點連接相比, 它減少了所需電纜、所需空間和系統的重量.便于維護, 易于增加或刪除節點, 提高了設計靈活性, 冗余容錯能力等特點,在航空、航天、航海和民用等領域都得到廣泛的使用[3].
Socket 1553B協議是在Linux下1553B協議實現的一種方法.Linux下一般使用1553B協議的方法是基于字符設備來實現的, 與之不同的是Socket 1553B使用Netlink的socket接口和linux網絡協議棧, 這種方法使得1553B設備驅動可以通過網絡接口來調用.Socket 1553B的接口被設計的盡量接近TCP/IP的協議, 讓那些熟悉網絡編程的程序員能夠比較容易的學習和使用.
整個系統環境的最底層是PCIE-1553B接口卡, 在操作系統中為1553B設備創建網絡驅動, 負責配置和收發操作.在收發網絡包過程中對其進行解析報文, 根據協議類型判斷是否為1553B數據包, 遞交給Netlink內核層中的接收函數處理[4].利用內核提供的Netlink接口層, 創建新的網絡協議, 屏蔽了底層實現的差異, 為上層socket應用提供了統一的應用接口.系統總體架構如圖1.

圖1 系統總體架構
1553B總線系統包括串行傳輸電纜、總線控制器BC、遠程終端RT、總線監控器MT[5].其組成如圖2所示.
本文以BC與RT之間的通信為例, 來說明該總線系統的工作過程.該過程如下:
(1) 總線控制器BC通過初始化完成數據傳輸的準備工作.
(2) BC通過向總線發送命令字來決定參與本次數據傳輸的遠程終端和采用的消息格式.命令字分為發送命令字(要求RT向總線上發送數據), 接收命令字(要求RT接收總線上的數據), 方式代碼命令字(要求RT完成特殊操作).

圖2 1553B 總線通信系統組成
(3) RT檢測到BC傳來的命令字與自己地址匹配時, 將按照命令字的要求參與到數據傳輸過程中, 完成數據傳輸后RT將在規定時間內返回狀態字[6].
(4) BC通過檢測RT返回的狀態字來判斷本次數據傳輸完成情況.如果數據正確傳輸將結束本消息傳輸.
其中字格式如圖3所示.

圖3 1553B 協議字格式
1553B協議驅動實現BC與RT之間的通信.BC和RT的通信過程可以分為BC發送RT接收和RT發送BC接收兩種情況.
2.1.1 BC 讀 RT
BC讀RT表示RT發送BC接收, 如圖4所示.如圖4所示, 整個過程如下:
(1) RT將數據寫入本地DMA發送緩存, 等待BC來讀取.
(2) BC發送BC讀RT的命令給目的RT, 告訴目的RT, BC需要從RT的哪個子地址(sa)讀取多長的數據(len), 同時BC開始計時, 在一定的時間內若沒有收到RT返回的狀態(幀), 則產生超時中斷.

圖4 BC 讀 RT 數據原理框圖
(3) RT接收到BC的命令后RT啟動本地定時器(RT需要在規定的時間內向BC發送狀態幀), 根據本地情況返回不同的狀態幀給BC, 具體情況如下:
1) 如果BC要求讀取的目的子地址沒有數據,則返回busy幀給BC, 并產生一次正常數據發送中斷.
2) 如果BC要求讀取的目的子地址有數據,則返回“狀態+數據”給BC, 并產生一次正常數據發送中斷.
3) 在 1), 2)的過程中, 如果定時時間到, 則會產生一個“RT返回狀態超時”的中斷[7].
(4) BC在規定的時間內收到RT返回的狀態(busy幀或數據幀), BC將RT返回的狀態寫入特定寄存器保存起來, 如果返回的狀態后面跟有數據, 則將數據存入BC特定的DMA接收緩存中, 供驅動程序獲取, 并產生一次正常數據接收中斷.
(5) 驅動程序根據返回的狀態內容, 將busy狀態或接收到的數據返回給用戶.
2.1.2 BC寫 RT
BC寫RT表示BC發送RT接收, 如圖5所示.如圖5所示, 整個過程如下:
(1) BC將用戶數據寫入本地特定DMA發送緩存.

圖5 BC 寫 RT 數據原理框圖
(2) BC將目的RT號、目的子地址號、數據長度信息寫入特定寄存器, 供硬件組“命令字”使用.
(3) 啟動命令發送.
(4) BC以命令字+數據字(可以有多個)的形式往外發送數據, 在最后一個數據字發送完后, 啟動定時.
(5) RT收到BC寫命令進行解析, 當BC向RT所寫的子地址中有數據時, 返回busy幀發送BC;當BC向RT所寫的子地址中無數據時, RT接收數據, 并產生正常接收的狀態發送BC[8].
(6) 如果在一定時間內沒有收到RT返回的狀態(表征busy或正常接收), 則產生一次發送超時;否則產生一次正常發送中斷.
(7) RT 將根據狀態, 反饋給用戶.
Netlink機制用戶層的套接字采用標準的套接字接口來定義的, 其中包括 socket、bind、sendto、recvfrom、close等[9].
int socket( int domain, int type, int protocol);
socket函數, 指定期望的通信協議類型和傳輸方式.domain:地址域, 在 Netlink 機制下應為 AF_NETLINK;type:SOCK_DGRAM 或 SOCK_RAW 兩種類型;protocol:協議使用NETLINK_1553B_EVENT, 自定義協議號為NETLINK_1553B_EVENT=17.
int bind ( int sockfd, struct sockaddr *maddr,socklen_t addrlen);
bind函數, 用來綁定一個套接字的地址、協議、端口號[10].在Netlink機制下綁定地址的結構體如下:

sendto是向套接字發送消息給內核的Netlink層.Netlink套接字消息包括消息頭和數據兩部分.Netlink套接字所傳遞的消息都有一個固定的消息頭,結構體如下[11]:

nlmsg_len表示該消息的總長度;nlmsg_type表示消息類型, 該值與Netlink選用的通信協議相關, 本文設置為NLMSG_1553B_TYPE=0x12, 表示1553B協議消息類型;nlmsg_flags為控制信息, 如標志設為NLM_F_REQUEST或 NLM_F_ACK, NLM_F_ACK 表示要求消息接收方發回一個確認消息給消息發送方;nlmsg_seq表示序列號;nlmsg_pid表示發出消息進程的進程號.

close表示關閉一個Netlink網絡套接字, 具體操作是把套接字鏈表中對應的套接字刪除, 同時釋放套接字的接收緩存和發送緩存, 清除套接字的使用內存,設置套接字的傳輸狀態為關閉.
在Netlink內核初始化時, 通過netlink_kernel_create函數來創建內核套接字, 并將自定義協議號為NETLINK_1553B_EVENT=17, 消息類型為 NLMSG_1553B_TYPE=0x12進行注冊.

net表示網絡名字空間, 一般使用init_net這個全局變量;unit表示使用的協議號, 這里為 NETLINK_1553B_EVENT=17;cb_mutex 為 NULL;module為THIS_MODULE.
input()函數為具體的消息處理函數[12].內核中為某一個通信協議使用netlink_kernel_create函數創建了一個Netlink套接字, 所有該通信協議的用戶方Netlink消息都將發送給內核空間的這個Netlink套接字, 由該套接字再調用創建時注冊的input()函數來統一處理所有的消息.這里在自定義消息處理函數為msg1553b_receive中, 會判斷消息類型、長度等, 以及1553B協議整個收發過程, 包含BC讀RT, BC寫RT,RT讀數據, RT寫數據等.
基于Netlink機制的用戶態與內核態之間的通信如圖6所示.

圖6 1553B協議在內核態與用戶態的通信流程
用戶態中使用Netlink套接字socket創建時, 需要設置協議號為NETLINK_1553B_EVENT=17.在Netlink套接字封裝消息時, 需要將消息類型設置為NLMSG_1553B_TYPE=0x12.并且在消息數據中規定了以下幾種模式:
DEV_INIT:表示設備的初始化, 規定該設備的角色, 是BC還是RT.
BC_RD_RT:表示 BC 讀 RT, 即 BC需要從 RT的某個子地址中讀取數據.
BC_WR_RT:表示BC寫RT, 即BC需要向RT的某個子地址中寫入數據.
RT_RD_DATA:表示 RT 讀數據, 即 RT 從某個子地址中讀取數據.
RT_WR_DATA:表示 RT 寫數據, 即 RT向某個子地址中寫入數據.
內核態中使用Netlink套接字socket創建時, 需要注冊協議號NETLINK_1553B_EVENT=17, 并指明接收處理函數msg1553b_receive.在msg1553b_receive函數中會判斷消息類型是否為NLMSG_1553B_TYPE,如果是, 繼續處理消息;如果不是則返回錯誤.對應的消息處理函數中針對以上這幾種模式會做出不同的響應:
(1) 當收到DEV_INIT模式時, 將判斷是BC還是RT, 分別進行對應的初始化.
(2) 當收到 BC_RD_RT模式時, 將 1553B的狀態字發送給RT, 并讀取RT返回的數據或狀態, 再返回給用戶態.若超時或無數據, 則返回對應的狀態給用戶態.
(3) 當收到 BC_WR_RT 模式時, 將 1553B的“狀態字+數據”發送給RT, 并讀取RT返回狀態, 再返回給用戶態.若超時或無法寫數據, 則返回對應的狀態給用戶態.
(4) 當收到 RT_RD_DATA 模式時, 將從底層RT對應子地址中的讀取數據, 并返回給用戶態.若超時或無數據, 則返回對應的狀態給用戶態.
(5) 當收到 RT_WR_DATA 模式時, 將向底層RT對應子地址中的寫入數據, 并返回給用戶態.若超時或無法寫入數據, 則返回對應的狀態給用戶態.
搭建測試環境, 測試設備連線示意圖如圖7所示.
按圖7所示連接開發機、測試計算機、1553B監控設備(監控1553B總線上的數據).測試計算機上, 通過測試軟件打開1553B監控設備, 對整個通信過程進行監控;Linux軟件開發機上加載1553B驅動模塊;分別打開測試機上RT設備Netlink套接字測試程序和Linux開發機上BC設備Netlink套接字測試程序, 進行BC和RT設備間1553B數據通信測試.本測試以BC讀RT數據為例, 具體測試步驟如下.

圖7 設備連線關系示意圖
(1) BC向0號RT設備發送請求模式字命令, 以獲知當前0號RT設備的哪些子地址擁有數據.
(2) RT在收到BC的矢量請求命令后回復一個數據字, 告知BC此時自身擁有數據的子地址號, 如果沒有返回0.
(3) BC根據RT返回的結果, 向當前RT的擁有數據的某個子地址發送“RT發送數據”命令字, 來獲取該子地址的數據.
(4) RT 在收到“RT發送數據”命令字后, 將相應子地址的數據發送給BC, 記錄此時發送的數據.
測試結果如下表所示.
通過表1可見, BC發送給RT命令為0x0410, 而RT可以接收到0x0410;RT回復狀態和數據表示1子地址有數據;BC發送給RT命令0x0412表示從RT的1子地址上取兩個數據;RT回復狀態和數據表示1子地址上的1, 2數據傳給了BC.以上的每個步驟在記錄表中的數據與1553B監控設備監控的數據一致, 由此可見, 利用Netlink套接字編程可以實現BC與RT之間正常通信.

表1 1553B 接口測試記錄表
針對1553B協議提供了基于Netlink機制的通用socket接口, 給熟悉網絡編程的程序員帶來了極大的方便, 并且移植性好.本文采用的是基于linux的1553B協議的實現, 但是就協議本身而言, 與操作系統相關性不是很大.下一步工作是對1553B協議的優化以及多平臺, 多系統上的移植, 進一步完成在工程上的實現.