这就是 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 等。过滤器 gensios 会使用它。这些都使用 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 上的 SIGWINCH)。这些信息如何通过 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-设定的DEVEL
并获取SCTP内核模块,您可能必须要做:
sudo yum安装内核模块-Extra
要使用GO语言,您必须获得SWIG 4.1.0或更高版本的版本。您可能必须从git中拉出出血的边缘版本并使用它。
处理Python安装配置有点痛苦。默认情况下,构建脚本将在Python程序期望安装Python程序的任何地方都将其放置。普通用户通常没有对该目录的写入访问权限。
为了覆盖这一点,您可以使用 - 抛氧纳斯特尔(With-PythonInstall)和 - with-pythoninstalllib配置选项,也可以将pythoninstalldir和Pythoninstalllibdir环境变量设置为希望库和模块进入的位置。
请注意,您可能需要设置 - lockdir(在较旧的系统上它的/var/lock),这是默认值。在更新的情况下,它可能是/run/lock/lockdev。拨号和锁组的成员能够打开串行设备和/或锁。
GO语言支持需要安装并在路径中。
当我继续将Gensios添加到库中时,例如加密,MDN,声音,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。
要设置单个Gensio的构建方式,您要执行“ - with- <gensio> = x”其中x是“ no(no build),是(构建到库)或动态(动态加载可执行文件)。例如如果您只想将TCP Gensio构建到库中并进行静止动态,则可以为所有动态Gensios设置,然后添加“ - WITH-NET = YES”。
默认将这些模块放入
请注意,即使您在库中的所有Gensios中构建,动态加载也始终可用。因此,您仍然可以通过添加然后在适当的目录中添加自己的Gensios。
Gensios将首先从环境变量ld_library_path,然后从Gensio_library_path加载,然后从默认位置加载。
Macos是一种* Nix,与自制的人(https://brew.sh)相当干净。当然,您必须安装所需的所有库。大多数一切都起作用,除以下例外:
* CM108GPIO * SCTP * UUCP锁定
内置DNSSD代码用于MDN,因此不需要Avahi。
串行端口的羊群锁定起作用,因此确实不需要UUCP锁定。
OpenIPMI应该可以使用,但是在自制的情况下不可用,因此您必须自己构建它。
安装必要的软件:
- PKG安装GCC Portaudio Autoconf AutoConf automake libtool mdnsresponder swig
- 去python3 gmake
您必须使用gmake对其进行编译,由于某种原因,标准在BSD上的标准不接受要求列表中的“ C ++”变量。以下不起作用,也不编译:
* SCTP * ipmisol * CM108GPIO
将以下内容添加到/etc/rc.conf:
mdnsd_enable =是
并重新启动或开始服务。
Pty Gensio失败了OOMTEST(OOMTEST 14),BSD PTY似乎有一些东西。在情况下,我看到一个07字符插入数据流中。不过,我并没有花太多时间在上面,但是由于在Linux和MacOS上进行了大量测试,因此我认为问题在Gensio代码中并不是。
Gensio库可以使用MingW64在Windows下构建。以下内容不起作用:
* SCTP *帕姆 * libwrap * ipmisol
您也不需要安装ALSA,它使用Windows声音界面进行声音。
CM108GPIO使用本机Windows界面,因此不需要UDEV。
使用Windows内置的MDN接口,因此您不需要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开始安装并注销并登录。然后应该在路径中,但是如果不是路径,则需要将其添加到路径中。我还没有从事MingW32的工作,但是我还没有尝试过32位版本的GO。
对于GTLSSHD, - sysconfdir在Windows上没有任何意义。取而代之的是,Sysconf dir是相对于可执行文件的补丁,在../etc/gtlssh中。因此,如果GTLSSHD在:
c:/program文件/gensio/bin/gtlsshd
Sysconfdir将是:
c:/program文件/gensio/etc/gtlssh
对于标准安装,您可以运行:
../configure -sbindir =/gensio/bin -libexecdir =/gensio/bin -mandir =/gensio/man-includedir =/gensio/include -with-with-pythoninstall =/gensio/python3 -prefix =/gensio
当您运行“ Make Install DestDir = ...”时,将Destdir设置为所需的位置,例如“ C:/Program Files”。然后,您可以使用控制面板将其添加到路径中。要使用GTLSSHD,您可以在Gensio目录中创建一个ETC/GTLSSHD目录。您必须在此目录上设置权限,以便只有系统和管理员可以访问,例如:
PS C: Program Files(x86) Gensio etc> iCacls gtlssh gtlssh nt Authority System:(oi)(CI)(f) 内置管理员:(oi)(ci)(f)
否则,GTLSSHD会在密钥上的权限上失败。您可以在.key文件而不是目录上设置这些权限,但是每次生成新密钥时,都必须再次将其设置。
要使用Inno设置编译器,请“让安装DestDir = $ home/install”,然后在Gensio.iss上运行Inno。它将创建一个可执行的安装程序,用于安装Gensio。
然后,您需要从安装目录中删除.la文件,因为它们将与其他内容链接在一起:
rm $ home/install/gensio/lib/*
有许多针对Gensios的测试。如果您具有串行SIM内核模块,它们都在Linux上运行。除串行端口外,它们还在其他平台上运行,因为该平台上支持了Gensios。
串行端口测试需要串行SIM内核模块和Python接口。这些位于https://github.com/cminyard/serialsim,允许测试使用模拟串行端口来读取调制解调器控制线,注入错误等。
如果您有三个串行设备,则可以在没有串行的情况下获得:一个以回声模式挂钩(RX和TX绑在一起),而在一个设备上挂接在一起的两个串行设备do i/o to/to to/to in to in to/to。这应该在非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)使用回声。其他测试在网络上建立了单独的连接,数据将既流入stdin又流入单独的连接,然后流入单独的连接并通过Stdout返回。 oomtest是多线程,可以控制线程的数量。 Oomtest发现了很多错误。它有很多旋钮,但是您必须查看选项的源代码。如果有人想志愿服务,就需要记录下来...
要设置用于模糊,请安装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测试”并运行“制作test_fuzz_xxx”,其中XXX是:CERTAUTH,MUX,SSL,TELNET或RELPKT之一。您可能需要调整一些事情,AFL会告诉您。请注意,它将永远运行,完成后需要 ^c。
测试/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)。您可以针对GCOV版本的Gensio库构建SER2NET,并在Ser2net中运行“ Make Check”以获取这些部分的覆盖范围。这样,我看到约76%的覆盖范围,因此并不增加总数。
能够将其与模糊结合在一起会很高兴,但是我不确定该怎么做。 AFL在代码覆盖范围内做自己的事情。似乎有一个以某种方式集成了GCOV的AFL-COV软件包,但我还没有研究过。