OpenWrt 基礎軟體模組之procd
OpenWrt 基礎軟體模組之procd
Openwrt 支援模組化程式設計,增加新功能非常簡單。但是一些通用的基礎模組必須包含,他們是OpenWrt核心。
如:實用基礎庫libubox、系統匯流排ubus、網路介面管理模組netifd、核心工具模組ubox、服務管理模組procd。
服務管理模組procd
通常的嵌入式系統均有一個守護程式,該守護程式監控系統程式的狀態,如果某些系統程式異常退出,將再次啟動這些程式。procd 就是這樣一個程式,它是使用C語言編寫 的,一個新的OpenWrt程式管理服務。
它通過init指令碼來將程式資訊加入到procd的資料庫中來管理程式啟動,這是通過ubus匯流排呼叫來實現,可以防止程式的重複啟動呼叫
procd的程式管理功能主要包含3個部分:
- reload_config: 檢查配置檔案是否發生變化,如果有變化則通知procd程式
- procd守護程式: 接收使用者的請求,增加或刪除所管理的程式,並監控程式的狀態,如果發現程式退出,則再次啟動程式
- procd.sh: 提供函式封裝procd提供系統匯流排方法,呼叫者可以非常便利的使用procd提供的方法
程式碼:https://git.openwrt.org/?p=project/procd.git;a=tree
reload_config
工作原理:當在命令列執行reload_config時,會對系統中的所有配置檔案生成MD5值,並且和應用程式使用的配置檔案MD5值進行比較(對/var/run/config.md5檔案進行比較),如果不同就通過ubus匯流排通知procd配置檔案發生改變,如果應用程式在啟動時,向procd註冊了配置觸發服務,那就將呼叫 reload函式重新讀取配置檔案,通常是程式退出再啟動。如果配置檔案沒有改變將不會呼叫,這將節省系統CPU資源
兩點注意事項
- 配置檔案的真實配置內容發生改變之後才會呼叫,如果 增加空行和註釋並不會引起 配置檔案的實質內容改變
- 當系統啟動時 ,會執行reload_config將 初始配置檔案摘要值 儲存為**/var/run/config.md5** 檔案中,下次再執行reload_config就是與這檔案裡面的MD5值進行比較的
工作原理詳解:
我們以防火牆的配置檔案發生改變為例來說明
- ①當手動執行reload_config時,首先將目錄/etc/config目錄下的所有檔案通過“uci show”命令輸出其配置到“/var/run/config.check” 目錄下,這個命令將過濾配置檔案增加空行和註釋的情況
- ②初始系統啟動時的配置檔案摘要值儲存在檔案/var/run/config.md5 中,我們通過 “md5sum –c”命令來從檔案中讀取MD5值並驗證是否和現有的配置檔案MD5是否一致, 如果不一致則就呼叫ubus方法通知procd程式配置檔案發生改變
- ③當procd知道配置檔案發生改變後,procd就會呼叫/etc/init.d/firewall reload來處理配置檔案改變,其他配置檔案沒有改變的程式,系統將不會花費資源進行處理
- ④最後將現在執行中的配置檔案MD5值儲存到/var/run/config.md5中
procd程式
procd程式向ubus匯流排註冊了 service和system物件 .ubus list
命令可以才看到。
service物件
serveis物件提供的方法,主要有3部分功能: 程式的管理、檔案觸發器(trigger)、配置驗證服務(validate)
方 法 含 義 set 程式如果存在,則修改已經註冊的程式資訊,如果不存在則增加,最後啟動註冊的程式 add 增加註冊的程式 list 如果不帶引數,則列出所有註冊的程式和其資訊 delete 刪除指定服務程式,在結束程式時呼叫,例如停止防火牆會進行以下呼叫: ubus call service delete ‘{“name”:”firewall”}’ event 發出事件,例如 reload_config 就使用該方法來通知配置發生改變 validate 檢視所有的驗證服務
set方法:
上面的3個功能都是通過set方法增加到procd儲存的記憶體資料庫中。資料庫以服務名稱作為其主鍵
共有5個引數:第一個引數為被管理的服務程式名稱;第二個引數為啟動指令碼絕對路徑;第三個引數為程式例項資訊,例如可執行程式路徑和程式的啟動引數等;第四個引數為觸發器;第五個引數為配置驗證項(前3個引數是必須要傳遞的,後面兩個引數可選。)
delete方法:
在刪除時使用 delete 方法
共有兩個引數:第一個引數為服務名稱,第二個引數為程式例項名稱,可以不指定例項名稱
list方法:
查詢時使用 list 方法
共有兩個引數:第一個引數為服務名稱,第二個引數是布林值,表示是否輸出其詳細資訊,預設為不輸出詳細資訊(該方法可以不帶任何引數,表示查詢所有註冊的服務資訊)
ubus -v list service 'service' @7e08a163 "set":{"name":"String","script":"String","instances":"Table","triggers":"Array","validate":"Array","autostart":"Boolean","data":"Table"} "add":{"name":"String","script":"String","instances":"Table","triggers":"Array","validate":"Array","autostart":"Boolean","data":"Table"} "list":{"name":"String","verbose":"Boolean"} "delete":{"name":"String","instance":"String"} "signal":{"name":"String","instance":"String","signal":"Integer"} "update_start":{"name":"String"} "update_complete":{"name":"String"} "event":{"type":"String","data":"Table"} "validate":{"package":"String","type":"String","service":"String"} "get_data":{"name":"String","instance":"String","type":"String"} "state":{"spawn":"Boolean","name":"String"}
例如:
- ①增加程式: 如果hello程式需要procd來管理,那麼我們使用ubus命令將hello程式加入的procd的記憶體資料庫中。下面命令傳遞了4個引數,第一個引數設定被管理的服務程式名稱為“hello”。第二個引數設定啟動指令碼絕對路徑“/etc/init.d/hello”。第三個引數設定了程式例項資訊,例項的啟動命令為“/bin/hello”,啟動引數為“-f -c bjbook.net”,並設定程式意外退出的重生引數(respawn)為預設值。第四個引數為觸發器,收到檔案“hello” 的“config.change”訊息後執行指令碼“/ect/init.d/hello”並傳遞“reload”引數
ubus call service add '{"name":"hello", "script":"/etc/init.d/hello","instances":{"instance1":{ "command":["/bin/hello","-f","-c","bjbook.net"],"respawn":[ ] }},"triggers": [ ["config.change",["if", ["eq","package", "hello" ], ["run_script","/ect/init.d/hello", "reload" ] ] ] ]}'
- ②刪除程式: 引數傳遞程式的名字即可
ubus call service delete '{"name":"hello"}'
- ③檢視註冊的程式資訊: 也可以不指定名稱,將輸出所有的管理列表。“verbose”為真,表示輸出其詳細資訊
ubus call service list '{"name":"hello","verbose":true}'
- ④傳送事件: 第一個引數含義為事件型別,現在只支援“config.change”事件訊息; 第二個參數列示檔案“hello”,是指在目錄“/etc/config”下的檔案。在配置檔案發生改變 時呼叫。通知 procd 程式配置檔案 hello 發生了改變
ubus call service event '{"type":"config.change","data":{"package":"hello"}}'
system物件
方 法 | 含 義 |
---|---|
board | 系統軟硬體版本資訊,包含 4 個部分,分別為核心版本、主機名稱、系統 CPU 類 型資訊和版本資訊,版本資訊從/etc/openwrt_release 檔案讀出 |
info | 當前系統資訊,包含 5 部分,分別為系統啟動時間、系統當前時間、系統負載情況、 記憶體和交換分割槽佔用情況等 |
upgrade | 設定 service_update 為 1 |
watchdog | 設定 watchdog 資訊,還存在問題,例如如果本身為 0 的情況 |
signal | 向指定 pid 的程式發訊號,是通過 kill 函式來實現的 |
nandupgrade | 執行升級 |
procd.sh
- 使用ubus方法來進行管理時其傳遞引數複雜並且容易出錯,procd.sh將這些引數拼接組織功能封裝為函式,每一個需要被procd管理的程式都使用它提供的函式進行註冊 。
- 這些函式組織為JSON格式 的訊息然後通過ubus匯流排向procd程式傳送訊息。這些函式將不同功能封裝為不同的函式,構建特定的JSON訊息來表達特定的功能用法
- 函式命名規範: procd.sh提供的API命名非常規範,除了有一個uci_validate_section函式 用於驗證UCI 配置檔案以外,其他所有的函式均是以“procd_”開頭
procd_open_ trigger 函式建立一個觸發器陣列,在增加了所有的觸發器之後,呼叫procd_close_trigger函式 來結束觸發器陣列的增加
procd_add_reload_trigger:增加配置檔案觸發器,每次配置檔案的修改,如果呼叫了reload_config時,當前例項都被重啟。有一個可選的引數為配置檔名稱。其實它在內部是呼叫 procd_open_trigger、procd_add_config_trigger 和 procd_close_trigger 這3個函式來增加觸發器
procd_open_instance: 開始增加一個服務例項
procd_set_param: 設定服務例項的引數值。通常會有以下幾種型別的引數:(每次只能使用一種型別引數,其後是這個型別引數的值)
- command:服務的啟動命令列
- respawn:程式意外退出的重啟機制及策略,它需要有 3 個設定值。第一個設定為 判斷異常失敗邊界值(threshold),預設為 3600 秒,如果小於這個時間退出,則 會累加重新啟動次數,如果大於這個臨界值,則將重啟次數置 0。第二個設定為 重啟延遲時間(timeout),將在多少秒後啟動程式,預設為 5 秒。第三個設定是總 的失敗重啟次數(retry),是程式永久退出之前的重新啟動次數,超過這個次數進 程退出之後將不會再啟動。預設為 5 次。也可以不帶任何設定,那這些設定都是 預設值
- env:程式的環境變數
- file:配置檔名,比較其檔案內容是否改變
- netdev:繫結的網路裝置(探測 ifindex 更改)
- limits:程式資源限制
**procd_close_instance:**完成程式例項的增加
例如 rpcd對procd函式的使用,這個示例可以用於大多數應用程式。PROG變數在前面已設定為/bin/rpcd。該示例將最終呼叫以下命令完成程式的增加 :
ubus call service set '{"name":"rpcd", "script":"/etc/init.d/rpcd",
"instances": {"instance1":{ "command": ["/bin/rpcd"] } } }'
procd_open_instance
procd_set_param command "$PROG"
procd_close_instance
procd_open_validate: 開啟一個驗證陣列,是和 procd_close_validate 函式一起使用
procd_close_validate: 關閉一個驗證陣列。
演示案例:
下面是軟體包firewall使用 procd 來對防火牆配置的觸發器和驗證
procd_add_reload_trigger firewall
procd_open_validate
validate_firewall_redirect
validate_firewall_rule
procd_close_validate
procd_open_service(name, [script]): 至少需要一個引數,第一個引數是例項名稱, 第二個引數是可選引數為啟動指令碼。該函式僅在在 rc.common 中呼叫,用於建立一個新的 procd 程式服務訊息
procd_close_service: 該函式不需要引數,僅在 rc.common 中呼叫,完成程式管理服務的增加
procd_kill: 殺掉服務例項(或所有的服務例項)。至少需要一個引數,第一個參 數是服務名稱,通常為程式名,第二個是可選引數,是程式例項名稱,因為可能有多個進 程示例,如果不指定所有的例項將被關閉。該函式在 rc.common 中呼叫,使用者從命令列調 用 stop 函式時會使用該函式殺掉程式
uci_validate_section: 呼叫 validate_data 命令註冊為驗證服務。在配置發生改變 後對配置檔案的配置項合法性進行校驗。驗證服務是在程式啟動時通過 ubus 匯流排註冊到 procd 程式中
#輸入以下命令,可以看到系統所有註冊的驗證服務
ubus call service validate
{
"firewall": {
"redirect": {
"dest": "string",
"dest_ip": "cidr",
"dest_port": "or(port, portrange)",
"proto": "or(uinteger, string)",
"src": "string",
"src_dport": "or(port, portrange)",
"src_ip": "cidr",
"target": "or(SNAT, DNAT)"
},
"rule": {
"dest": "string",
"dest_port": "or(port, portrange)",
"proto": "or(uinteger, string)",
"src": "string",
"src_port": "or(port, portrange)",
"target": "string"
}
},
...............
- 這些驗證服務是在啟動指令碼中增加驗證服務來實現,如下程式碼所示,service_triggers 函式是預定義好的回撥函式,在每一個增加服務結束後會自動呼叫,使用者不必關注如何 呼叫。validate_cron_section 函式是真正的將驗證服務加入 procd 的驗證服務中。它呼叫 uci_validate_section 函式,而 uci_validate_section 函式進一步呼叫 validate_data 程式
validate_cron_section() {
uci_validate_section system system "${1}" \
'cronloglevel:uinteger'
}
service_triggers()
{
procd_add_validation validate_cron_section
procd_add_reload_trigger "hello"
}
rc.common
rc.common在1209及之前的版本中並不支援procd啟動,在1407版本中增加了專門針對procd的啟動,該指令碼向前相容
在軟體模組的啟動指令碼中如果沒有定義USE_PROCD變數:則啟動流程和之前完全相同
如果定義了 USE_PROCD變數:對start、stop 和 reload函式進行重新定義,在呼叫這些函式時,將呼叫start_service、stop_service和 reload_service函式等
-
procd預定義的函式如下:
如果在自己的啟動指令碼中定義了USE_PROCD那就呼叫這些函式。在rc.common中重新定義了start函式,相當於過載了這些函式
函 數 | 含 義 |
---|---|
start_service | 向 procd 註冊並啟動服務,是將在 services 所管理物件裡面增加了一項 |
stop_service | 讓 procd 解除註冊,並關閉服務, 是將在 services 中的管理物件刪除 |
service_triggers | 配置檔案或網路介面改變之後觸發服務重新讀取配置 |
service_running | 查詢服務的狀態 |
reload_service | 重啟服務,如果定義了該函式,在 reload 時將呼叫該函式,否則再次呼叫 start 函式 |
service_started | 用於判斷程式是否啟動成功 |
編寫一個procd啟動指令碼(注意與init指令碼不一樣)
#!/bin/sh /etc/rc.common
#使用/etc/rc.common來解釋指令碼
USE_PROCD=1 #表示使用procd來管理程式
START=15
STOP=85
PROG=/bin/hello #PROG變數用來給程式的啟動指令碼賦值,用於啟動應用程式
#validate_hello_section函式:驗證了配置檔案hello中的delay變數否為整型值,並且在合理的(1~200)範圍內
validate_hello_section()
{
uci_validate_section hello system globe \
'delay:uinteger(1:200)'
}
#start_service函式:負責程式的啟動
start_service() {
echo "start HelloRoute!"
validate_hello_section || {
echo "hello validattion failed!"
return 1
}
#在引數驗證完成後,呼叫procd_open_instance 數發起例項增加,接著呼叫了procd_set_param函式來設定了啟動命令和啟動引數,再接著respawn設定其程式意外退出的重啟機制及策略為預設值,最後呼叫procd_close_instance函式完成例項的增加。注意procd管理的程式需要執行在前臺,即不能呼叫daemon或類似函式
procd_open_instance
procd_set_param command "$PROG" –f -w bjbook.net
procd_set_param respawn
procd_close_instance
}
#service_triggers函式:增加觸發器,我們增加了對配置檔案hello的觸發服務。當hello檔案發生改變後,如果呼叫了 reload_config命令,將觸發呼叫reload_service函式
service_triggers()
{
procd_add_reload_trigger "hello"
}
#reload_service函式:在傳遞reload引數時進行呼叫,如果沒有該函式,將會呼叫預設start函式
reload_service()
{
stop
start
}
備註: 在執行該啟動指令碼時,如果需要對procd指令碼進行除錯,可以設定PROCD_DEBUG變數為 1,這樣可以輸出向ubus匯流排呼叫的引數資訊。例如: PROCD_DEBUG=1 /etc/init.d/hello start
相關文章
- Python基礎之模組Python
- python 基礎之模組與包Python
- 二、Ansible基礎之模組篇
- Django基礎八之認證模組---authDjango
- Python基礎——模組Python
- ansible基礎-模組
- python基礎之-sys模組、os模組基本介紹(未完成)Python
- Python基礎12(模組與datetime模組)Python
- Swoole 原始碼分析——基礎模組之 Pipe 管道原始碼
- Node.js基礎知識之Path模組Node.js
- 前端模組化基礎前端
- python基礎 之 正規表示式和re模組Python
- 4G模組軟體指南 | 必讀篇之模組資訊(hmeta)
- 豬行天下之Python基礎——10.2 Python常用模組(下)Python
- 豬行天下之Python基礎——10.1 Python常用模組(上)Python
- 9.Vue之webpack打包基礎---模組化思維VueWeb
- python基礎學習16—-模組Python
- Javascript模組化開發基礎JavaScript
- python 基礎筆記——常用模組Python筆記
- Python基礎:常用系統模組Python
- 【python基礎】os模組的使用Python
- 中介軟體和Auth模組
- 豬行天下之Python基礎——2.1 Python註釋與模組Python
- 2.3、mybatis原始碼分析-基礎模組之型別轉換MyBatis原始碼型別
- 訊息型中介軟體之RabbitMQ基礎使用MQ
- 零基礎學軟體之HTML語言HTML
- Profinet遠端IO模組:模擬量模組_軟體組態說明
- Python基礎(九) 常用模組彙總Python
- Python pip(管理模組工具)基礎用法Python
- .NET Core基礎篇之:白話管道中介軟體
- 軟體測試基礎
- python之tuple元組,基礎篇Python
- Vue.JS基礎- 外掛、模組化Vue.js
- Python模組之urllib模組Python
- python模組之collections模組Python
- Django基礎七之CBV裝飾器和中介軟體Django
- 如何組織軟體模組的程式碼結構?
- Linux基礎之使用者和組Linux