這就是 gensio(發音為 gen'-see-oh),一個用於提供各種流(和資料包)I/O 類型的一致視圖的框架。您建立一個 gensio 物件(或一個 gensio),並且可以使用該 gensio,而不必了解太多底層的情況。您可以將 gensio 堆疊在另一個 gensio 之上以添加協定功能。例如,您可以建立一個 TCP gensio,在其上堆疊 SSL,並在其上堆疊 Telnet。它支援多個網路 I/O 和串行埠。它還支援聲音介面。堆疊在其他 gensios 上的 gensios 稱為過濾器。
您可以對接收連接埠執行相同的操作。您可以設定 gensio 接受器來接受堆疊中的連線。因此,在我們前面的範例中,您可以設定 TCP 來偵聽特定端口,並在連接進入時自動將 SSL 和 Telnet 堆疊在頂部,並且在一切準備就緒之前不會通知您。
gensio 適用於 Linux、BSD、MacOS 和 Windows。在 Windows 上,它為您提供了一個支援單執行緒(也支援多執行緒)的事件驅動介面(具有可用的阻塞介面),以簡化大量 I/O 的程式設計。它對於簡化可移植 I/O 驅動程式碼的編寫大有幫助。
gensio 的一個非常重要的功能是,它使建立加密和經過身份驗證的連接比沒有它更容易。除了基本的金鑰管理之外,它實際上並不比 TCP 或其他任何東西難。如果需要,它可以為控制身份驗證過程提供更大的靈活性。它真的很容易使用。
請注意,gensio(5) 手冊頁包含有關各個 gensio 類型的更多詳細資訊。
有關從原始程式碼建置此程式的說明,請參閱末尾的“建置”部分。
有幾個使用 gensios 的工具可用,既可以作為範例,也可以用於嘗試。這些都是:
一個類似 sshd 的守護程式,使用 certauth、ssl 和 SCTP 或 TCP gensios 來建立連線。它使用標準 PAM 身份驗證並使用 ptys。詳細資訊請參閱 gtlsshd(8)。
FAQ.rst 中有一個名為「如何在 Windows 上運行 gtlsshd」的項目,請參閱該項目以及下面的「在 Windows 上建置」部分以了解更多詳細信息,因為您必須處理一些棘手的事情。
庫中提供了以下 gensios:
一個 gensio 接受器,將 gensio 堆疊字串作為參數。這允許您使用 gensio 作為接受者。當 conacc 啟動時,它會開啟 gensio,當 gensio 開啟時,它會向接受者報告一個新的子程序。當子進程關閉時,它會嘗試再次開啟子進程並再次執行該過程(除非在 conacc 上停用了接受)。
為什麼要使用這個?假設在 ser2net 中您想要將一個序列埠連接到另一個序列埠。你可以有這樣的連結:
connection : &con0
accepter : conacc,serialdev,/dev/ttyS1,115200
connector : serialdev,/dev/ttyS2,115200
它將 /dev/ttyS1 連接到 /dev/ttyS2。如果沒有 conacc,您就無法使用serialdev 作為接受者。如果您想透過序列埠進行加密的身份驗證登錄,它還允許您在序列埠上使用 gtlsshd。如果您使用以下命令執行 gtlsshd:
gtlsshd --notcp --nosctp --oneshot --nodaemon --other_acc
' conacc,relpkt(mode=server),msgdelim,/dev/ttyUSB1,115200n81 '
您可以連線:
gtlssh --transport ' relpkt,msgdelim,/dev/ttyUSB2,115200n81 ' USB2
這將在串行端口上創建可靠的資料包傳輸。需要 mode=server 才能使 relpkt 作為伺服器運行,因為它通常會作為客戶端運行,因為它不會作為接受器啟動。 ssl gensio(透過傳輸運作)需要可靠的通信,因此它不會直接透過序列埠運作。
是的,它看起來像一堆字母。
濾波器 gensio 位於聲音 gensio 之上,並執行音頻頻移鍵控調製解調器,如 AX.25 業餘無線電中使用的那樣。
用於分組無線電的業餘無線電協定。要充分利用它,您需要編寫程式碼,因為它使用通道和 oob 資料來獲取未編號的信息,但如果您需要的只是一個通訊通道,則只需使用 gensiot 即可完成基本操作。例如,如果您想透過無線電與某人聊天,並且兩台機器上的 Kiss 連接埠都在 8001 上,則在接受機器上您可以運行:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),kiss,conacc,tcp,localhost,8001 '
它將掛接到 TNC 並等待位址 AE5KM-1 上的連線。然後你可以運行:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"),kiss,tcp,localhost,8001 '
在另一台機器上。這將透過 TNC 0 連接到具有給定位址的另一台機器。然後,您在其中一個中輸入的任何內容都會顯示在另一個上,一次一行。輸入“Ctrl-D”退出。 「stdio(self)」部分關閉原始模式,因此一次是一行,您會得到本地迴聲。否則,您輸入的每個字元都會發送一個資料包,而您將看不到您輸入的內容。
若要連接到 N5COR-11 AX.25 BBS 系統,您可以執行下列操作:
gensiot -i ' xlt(nlcr),stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,N5COR-11,AE5KM-2"),kiss,tcp,localhost,8001 '
大多數 BBS 系統使用 CR 而不是 NL 來換行,因此 xlt gensio 用於翻譯傳入的這些字元。
當然,這就是 gensio,您可以將任何可用的 gensio 放在您想要的 ax25 下。因此,如果您想在沒有無線電的情況下進行遊戲或測試,您可以透過 UDP 多播進行 ax25。這是接受方:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),conacc, '
' udp(mcast="ipv4,224.0.0.20",laddr="ipv4,1234",nocon), '
' ipv4,224.0.0.20,1234 '
這是連接器一側:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"), '
' udp(mcast="ipv4,224.0.0.20",laddr="ipv4,1234",nocon), '
' ipv4,224.0.0.20,1234 '
Kiss 不是必要的,因為 UDP 已經是面向資料包的媒體。或者您可以使用 grreflector 程式來建立模擬無線電情況。在機器“radiopi2”上,運行:
greflector kiss,tcp,1234
這將創建一個程序,將所有收到的輸入反映到所有其他連接。然後在接受方:
gensiot -i ' stdio(self) ' -a
' ax25(laddr=AE5KM-1),kiss,conacc,tcp,radiopi2,1234 '
和連接側:
gensiot -i ' stdio(self) '
' ax25(laddr=AE5KM-2,addr="0,AE5KM-1,AE5KM-2"),kiss,tcp,radiopi2,1234 '
測試程式碼使用反射器進行一些測試,因為它使用起來很方便。
這些都在 gensio(5) 中有詳細記錄。除非另有說明,這些都可以作為接受者或連接 gensios。
您可以建立自己的 gensios,並將它們註冊到庫中,並將它們與其他 gensios 一起堆疊。
最簡單的方法是從 gensio 竊取可以實現您想要的功能的程式碼,然後修改它以建立您自己的 gensio。不幸的是,沒有關於如何執行此操作的良好文件。
包含檔案 include/gensio/gensio_class.h 具有主 gensio 函式庫和 gensio 之間的介面。 gensio 呼叫全部透過一個帶有數字的函數來識別所請求的函數。你必須將所有這些映射到實際操作。這有點痛苦,但它使向前和向後相容性變得更加容易。
以這種方式創建您自己的 gensio 相當複雜。類似這樣的狀態機可能非常複雜。清理是最難的部分。您必須確保您不參與所有回調,並且在關閉時的競爭條件下不會回調任何計時器。只有最簡單的gensios(echo、dummy)、奇怪的gensios(conadd、keepopen、stdio)和有通道的gensios(mux、ax25)直接實作該介面。其他一切都使用 include/gensio/gensio_base.h。 gensio_base 為 gensio 提供基本狀態機。它有一個過濾器部分(可選)和一個低級 (ll) 部分(不是)。
過濾器介面有資料透過它處理。這用於 ssl、certauth、ratelimit 等。這些都使用 gensio_ll_gensio (用於將一個 gensio 堆疊在另一個 gensio 之上)作為 ll。
每個終端gensios都有自己的ll並且通常沒有過濾器。對於基於檔案描述符 (fd) 的 lls,使用 gensio_ll_fd。還有一個用於 IPMI 區域網路串列 (ipmisol) 和聲音的 ll。顯然,大多數終端 gensios(tcp、udp、sctp、串列埠、pty)都使用 fd ll。
一旦你有了 gensio,你就可以將其編譯為模組並將其貼到 $(moduleinstalldir)/<version> 中。然後 gensio 就會拿起它並使用它。您也可以將其連結到您的應用程式並從您的應用程式執行 init 函數。
mdns gensio 已經討論過了,但是 gensio 函式庫提供了一個易於使用的 mDNS 介面。它的包含檔案位於 gensio_mdns.h 中,您可以使用 gensio_mdns(3) 手冊頁來獲取有關它的更多資訊。
要使用 gensiot 建立 mdns 連接,假設您已設定 ser2net 並啟用了 mdns,如下所示:
connection : &my-port
accepter : telnet(rfc2217),tcp,3001
connector : serialdev,/dev/ttyUSB1,115200N81
options :
mdns : true
然後你可以使用 gensiot 連接到它:
gensiot ' mdns,my-port '
gensiot 將尋找伺服器、連接埠以及是否啟用了 telnet 和 rfc2217 並建立連線。
此外,還有一個 gmdns 工具可以讓您進行查詢和廣告,並且 gtlssh 可以進行 mDNS 查詢以查找服務。如果您有 ser2net 的安全性驗證登錄,並且在 ser2net 上啟用 mdn,例如:
connection : &access-console
accepter : telnet(rfc2217),mux,certauth(),ssl,tcp,3001
connector : serialdev,/dev/ttyUSBaccess,115200N81
options :
mdns : true
它使設置非常方便,您可以這樣做:
gtlssh -m access-console
沒錯,您可以直接使用連線名稱,無需知道主機、是否啟用了 telnet 或 rfc2217、或連接埠是什麼。當然,您仍然需要按照這些說明在 ser2net 伺服器上設定金鑰等。
gensio 有一個事件驅動的物件導向的介面。也提供同步介面。您在 gensio 中處理兩個主要物件:gensio 和 gensio 接受器。 gensio 提供了一個通訊接口,您可以在其中連接、斷開、寫入、接收等。
gensio 接受器可讓您接收傳入連線。如果連接進來,它會給你一個 gensio。
此介面是事件驅動的,因為它在大多數情況下是完全非阻塞的。如果你打開一個 gensio,你給它一個回調,當連接建立或連接失敗時將調用該回調。關閉時也一樣。寫入將返回接受的位元組數,但它可能不會佔用所有位元組(甚至任何位元組),並且呼叫者必須考慮到這一點。
為了方便起見,開啟和關閉介面都有一個輔助阻塞介面。這些以 _s 結尾。這是為了方便,但這不是必需的,使用它們必須小心,因為您不能真正從回調中使用它們。
說到回調,從 gensio 到用戶的資料和資訊是透過函數回調完成的。讀取數據,當 gensio 準備好寫入時,數據會在回調中返回。類似的介面用於從用戶到gensio層的調用,但它對用戶是隱藏的。這種介面很容易擴展,可以輕鬆添加新操作而不破壞舊介面。
該庫提供了多種創建 gensio 或 gensio 接受器的方法。主要方法是str_to_gensio()和str_to_gensio_accepter()。這些提供了一種將 gensios 或接受器堆疊指定為字串並建構的方法。一般來說,如果可以的話,您應該使用這個介面。
一般來說,對效能不敏感的介面是基於字串的。您將在 gensio_control 以及讀寫介面中的輔助資料中看到這一點,以控制寫入的某些方面。
該庫還提供了透過單獨建立每個 gensios 來設定 gensios 的方法。在某些情況下,這可能是必要的,但它限制了使用 gensio 庫擴展時的新功能的能力。
如果 gensio 支援多個流(如 SCTP),則流編號將透過「stream=n」在 auxdata 中傳遞。流不是單獨進行流量控制的。
另一方面,通道是同一連接上的單獨資料流。通道被表示為單獨的 gensios,並且它們可以單獨進行流量控制。
使用 gensios 時可能需要處理一些包含檔案:
這些大部分都記錄在手冊頁中。
要建立您自己的 gensios,您可以使用以下包含檔案:
每個包含檔案都有大量有關各個呼叫和處理程序的文件。
gensio 有自己的一組錯誤,可將其從作業系統錯誤中抽象化(名為 GE_xxx),並在錯誤報告中提供更大的靈活性。這些位於 gensio_err.h 包含檔案中(從 gensio.h 自動包含),並且可以使用 gensio_err_to_str() 將數字轉換為有意義的字串。零被定義為不是錯誤。
如果出現作業系統無法辨識的錯誤,則傳回GE_OSERR,並透過OS handler日誌介面上報日誌。
關於gensio 的一個有點煩人的事情是,它要求您提供一個作業系統處理程序(struct gensio_os_funcs)來處理作業系統類型的函數,例如記憶體分配、互斥體、處理檔案描述符的能力、計時器和時間以及其他一些東西。
該庫確實提供了幾個作業系統處理程序。您可以呼叫 gensio_alloc_os_funcs() 為您的系統(POSIX 或 Windows)指派預設值。您可以查看該手冊頁以獲取更多詳細資訊。這通常是您的系統效能最佳的選項。
對於 POSIX 系統,glib 和 TCL 的作業系統處理程序可用,透過 gensio_glib_funcs_alloc() 和 gensio_tcl_funcs_alloc() 進行指派。這些確實無法很好地工作,特別是從效能角度來看,glib 和 TCL 的 API 並沒有很好地設計用於 gensio 的功能。 TCL只能支援單執行緒操作。 glib 多執行緒操作一次只有一個執行緒等待 I/O。但它們確實有效,測試也與它們一起運行。這些在 Windows 上不可用,因為 glib 的抽像很差,而且 TCL 缺乏動力。
但如果您使用的是其他東西,例如 X Windows 等,它有自己的事件循環,您可能需要根據您的需求進行調整。但好處是您可以做到這一點,並將 gensio 與幾乎任何東西整合。
還有一個等待者接口,它提供了一種在運行事件循環時等待事件發生的便捷方法。這就是您通常進入事件循環的方式,因為它提供了一種方便的方式來在您完成並需要離開循環時發出信號。
這方面的文檔位於:
內含/gensio/gensio_os_funcs.h
gensio 庫完全支援線程並且是完全線程安全的。然而,它在 POSIX 系統上使用訊號,在 Windows 系統上使用 COM,因此需要一些設定。
「主」執行緒應該在啟動時呼叫 gensio_os_proc_setup() ,並在完成時呼叫 gensio_os_proc_cleanup() 。這會設定訊號和訊號處理程序、Windows 上的執行緒本地儲存以及其他類型的東西。
您可以從已使用 gensio_os_new_thread() 設定的執行緒中產生新執行緒。這為您提供了一個基本的作業系統線程,並為 gensio 進行了正確的配置。
如果您想在 gensio 中使用透過其他方式建立的線程,只要該線程建立另一個線程並且不執行任何阻塞函數(任何類型的等待、後台處理、以 _s 結尾的函數,如 read_s、等)您不必設定它們。這樣,某些外部執行緒就可以寫入資料、喚醒另一個執行緒或執行類似的操作。
如果外部執行緒需要做這些事情,它應該呼叫 gensio_os_thread_setup()。
如同在線程部分中所提到的,Unix 上的 gensio 函式庫使用訊號進行執行緒間喚醒。我仔細一看,確實沒有其他辦法可以乾淨地做到這一點。但是 Windows 也有一些類似訊號的東西,這些東西在 gensio 中也可用。
如果您使用 gensio_alloc_os_funcs(),您將使用傳入的 IPC 訊號來獲得作業系統函數。如果您想要預設值 SIGUSR1,您可以傳入 GENSIO_OS_FUNCS_DEFAULT_THREAD_SIGNAL 作為訊號。你使用的訊號會被gensio屏蔽並接管,你無法使用。
gensio 也為一些訊號提供了一些通用處理。在 Unix 上,它將透過 gensio_os_proc_register_reload_handler() 函數處理 SIGHUP。
在 Windows 和 Unix 上,您可以使用 gensio_os_proce_register_term_handler(),它將處理終止請求(Unix 上的 SIGINT、SIGTERM、SIGQUIT)和 gensio_os_proc_register_winsize_handler()(Unix 上的 SIGCH)。這些資訊如何透過 Windows 進入有點混亂,但對使用者來說是不可見的。
所有回調均來自等待例程的等待,而不是來自訊號處理程序。這應該會大大簡化你的生活。
您可以查看手冊頁以獲取有關所有這些內容的更多詳細資訊。
要建立 gensio,一般方法是使用格式正確的字串呼叫str_to_gensio()
。該字串的格式如下:
<類型>[([<選項>[,<選項[...]]])][,<類型>...][,<結束選項>[,<結束選項>]]
end option
適用於終端 gensios,或位於堆疊底部的選項。例如, tcp,localhost,3001
將建立一個連接到 localhost 上的連接埠 3001 的 gensio。對於串口,例如serialdev,/dev/ttyS0,9600N81
將建立到串列埠/dev/ttyS0的連接。
這使您可以將 gensio 層堆疊在 gensio 層之上。例如,要在 TCP 連線之上分層 telnet:
telnet,tcp,localhost,3001
假設您想在 telnet 連線上啟用 RFC2217。您可以新增一個選項來執行此操作:
telnet(rfc2217=true),tcp,localhost,3001
建立 gensio 時,您可以提供帶有使用者資料的回呼。當 gensio 上發生事件時,將呼叫回調,以便使用者可以處理它。
gensio 接受器與連接 gensio 類似,但使用str_to_gensio_accepter()
代替。格式是一樣的。例如:
telnet(rfc2217=true),tcp,3001
將建立一個頂部帶有 telnet 的 TCP 接受器。對於接受者,如果要綁定到本機電腦上的所有接口,通常不需要指定主機名稱。
創建 gensio 後,它尚未開放或運行。要使用它,您必須打開它。要打開它,請執行以下操作:
struct gensio * io ;
int rv ;
rv = str_to_gensio ( "tcp,localhost,3001" , oshnd ,
tcpcb , mydata , & io );
if ( rv ) { handle error }
rv = gensio_open ( io , tcp_open_done , mydata );
if ( rv ) { handle error }
請注意,當gensio_open()
返回時,gensio 並未開啟。您必須等到回呼(在本例中為tcp_open_done()
)被呼叫。之後,您就可以使用它了。
一旦 gensio 打開,您將不會立即獲得任何數據,因為接收已關閉。您必須呼叫gensio_set_read_callback_enable()
來開啟和關閉在接收資料時是否呼叫回呼(本例中為tcpcb
)。
當呼叫讀取處理程序時,將傳入緩衝區和長度。您必須使用實際處理的位元組數來更新 buflen。如果不處理數據,未處理的數據將緩存在gensio中以備後用。並不是說如果您不處理所有數據,您應該關閉讀取使能,否則該事件將立即再次呼叫。
如果連線出現問題,則會呼叫讀取處理程序並設定錯誤。在這種情況下, buf
和buflen
將為 NULL。
對於寫入,可以呼叫gensio_write()
寫入資料。您可以隨時在開啟的 gensio 上使用gensio_write()
。 gensio_write()
可能不會取得您寫入的所有資料。 count
參數傳回 write 呼叫中實際佔用的位元組數。
您可以將程式碼設計為在有資料要傳送時呼叫gensio_set_write_callback_enable()
,並且 gensio 將呼叫寫入就緒回調,您可以從回調寫入。這通常更簡單,但啟用和停用寫入回調會增加一些開銷。
更有效的方法是在需要時寫入資料並停用寫入回呼。如果寫入操作傳回的資料少於完整請求,則另一端已進行流量控制,您應該啟用寫回調並等到它被呼叫後再發送更多資料。
在回呼中,您可以使用gensio_get_user_data()
取得傳遞給 create 呼叫的使用者資料。
請注意,如果您開啟然後立即關閉 gensio,即使尚未呼叫 open 回呼,也沒有問題。不過,在這種情況下,可能會或不會呼叫 open 回調,因此很難正確處理此問題。
您可以使用 gensios 進行基本的同步 I/O。這在某些需要內聯讀取內容的情況下很有用。為此,請致電:
err = gensio_set_sync ( io );
給定的 gensio 將停止傳遞讀取和寫入事件。其他事件已交付。然後你可以這樣做:
err = gensio_read_s ( io , & count , data , datalen , & timeout );
err = gensio_write_s ( io , & count , data , datalen , & timeout );
計數設定為實際讀取/寫入的位元組數。如果你不關心它可能是NULL(儘管這對閱讀來說沒有多大意義)。
超時可能為NULL,如果是則永遠等待。如果您設定逾時,則會更新為剩餘時間。
請注意,信號將導致它們立即返回,但不會報告錯誤。
讀取將阻塞,直到一些資料進入並返回該資料。它不會等到緩衝區已滿。 timeout 是一個時間值,讀取將等待該時間量以完成讀取並返回。超時不是錯誤,計數將被設定為零。
寫入區塊直到整個緩衝區被寫入或發生逾時。再次強調,超時不是錯誤,實際寫入的總位元組數以 count 形式傳回。
使用 gensio 完成同步 I/O 後,呼叫:
err = gensio_clear_sync ( io );
透過事件介面的傳遞將像以前一樣繼續。呼叫此方法時,您不能處於同步讀取或寫入呼叫中,否則結果將是未定義的。
請注意,等待同步 I/O 時,其他 gensios 上的其他 I/O 仍然會發生
目前還沒有辦法透過同步 I/O 等待多個 gensios。如果您這樣做,您實際上應該只使用事件驅動的 I/O。它更有效率,而且無論如何,你最終都會做同樣的事情。
與 gensio 一樣,gensio 接受器在創建時無法運作。您必須呼叫gensio_acc_startup()
來啟用它:
struct gensio_accepter * acc ;
int rv ;
rv = str_to_gensio_accepter ( "tcp,3001" , oshnd ,
tcpacccb , mydata , & acc );
if ( rv ) { handle error }
rv = gensio_startup ( acc );
if ( rv ) { handle error }
請注意,啟動呼叫沒有回調來知道它何時啟用,因為沒有真正需要知道,因為您無法寫入它,它只執行回調。
即使在啟動接受器之後,它仍然不會執行任何操作,直到您呼叫gensio_acc_set_accept_callback_enable()
來啟用該回呼。
當呼叫回調時,它會在data
參數中為您提供一個 gensio,該資料參數已開啟且停用讀取。從 gensio 受體收到的 gensio 可能會有一些限制。例如,您可能無法關閉然後重新開啟它。
gensio 接受器可以使用gensio_acc_set_sync()
和gensio_acc_accept_s
進行同步接受。有關詳細信息,請參閱這些內容的手冊頁。
struct gensio_os_funcs
有一個用來處理內部 gensio 日誌的 vlog 回呼。當發生重要的事情但 gensio 無法報告錯誤時,就會呼叫這些函數。當出現問題時,也可以呼叫它來更輕鬆地診斷問題。
gensio 和 gensio 接受器類別各自具有用於處理串行 I/O 和設定與序列埠關聯的所有參數的子類別。
您可以透過呼叫gensio_to_sergensio()
來發現 gensio (或其任何子層級)是否為序列埠。如果傳回 NULL,則它不是 sergensio,而且它的子層級都不是 sergensios。如果傳回非 NULL,則傳回 sergensio 物件供您使用。請注意, sergensio_to_gensio()
返回的 gensio 將是傳遞給gensio_to_sergensio()
gensio,而不一定是與 sergensio 直接關聯的 gensio。
sergensio 可以是客戶端,這意味著它可以設定串行設置,也可以是伺服器,這意味著它將從連接的另一端接收串行設定。
大多數 sergensios 只是客戶端:serialdev(普通序列埠)、ipmisol 和 stdio 接受器。目前只有 telnet 具有客戶端和伺服器功能。
注意:此處所述的 python 介面已被棄用。現在使用 c++/swig/pygensio 中的那個。
您可以透過 python 存取幾乎所有 gensio 接口,儘管它的做法與 C 接口略有不同。
由於 python 是完全物件導向的,因此 gensios 和 gensio 接受器以及 gensio_os_funcs、sergensios 和 waiters 都是一流的物件。
這是一個小程式:
import gensio
class Logger :
def gensio_log ( self , level , log ):
print ( "***%s log: %s" % ( level , log ))
class GHandler :
def __init__ ( self , o , to_write ):
self . to_write = to_write
self . waiter = gensio . waiter ( o )
self . readlen = len ( to_write )
def read_callback ( self , io , err , buf , auxdata ):
if err :
print ( "Got error: " + err )
return 0
print ( "Got data: " + buf );
self . readlen -= len ( buf )
if self . readlen == 0 :
io . read_cb_enable ( False )
self . waiter . wake ()
return len ( buf )
def write_callback ( self , io ):
print ( "Write ready!" )
if self . to_write :
written = io . write ( self . to_write , None )
if ( written >= len ( self . to_write )):
self . to_write = None
io . write_cb_enable ( False )
else :
self . to_write = self . to_write [ written :]
else :
io . write_cb_enable ( False )
def open_done ( self , io , err ):
if err :
print ( "Open error: " + err );
self . waiter . wake ()
else :
print ( "Opened!" )
io . read_cb_enable ( True )
io . write_cb_enable ( True )
def wait ( self ):
self . waiter . wait_timeout ( 1 , 2000 )
o = gensio . alloc_gensio_selector ( Logger ())
h = GHandler ( o , "This is a test" )
g = gensio . gensio ( o , "telnet,tcp,localhost,2002" , h )
g . open ( h )
h . wait ()
此介面是 C 介面的直接翻譯。此介面的 Python 表示位於 swig/python/gensiodoc.py 中,您可以檢視文件。
C++ 介面記錄在 c++/README.rst 中。
新的 pygensio 介面是一個更乾淨的實現,使用 swig 控制器而不是手動編碼到 python 中的回調。請參閱 c++/swig/pygensio 中的 README.rst。 glib和tcl目錄下還有glib和tcl OS_Funcs。
完整的 C++ 介面可透過 swig 和 swig 控制器供 Go 程式使用。有關詳細信息,請參閱 c++/swig/go/README.rst。
這是一個普通的autoconf系統,沒什麼特別的。請注意,如果您直接從 git 取得此內容,則不會包含建置基礎架構。主目錄中有一個名為「reconf」的腳本,它將為您建立它。
如果您不了解 autoconf,INSTALL 檔案有一些訊息,或 google 一下。
要完整建立 gensio,您需要以下內容:
以下內容在 ubuntu 20.04 上設定 openipmi 以外的所有內容:
- sudo apt install gcc g++ git swig python3-dev libssl-dev pkg-config
- libavahi-client-dev avahi-daemon libtool autoconf automake make libsctp-dev libpam-dev libwrap0-dev libglib2.0-dev tcl-dev libasound2-dev libudev-dev
在 Redhat 上,libwrap 已經消失了,所以你不會使用它,而且 swig 似乎也不可用,所以你必須自己建置它,至少要有 go 和 python 支援。以下是類似 Redhat 系統的指令:
- sudo yum install gcc gcc-c++ git python3-devel swig openssl-devel
- pkg-config avahi-devel libtool autoconf automake make lksctp-tools-devel pam-devel glib2-devel tcl-devel alsa-lib-devel systemd-devel
您可能必須執行以下操作才能啟用對開發包的存取:
sudo dnf config-manager --set-enabled devel
並取得 SCTP 內核模組,您可能需要執行以下操作:
sudo yum 安裝核心模組額外
要使用Go語言,您必須獲得swig 4.1.0或更高版本。您可能需要從 git 中提取一個前沿版本並使用它。
處理 python 安裝配置有點痛苦。預設情況下,建置腳本會將其放置在 python 程式期望安裝的 python 程式所在的位置。普通使用者通常沒有該目錄的寫入權限。
若要覆寫此設置,您可以使用 --with-pythoninstall 和 --with-pythoninstalllib 配置選項,也可以將 pythoninstalldir 和 pythoninstalllibdir 環境變數設定為您想要程式庫和模組所在的位置。
請注意,您可能需要將--with-uucp-locking 設定為您的lockdir(在較舊的系統上,它是/var/lock,這是預設值。在較新的系統上,它可能是/ run/lock/lockdev。
go 語言支援需要安裝 go 並且位於路徑中。
當我繼續在庫中添加 gensios(例如 crypto、mdns、sound、IPMI、sctp 等)時,庫中的依賴項數量逐漸失去控制。如果不需要,為什麼要載入 libasound 或 libOpenIPMI?另外,雖然該程式庫支援透過程式設計 API 添加您自己的 gensios,但它沒有為系統添加它們的標準方法,因此您可以編寫自己的 gensio 並讓系統上的每個人都使用它。
gensio 庫支援動態載入 gensios 或將它們建置到庫中。預設情況下,如果您建立共用程式庫,則所有 gensios 都會編譯為動態載入的模組,並安裝在可實現動態載入的位置。如果您不建立共享庫,則所有 gensios 都會內建在該庫中。但您可以覆寫此行為。
要將所有 gensios 設定為內建到庫中,您可以在配置命令列上新增“--with-all-gensios=yes”,它將把它們內建到庫中。
您也可以透過新增「--with-all-gensios=dynamic」將它們設定為全部動態加載,但這是預設值。
您也可以透過指定“--with-all-gensios=no”預設為停用所有 gensios。那麼預設不會建置任何gensios。如果您只想要幾個 gensios,這非常有用,您可以將它們全部關閉,然後啟用您想要的。
要設定單一gensios 的建置方式,您可以執行“--with-<gensio>=x”,其中x 是“no(不建置)、yes(建置到庫中)或動態(動態載入的可執行檔) 。
這些模組預設放入
請注意,動態載入始終可用,即使您建立了庫中的所有 gensios。所以你仍然可以透過添加到正確的目錄來添加你自己的 gensios。
Gensios 將首先從環境變數 LD_LIBRARY_PATH 加載,然後從 GENSIO_LIBRARY_PATH 加載,最後從預設位置加載。
MacOS 是一種* nix,可以使用 Homebrew (https://brew.sh) 非常乾淨地建造。當然,您必須安裝所需的所有程式庫。大多數情況下一切正常,但以下情況除外:
* 厘米108gpio * SCTP * uucp鎖定
內建 DNSSD 代碼用於 MDNS,因此不需要 avahi。
串行埠的叢集鎖定有效,因此實際上不需要 uucp 鎖定。
openipmi 應該可以工作,但它在自製程序中不可用,因此您必須自己建立它。
安裝必要的軟體:
- pkg 安裝 gcc portaudio autoconf automake libtool mDNSResponder swig
- 去 python3 gmake
您必須使用 gmake 來編譯它,由於某種原因,BSD 上的標準 make 不接受需求清單中的「c++」變數。以下內容不起作用且未編譯:
* SCTP * 異丙醇 * 厘米108gpio
將以下內容加入 /etc/rc.conf 中:
mdnsd_enable=是
並重新啟動或啟動服務。
pty gensio 未能通過 oomtest(oomtest 14),BSD PTY 似乎出了問題。我發現在某些情況下,資料流中插入了 07 字元。不過,我沒有花太多時間,但由於這在 Linux 和 MacOS 上經過了大量測試,我認為問題不在於 gensio 程式碼。
gensio庫可以在Windows下使用mingw64建置。以下事情不起作用:
* SCTP * 帕姆 * 庫包裝 * 異丙醇
你也不需要安裝 alsa,它使用 Windows 聲音介面來發出聲音。
cm108gpio 使用本機 Windows 接口,因此不需要 udev。
使用Windows內建的MDNS接口,因此不需要avahi或DNSSD。如果您想要其中包含正規表示式,則需要安裝 PCRE 庫。
您需要從 https://msys2.org 取得 msys2。然後安裝 autoconf、automake、libtool、git、make 和 swig 作為主機工具:
pacman -S autoconf automake libtool git make swig
您必須安裝所有函式庫的 mingw-w64-x86_64-xxx 版本或所有函式庫的 mingw-w64-i686-xxx 版本。 32 位尚未經過充分測試:
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-python3 mingw-w64-x86_64-PCRE mingw-w64-x86_64-openssl
對於 mingw64 或 ucrt64:
pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-python3 mingw-w64-ucrt-x86_64-PCRE mingw-w64-ucrt-x86_64-openssl
對於 go,從 https://go.dev 安裝 go 並登出並重新登入。我還沒有開始在 mingw32 上工作,但我還沒有嘗試過 32 位元版本的 go。
對於 gtlsshd,--sysconfdir 在 Windows 上沒有任何意義。相反,sysconf 目錄與可執行檔的補丁相關,位於 ../etc/gtlssh 中。因此,如果 gtlsshd 位於:
C:/Program Files/Gensio/bin/gtlsshd
sysconfdir 將是:
C:/Program Files/Gensio/etc/gtlssh
對於標準安裝,您可以運行:
../configure --sbindir=/Gensio/bin --libexecdir=/Gensio/bin --mandir=/Gensio/man --includedir=/Gensio/include --with-pythoninstall=/Gensio/python3 --prefix=/Gensio
當您執行“make install DESTDIR=...”並將 DESTDIR 設定為您想要的位置時,例如“C:/Program Files”。然後您可以使用控制面板將其新增至路徑。若要使用 gtlsshd,請在 Gensio 目錄中建立一個 etc/gtlsshd 目錄。您必須設定此目錄的權限,以便只有系統和管理員才能訪問,例如:
PS C:Program Files (x86)Gensioetc> icacls gtlssh gtlssh NT 權限系統:(OI)(CI)(F) 內建管理員:(OI)(CI)(F)
否則 gtlsshd 將失敗並出現有關金鑰權限的錯誤。您可以在 .key 檔案而不是目錄上設定這些權限,但每次產生新金鑰時都必須重新設定。
若要使用 Inno 安裝編譯器,請執行“make install DESTDIR=$HOME/install”,然後在 gensio.iss 上執行 Inno。它將建立一個用於安裝 Gensio 的可執行安裝程式。
然後您需要從安裝目錄中刪除 .la 文件,因為它們會破壞與其他內容的連結:
rm $HOME/install/Gensio/lib/*.la
gensios 有很多測試。如果您有serialsim 核心模組,它們都可以在Linux 上運行。除了串行埠之外,它們還可以在其他平台上運行,因為該平台支援 gensios。
串列埠測試需要serialsim核心模組和python介面。這些位於 https://github.com/cminyard/serialsim 並允許測試使用模擬序列埠來讀取調變解調器控制線、注入錯誤等。
如果您有三個串行設備,則無需使用Serialsim 即可:一個以回顯模式掛接(RX 和TX 連接在一起),並且兩個掛接在一起的串行設備在一個設備上進行I/O傳輸到另一個裝置或來自另一個裝置。這應該適用於非 Linux 平台。然後設定以下環境變數:
export GENSIO_TEST_PIPE_DEVS= " /dev/ttyxxx:/dev/ttywww "
export GENSIO_TEST_ECHO_DEV= " /dev/ttyzzz "
它將無法測試調製解調器狀態或 rs485。
他們還需要來自 https://github.com/cminyard/openipmi 的 OpenIPMI 庫中的 ipmi_sim 程式來執行 ipmisol 測試。
要執行測試,您需要啟用一些內部偵錯才能獲得完整效果。你通常想要運行類似的東西:
./configure --enable-internal-trace CFLAGS= ' -g -Wall '
如果您願意,也可以在 CFLAGS 中開啟 -O3,但這會使偵錯變得更加困難。
有兩種基本類型的測試。 python 測試是測試 python 介面和 gensio 函式庫的功能測試。目前他們還可以,但還有很大的進步空間。如果您想提供協助,可以編寫測試。
oomtest 曾經是一個記憶體不足測試器,但現在已經演變成更廣泛的東西。它會產生一個具有特定環境變數的 gensiot 程序,導致其在某些點失敗,並進行記憶體洩漏和其他記憶體檢查。它透過 stdin 將資料寫入 gensiot,並在 stdout 上接收資料。一些測試(如serialdev)使用echo。其他測試透過網路建立單獨的連接,資料既流入標準輸入並透過單獨的連接返回,又流入單獨的連接並透過標準輸出返回。 oomtest是多執行緒的,執行緒數量可以控制。 oomtest發現了許多bug。它有很多旋鈕,但你必須查看選項的源代碼。如果有人願意自願的話,需要記錄下來......
若要設定模糊測試,請安裝 afl,然後進行以下配置:
mkdir Zfuzz ; cd Zfuzz
../configure --enable-internal-trace=yes --disable-shared --with-go=no
CC=afl-gcc CXX=afl-g++
或使用 clang(如果可用):
../configure --enable-internal-trace=yes --disable-shared --with-go=no
CC=afl-clang-fast CXX=afl-clang-fast++ LIBS= ' -lstdc++ '
我不確定為什麼上面的 LIBS 是必要的,但我必須添加它才能編譯它。
然後構建。然後“cd測試”並執行“make test_fuzz_xxx”,其中xxx是以下之一:certauth、mux、ssl、telnet或relpkt。 afl 會告訴您,您可能需要調整一些事情。請注意,它將永遠運行,完成後您需要 ^C 它。
test/Makefile.am 中的 makefile 包含如何處理偵錯重現失敗的說明。
在庫上運行程式碼覆蓋率非常容易。首先需要配置程式碼以啟用覆蓋:
mkdir Ocov ; cd Ocov
../configure --enable-internal-trace=yes
CC= ' gcc -fprofile-arcs -ftest-coverage '
CXX= ' g++ -fprofile-arcs -ftest-coverage '
編譯並執行“make check”。
若要產生報告,請執行:
gcovr -f ' .*/.libs/.* ' -e ' .*python.* '
這將產生一個摘要。如果你想查看文件中各行的覆蓋範圍,你可以這樣做:
cd lib
gcov -o .libs/ * .o
您可以查看創建的各個 .gcov 文件,以獲取有關所涵蓋內容的資訊。有關詳細信息,請參閱 gcov 文件。
在撰寫本文時,我的程式碼覆蓋率約為 74%,所以這確實非常好。我將努力改進這一點,主要是透過改進功能測試。
ser2net 用於測試一些東西,主要是串行埠配置(termios 和 rfc2217)。您可以針對 gensio 庫的 gcov 版本建立 ser2net,並在 ser2net 中執行「make check」以覆蓋這些部分。這樣一來,我看到的覆蓋率約為 76%,所以它並沒有為總數增加太多。
如果能夠將其與模糊測試結合就好了,但我不知道如何做到這一點。 afl 在程式碼覆蓋方面做了它自己的事情。似乎有一個 afl-cov 軟體包以某種方式整合了 gcov,但我還沒有研究過它。