• 沒有找到結果。

執行命令的程式界面win_do_cmd( )

第四章 支援網路感知之中介軟體設計與架構

4.5 應用程式界面 (API) 設計

4.5.6 執行命令的程式界面win_do_cmd( )

由於我們提出了 win_cmd 的資料結構,所以執行命令的應用程式界面只需 有一個 win_do_cmd 就行了。當應用程式將 win_cmd 資料結構填好要執行的命 令和參數後,只要將該 win_cmd 資料結構當參數傳進 win_do_cmd,WinME 就 會去執行該命令。Table 4 12 顯示了 win_do_cmd 的原型。這邊有一件事要注意 的是,win_do_cmd 並不會直接執行應用程式下達的命令,它只有把要執行命令 的訊息傳遞給 WinME,然後再由 WinME 自行找機會執行。

Table 4-12 wn_do_cmd 的原型 (prototype)

INT32 win_do_cmd ( struct win_cmd* cmd );

我們可以看到win_do_cmd非常的簡潔,只有一個 win_cmd資料結構的參數,

這是因為我們將複雜的事物都由win_cmd封裝起來處理掉了。也因為如此,我們 的中介軟體就不需提供許許多多不同的應用程式界面來處理許多不同的命令。

Table 4-13 顯示了 win_do_cmd 的傳回值。

Table 4-13 win_do_cmd 傳回值

R_OK 命令執行成功

R_FAIL 命令因為某種原因而執行失敗。失敗原因為以字串型

式存放在win_cmd結構中的pv_value欄位可參考 Table 4-11。

R_INV_PARAM 不合法的參數。

R_NOT_SUPPORT 此中介軟體不支援這個命令。

第 5 章 WinME 在 Linux 作業系統下的實作

5.1 軟硬體需求

以下是本論文實作之軟硬體平台:

z 硬體需求:IA-32 Intel architecture compliant computer z 系統核心:Linux kernel version 2.4.32

z 發行套件:Red Hat Linux Release 9 z 發展工具:

„ GNU gcc 3.2.2 (Red Hat Linux 3.2.2-5)

„ GNU ld 2.13.90

„ GNU make 3.79.1

5.2 查詢和控制命令的實作

命令的實作部份是比較簡單的,原則上來講就是應用程式透過 WinME 去跟 系統底層下達命令。而處理系統底層的程式碼是由 WinME 作掉,應用程式只需 查看 WinME 放出來的命令來使用即可。

當應用程式填好 win_cmd 資料結構後呼叫 win_do_cmd 函式時,win_do_cmd 的動作只有把該資料結構的命令和參數欄位包裝成一個訊息封包後傳送給 WinME,然後 win_do_cmd 函式就會等待 WinME 的回傳資訊,等到 WinME 把 資訊回傳之後,再將該訊息的資料取出,填入 win_cmd 資料結構的 value 欄位,

然後再返回呼叫。

因為可能同時會有多個應用程式呼叫 win_do_cmd 函式,所以 WinME 並不是 馬上處理應用程式們所有要求的命令,而是把一些來不及處理的命令訊息先放到 命令佇列中,等到前一個命令處理完後再處理下一個。這邊要再強調一下,當應 用程式呼叫 win_do_cmd 時並不是直接去執行系統底層的程式碼,而是把要執行 系統相關動作的要求傳送給 WinME,請 WinME 幫忙執行。舉個例來說,當行 動管理員程式使用 win_do_cmd 來更改 IP 位址時,更改 IP 位址的動作並不是在 行動管理員的程序下執行的,而是在 WinME 的程序下執行的。

我們不使用類似函式庫的作法,也就是將底層的程式碼實作在 win_do_cmd 中,而是將所要執行的命令以訊息的方式傳送給 WinME 中央控管的好處有:

z WinME 可以控制資源的使用 z WinME 可以幫命令排優先權

z 只有 WinME 處理系統相關動作,win_do_cmd 的移殖可變的容易

缺點為就是當有很多命令在佇列中等待時,應用程式就需要等較長的時間。

Table 5-1 顯示了 win_do_cmd 的虛擬碼,我們可以發現在win_do_cmd中沒有任 何關於系統底層的動作。

Table 5-1 win_do_cmd 的虛擬碼

INT32 win_do_cmd (struct win_cmd* cmd) { pack cmd;

send to WinME;

wiat for WinME’s response;

fill info into cmd.value field;

return;

}

Figure 5-1 win_do_cmd 和中介軟體等序列圖

Figure 5-1 顯示了應用程式呼叫 win_do_cmd和中介軟體等序列圖。Table 5 2 則 顯示了各步驟的說明。

Table 5-2 Figure 5-1 的各步驟說明

步驟 描述

call win_do_cmd 應用程式包裝好 win_cmd 資料結構後,呼叫 win_do_cmd,並且將 win_cmd 當作參數傳入。

pack cmd win_do_cmd 先將 win_cmd 資料結構中關於命令和參數的 欄位封裝成一個命令訊息封包。

send cmd packet 將前一個步驟所產生的命令訊息封包傳送給中介軟體。

queue command 中介軟體將訊息封包放入命令佇列中。

do system code 執行命令佇列裡的應用程式所請求的命令訊息,這時會呼 到到系統函式。

system call return 系統函式執行成功後回傳。

send response packet 中介軟體將執行結果包成封包後傳回給 win_do_cmd fill result into

cmd.value

win_do_cmd 函式將執行結果放入 win_cmd 資料結構的 value 欄位。

return 返回呼叫。

5.3 事件通知機制的實作

這一節會分兩個部份介紹,第一個部分是 WinME 核心的事件服務元件 (Event Service) 的實作,第二部分為系統底層的事件如何傳達給事件服務元件。

5.3.1 事件服務元件

應用程式透過 win_event_init、win_event_register、win_check_event 和 WinME 的事件服務元件溝通。本節就是介紹這一部分在 Linux 系統下的實作方法。

在 Linux 下已經有一個存在的機制 socket,跟我們的網路事件通知機制的設 計很符合,所以我們在 Linux 系統下事件服務元件和應用程式的溝通就採用目前 已存在的機制 socket 完成的。當應用程式呼叫 win_event_init 的時候,事實上就 是透過 socket 和事件服務元件建立一個 Streaming 的連線,而傳回的事件描述子 就是建立連線後的 socket file descriptor。

當應用程式呼叫完 win_event_init 後,WinME 這邊的事件元件也會有一個對 應的 socket file descriptor,所以只要網路底層的事件產生的話,就可以直接使用 該 socket file descriptor 把網路事件傳達給應用程式。

那如何知道哪些事件才是應用程式感興趣的事件呢?我們在事件服務元件 中,每個應用程式所對應的 socket file descriptor 都會有一個對應的表格,該表格 就記錄著應用程式感興趣的事件。於是當網路事件發生時,事件服務元件會去掃 描所有註冊過的 socket file descriptor 所對應的表格,發現有註冊過該事件,就將 送出該網路事件的資訊。

Figure 5-2 顯示了我們的實作方式。所以在我們的事件服務元件中是不曉得為哪

Figure 5-2 Event Service 和應用程式通訊的實作方式

要使用 win_event_register 註冊事件時也是透過 socket 傳達註冊事件的訊息給 事件服務元件。事件服務元件就會把對應的資料加到事件註冊的表格內。

我們知道事件服務元件和應用程式間的溝通是用socket 之後,就可以很簡單 的實作win_check_event這個函式了。我們直接使用select函式來檢查是否有訊息 可以讀取就行了。當發現有訊息可以讀取時,就將該訊息讀進win_event的資料 結構中後返回。沒有訊息可以讀取的時候,就看應用程式傳進來的block參數,

如果是FALSE的話,我們就讓select函式等待 0 秒,如果是TRUE的話,就讓select 函式等待到有訊息可讀取。Table 5-3 顯示了 win_check_event 的實作程式碼。

Table 5-3 win_check_event 在 Linux 下實作程式碼

INT32 win_check_event(

INT32 event_descriptor,

nready = select(event_descriptor + 1, &rset, NULL, NULL, &timeout);

} 本的rtnetlink socket對於我們的WinME而言還是有以下幾點缺失:

z 支援事件的數量不足。

„ 對於本論文的中介軟體而言,rtnetlink 有許多事件尚未支援。如 Link

z 當事件發生時,所提供的資訊無法分類是何種事件發生。

„ e.g. 當網路卡的狀態改變時不論是 MTU 改變,或著是網卡被啟動了,

rtnetlink 都一律只提供 RTM_NEWLINK 型態的事件。如果我們想要知道 是何種事件的發生,就需要把所有的屬性值都記錄下來,然後和之前的作 比較,才可以知道是哪個屬性的值更動了,如要知道是 MTU 改變的事 件,就比較新的 MTU 值和舊的 MTU 值有無改變,這樣顯得就沒有效率。

所以我們就修改 rtnetlink 在核心中實作的方式使之可以在通知事件產生時 一併通知是何種事件的發生。

由於rtnetlink對我們的WinME而言有以上兩點缺失,所以我們會去擴充核心中 原本rtnetlink的實作方法,除了增加新的觸發點1外,還增加了一些屬性

(Attribute)。觸發點用於提供原本netlink未提供的事件,新的屬性可以幫助我們分 辨事件。

5.3.3 網路層事件

本論文所提之中介軟體中目前只定義了三個網路事件,分別為 IP_CHANGE、DEFAULT_GATEWAY_CHANGE 和

ROUTING_TABLE_CHANGE,而這三個事件在 rtnetlink 中已經有支援了,所以 這部份的實作就我們就直接將從 rtnetlink 得的訊息資訊重新包裝成本論文所提 中介軟體的所定的訊息格式即可。

當 rtnetlink 傳來的訊息型態為 RTM_NEWADDR 時,就可以知道網路界面卡 的 IP 位址改變了,此時就可以將改變後的 IP 位址從 IFA_ADDRESS 的屬性中讀 取出來,

當 rtnetlink 傳來的訊息型態為 RTM_NEWROUTE 或著是 RTM_DELROUTE 時,我們就可以知道是路由表的資訊改變了,NEW 表示有資料新增進來了,DEL 表示有資料被刪除。而目的地、閘道及出去的網路界卡資料分別為屬性

RTA_DST、RTA_GATEWAY、RTA_OIF。如果要知道是否為預設閘道改變的資 訊,我們就直接看 RTA_DST 屬性的長度是否為 0 就知道了。

5.3.4 NETDEV_XXX 相關事件

在 Linux 的核心中已經提供NETDEV_XXX的訊息通知機制,就是使用 notifier chain[38]。由於 rtnetlink在核心中實作時已經有註冊一個 notifier block,所以我們也毋需註冊新的notifier block,而直接修改核心中rtnetlink實作的 程式碼。由於原本的實作所提供的資訊太少了,不論是何種的NETDEV事件,原 本的rtnetlink全部歸類為RTM_NEWLINK或RTM_DELLINK的型態。

Table 5-4 顯示了rtnetlink在核心中處理NETDEV_XXX事件的程式碼,我們可以 發現它所提供的資訊傳到使用者空間的應用程式時,關於NETDEV_XXX的訊息 完全不見了,而且有很多NETDEV_XXX的事件都沒經過特別處理,一律規類在 default中處理,而且處理方式就只是將網路界面卡的資料傳上使用者空間,然後 給個型態 RTM_NEWLINK而已。

因此我們擴充了這一部份的程式碼,為了直接分辨系統層事件或著是驅動層 事件,所以我們增加了兩個訊息型態分別為 RTM_SYSTEM_EVENT 及

RTM_DRIVER_EVENT,然後又增加了一個屬性為 IFLA_WIN_EVENT 用來指名 是何種 NETDEV_XXX 的事件發生。為了保持原本 rtnetlink 在這邊的運作,我們 會自己再送一個 rtnetlink 的訊息出來,然後裡面填的資料就是 WinME 想要知道 的資料。因此當 notifier chain 呼叫到 rtnetlink 的 notifier block 時,是會送出兩個

Table 5-4 rtnetlink 在核心中 notifier block 的部分程式碼

case NETDEV_UNREGISTER:

rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);

win_rtmsg_ifinfo(event, RTM_SYSTEM_EVENT, dev, ~0U);

break;

case NETDEV_REGISTER:

rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);

win_rtmsg_ifinfo(event, RTM_SYSTEM_EVENT, dev, ~0U);

break;

5.3.5 其它的事件

5.3.5 其它的事件