阅读前言
本文以QNX系统官方的文档英文原版资料为参考,翻译和逐句校对后,对QNX操作系统的相关概念进行了深度整理,旨在帮助想要了解QNX的读者及开发者可以快速阅读,而不必查看晦涩难懂的英文原文,这些文章将会作为一个或多个系列进行发布,从遵从原文的翻译,到针对某些重要概念的穿插引入,以及再到各个重要专题的梳理,大致分为这三个层次部分,分不同的文章进行发布,依据这样的原则进行组织,读者可以更好的查找和理解。
1. 原生网络(Qnet)
在本手册前面的进程间通信(IPC)一章中,我们描述了在单个节点上下文中的消息传递。但是,QNX Neutrino RTOS的真正强大之处在于,它能够采用消息传递范式,并在微内核网络上透明地扩展它。本章描述QNX Neutrino原生网络(通过Qnet协议)。
一个节点上最多只能运行一个Qnet实例,即使运行多个io-pkt实例也是如此。
1.1. QNX Neutrino分布式
QNX Neutrino原生网络的核心是 Qnet 协议,它被部署为紧密耦合的、可信的机器的网络。
Qnet 允许这些机器以很少的开销有效地共享资源。使用 Qnet,你可以使用标准的操作系统实用工具程序(cp、mv等)来操作 Qnet 网络上的任何地方的文件,就像这些文件在你的机器上一样。此外,Qnet 协议不负责对远程请求进行任何身份验证;这些文件受到适用于用户和用户组的普通权限的保护。除了文件,你还可以访问和启动/停止位于Qnet网络上的任何机器上的进程,包括管理器进程。
Qnet 的分布式处理能力可以让你高效地完成以下任务:
- 访问远程文件系统。
- 按照前所未有的轻松程度,对应用程序进行扩展。
- 使用一组协作进程编写应用程序,这些进程使用 QNX Neutrino 消息传递透明地相互通信。
- 将应用程序轻松地从单处理器或SMP机器扩展到多台单处理器机器,并在这些CPU之间分配进程。
- 将大型应用程序划分为几个进程,每个进程可以执行不同的功能。这些进程使用消息传递来协调工作。
- 利用 Qnet 固有的远程程序调用功能。
此外,由于 Qnet 在网络上扩展了 QNX Neutrino 消息传递机制,因此,其他形式的 IPC(例如,信号,消息队列)也可以在网络上工作。
为了理解网络范围内的 IPC 是如何工作的,可以假设存在两个希望相互通信的进程:一个客户端进程以及一个服务器进程(在本例中是串行端口管理器进程)。在单节点情况下,客户端只需调用open()
、read()
、write()
等。我们很快就会看到,像open()
这样的高级POSIX调用,实际上需要以(ConnectAttach()
,MsgSend()
等)消息传递相关的内核调用为"基础"。但是客户端不需要关心这些内核调用函数;它只需要知道调用open()
等函数即可。
cpp
fd = open("/dev/ser1", O_RDWR,...); /* Open a serial device */
现在我们来考虑一下两台机器的简单网络的情况:一台机器包含客户端进程,另一台包含服务器进程。
客户机和服务器位于不同机器上的简单网络的情况
客户端-服务器通信所需的代码与单节点情况下的代码相同,但有一个重要的例外:路径名。路径名将包含一个前缀,该前缀指定服务(/dev/ser1
)所在的节点。正如我们稍后将看到的,这个前缀将被转换为一个节点描述符,被用于即将发生的底层ConnectAttach()
内核调用中。网络中的每个节点都被分配了一个节点描述符,这是确定操作系统是作为网络中的机器在运行还是作为独立的机器在运行的唯一可见手段。
有关节点描述符的更多信息,请参阅 QNX Neutrino 程序员指南中的 Transparent Distributed Processing with Qnet(Qnet 透明分布式处理)章节的内容。
1.2. 名称解析和查找
当你运行 Qnet 时,Qnet 网络中所有节点的路径名空间都会添加到你的路径名空间中。回想一下,路径名是一个符号名称,它告诉程序基于根(/)目录层次结构在哪里找到一个文件。
远程节点的路径名空间将出现在前缀/net
(默认情况下由 Qnet 协议管理器lsm-qnet.so
所创建的目录)。
例如,远程node1
将显示为:
bash
/net/node1/dev/socket
/net/node1/dev/ser1
/net/node1/home
/net/node1/bin
...
因此,随着 Qnet 的运行,你现在可以在其他远程 Qnet 节点上打开路径名(文件或管理器),就像在你自己的节点上本地打开文件一样。这意味着你可以访问其他 Qnet 节点上的常规文件或管理器进程,就好像是它们是在你的本地节点上进行执行一样。
回想一下上面的open()
示例。如果你想在node1
上而不是在本地机器上打开串行设备,只需指定路径:
cpp
fd = open("/net/node1/dev/ser1", O_RDWR, ...); /* Open a serial device on node1 */
对于客户端-服务器通信,客户端如何知道服务器使用了什么节点描述符?
客户机使用文件系统的路径名空间"查找"服务器的地址。在单机情况下,查找的结果将是节点描述符、进程ID和通道ID。在网络情况下,结果是相同的,唯一的区别是节点描述符的值。
|----------------------|---------------|
| 如果节点描述符是: | 则服务器是: |
| 0 (or ND_LOCAL_NODE) | 本地的(即,"当前节点") |
| 非零值 | 远程的 |
1.2.1. 文件描述符(连接ID)
在本地情况下以及在网络情况下,实际结果都是,当客户端连接到服务器时,客户端会获得一个文件描述符(或者在内核调用的情况下,如ConnectAttach()
内核调用,客户端会获得一个连接ID)。然后,将此文件描述符用于所有后续的消息传递操作。请注意,从客户端的角度来看,本地的情况下和网络的情况下的文件描述符是相同的。
1.2.2. 简单的*open()*函数的背后
让我们回到open()
示例。假设一个节点(lab1
)上的客户端希望使用另一个节点(lab2
)上的串行端口(/dev/ser1
)。客户端可以有效地对路径名/net/lab2/dev/ser1
执行open()
操作。
下图显示了当客户端open()
打开/net/lab2/dev/ser1
时所涉及的步骤:
客户端-服务器跨网络消息传递
以下是交互过程:
- 从客户端向其本地进程管理器发送一条消息,询问应该联系谁来解析路径名
/net/lab2/dev/ser1
。
由于原生网络管理器(lsm-qnet.so
)已经接管了整个/net
名称空间,因此进程管理器将会返回一条重定向消息,指示客户端应该联系原生网络管理器以获取更多信息。
- 然后,客户端向原生网络管理器发送一条消息,再次询问应该联系谁来解析路径名。
然后,原生网络管理器使用另一个重定向消息进行应答,给出节点lab2
上进程管理器的节点描述符、进程ID和通道ID,这有效地把请求解析推迟到节点lab2
上进行执行。
- 然后,客户端在节点
lab2
上创建到进程管理器的连接,再次询问应该联系谁来解析路径名。
节点lab2
上的进程管理器将会返回另一个重定向消息,这次带有节点描述符、通道ID和自己节点上串行驱动程序的进程ID。
- 客户端在节点
lab2
上创建了到串行驱动程序的连接,并最终获得一个连接ID,然后可以将该连接ID用于后续的消息传递操作中。
在此之后,从客户端的角度来看,到连接ID的消息传递与是在本地的情况相同。注意,所有进一步的消息操作现在都直接是在客户端和服务器之间进行的。
这里要记住的关键是,客户端并不知道正在发生的操作;这些都是由POSIX的open()
调用进行处理的。就客户端而言,它仅仅是执行了open()
并返回了文件描述符(或错误指示)。
在每个后续的名称解析步骤中,来自客户端的请求的路径名将会从已经解析的名称组件中被剥离;剥离过程在资源管理器框架内自动发生。这意味着在上面的步骤2中,从原生网络管理器的角度来看,请求的相关路径名组件是
lab2/dev/ser1
。而在步骤3中,请求路径名会被剥离为dev/ser1
,因为这是lab2
的进程管理器需要知道的全部内容。最后,在步骤4中,请求的路径名的相关组件仅为ser1
,因为这是串行驱动程序需要知道的全部内容。
1.2.3. 全局命名服务(Global Name Service,GNS)
在目前显示的示例中,远程服务或文件位于已知节点或已知路径名上。例如,lab1
的串口位于/net/lab1/dev/ser1
。
GNS 允许你通过任意名称定位服务,无论服务位于何处,无论是本地系统还是远程节点。例如,如果想在网络中找到一个调制解调器,只需查找名称"modem"即可。GNS服务器就会去定位"调制解调器"服务,而不是使用静态路径,如 /net/lab1/dev/ser1
。可以部署 GNS 服务器,使其为所有或部分 Qnet 节点提供服务。你可以有冗余的 GNS 服务器。
1.2.4. 网络命名
如前面所描述,路径名前缀 /net
是 lsm-qnet.so
最常用的名称。
在整个网络的路径名空间中进行名称解析时,会涉及以下术语:
1. 节点名(Node Name) :一个字符字符串,用于标识你正在与之通信的节点。请注意,节点名不能包含斜杠(/)或点(.)。在之前的例子中,我们使用了lab2
作为一个节点名。默认情况下,节点名是通过confstr()
函数与_CS_HOSTNAME
参数获取的。
@>> 理解 :
节点名就像是网络中每个节点的"名字",用于在网络中唯一标识该节点。它必须遵循一定的命名规则,比如不能包含斜杠或点。
2. 节点域(Node Domain) :一个字符字符串,由lsm-qnet.so
(一种网络库或模块)附加到节点名上。节点名和节点域组合在一起,必须为所有相互通信的节点形成一个唯一的字符串。默认情况下,节点域是通过confstr()
函数与_CS_DOMAIN
参数获取的。
@>> 理解:
为了让节点的标识更具唯一性,会有这么个"节点域"的字符串会和节点名称结合起来,通过专门的软件模块(
lsm-qnet.so
)来操作附加。这就好比给每个人的名字后面加上来自哪个地区一样,确保在整个相互通信的网络节点群体里,这个组合起来的标识是独一无二的,同样它也能通过特定函数获取默认值。
3. 完全限定节点名(Fully Qualified Node Name, FQNN) :通过将节点名和节点域组合在一起形成的字符串。例如,如果节点名是lab2
,节点域名是qnx.com
,那么结果FQNN就是lab2.qnx.com
。
@>> 理解:
FQNN就像是节点在网络中的"全名",它包含了节点名和节点域,用于在网络中唯一地标识和定位一个节点。这是把前面说的节点名称和节点域按照规则拼接起来后的一个完整名称形式,很像我们平时上网看到的完整域名,例如网站域名那样,通过具体例子 "lab2.qnx.com" 就能清晰地看到是怎么组合的,它能更精准全面地定位网络中的一个节点。
4. 网络目录(Network Directory) :由lsm-qnet.so
实现的路径名空间中的一个目录。每个网络目录(一个节点上可以有多个)都有一个关联的节点域。默认的网络目录是/net
,如本章示例中所示。
@>> 理解:
可以理解为网络中一种有组织管理功能的 "文件夹",是由特定软件模块在整个网络的路径名空间里构建出来的,每个这样的 "文件夹" 都和一个节点域相关联,而且有默认的设置路径(如 "
/net
"),方便在网络中进行相关资源等的分类管理以及名称解析等操作时作为一种参照。
5. 名称解析(Name Resolution) :lsm-qnet.so
将 FQNN 转换为传输层知道如何到达的目的地地址列表的过程。
@>> 理解:
从实际网络通信角度来看,当知道了一个节点的完整名称(FQNN)后,要想真正和这个节点通信,得知道它具体对应的地址呀,这个过程就是把这个看起来比较好记、好识别的完整名称转化为传输层能明白、能找到的目标地址列表的过程,就好像把一个人的家庭住址对应的详细街道门牌号等信息找出来一样,方便数据能准确送达到对应的节点。
6. 名称解析器(Name Resolver) :实现将FQNN转换为目的地地址列表的一种方法的一段代码。每个网络目录都有一个名称解析器列表,这些解析器会依次尝试解析FQNN。默认的名称解析器是en_ionet
(见下一节)。
@>> 理解:
它相当于是实现名称解析这个任务的不同 "工具" 或者说 "方法代码",每个网络目录下都准备了好几个这样的 "工具",按顺序去尝试用它们把完整节点名称(FQNN)解析成地址列表,默认情况下会先使用
en_ionet
这个解析器来干活,不同的解析器可能有不同的解析策略和适用场景。
7. 服务质量(Quality of Service, QoS) :两个节点之间连接性的定义。默认的QoS是loadbalance
(见本章后文的QoS部分)。
@>> 理解:
在网络中,节点之间通信不能只看能不能通,还要看通信的质量怎么样呀,比如是不是稳定、速度快不快等,QoS 就是对这些通信质量相关情况的一种定义描述,默认是
loadbalance
(负载均衡模式),意味着会尽量合理分配网络资源,让通信更顺畅高效,后面章节会详细介绍它具体的相关内容。通过定义QoS,可以优化网络通信的效率和可靠性,满足不同的应用需求。
1.2.4.1. 解析器(Resolvers)
在网络管理器中内置了以下解析器:
1. en_ionet:在局域网(LAN)上广播名称解析请求(类似于TCP/IP的ARP地址解析协议)。这是默认设置。
@>> 理解:
在局域网这样相对较小范围的网络环境里,当需要知道某个节点对应的具体网络地址等信息来进行通信时,它会采用广播的方式向整个局域网内发送请求,询问谁是对应的节点,就好比在一个办公室里大声问谁是某个名字对应的那个人一样。和 TCP/IP 协议里的 ARP 协议类似,ARP 协议也是在网络中通过广播等方式来获取 IP 地址对应的 MAC 地址(物理地址),它们目的都是为了搞清楚网络中节点相关的具体地址信息,方便后续通信,并且它是系统默认会首先使用的解析方式。
2. dns :取节点名,后加一个点(.),再跟上节点域,然后将结果发送给TCP/IP的gethostbyname()
函数。
@>> 理解 :
dns
解析器利用DNS(域名系统)来解析节点名。它首先将节点名和节点域组合成一个完整的域名(例如node.domain.com
),然后使用TCP/IP的gethostbyname()
函数来查询该域名的IP地址。这是网络中最常见的名称解析方式之一,因为 DNS 系统在互联网上广泛部署,并且能够有效地将域名解析为 IP 地址。
3. file:在静态文件中搜索可访问的节点,包括相关的网络地址。
@>> 理解 :
file
解析器通过查找一个预先定义好的静态文件来解析节点名。这个文件中列出了所有可访问的节点及其对应的网络地址。当需要解析一个节点名时,file
解析器会在该文件中搜索匹配的条目,并返回相应的网络地址。这种方式适用于网络配置相对固定,且不希望依赖于动态名称解析服务(如DNS)的场景。比如小型企业内部网络里,有一份专门记录各个办公设备网络地址等情况的文件,就可以通过这个解析器去里面查找对应的节点信息。
1.3. 冗余的Qnet:服务质量(QoS,Quality of Service)和多路径
服务质量(Quality of Service,QoS)是一个在高可用性网络以及实时控制系统中经常出现的问题。在 Qnet(一种网络相关的环境或系统,此处应是特定指代)背景下,服务质量实际上归根结底就是传输介质的选择问题 ------ 在具备两个或更多网络接口的系统中,Qnet 会根据你指定的策略来选择使用哪一个网络接口。
如果你只有一个网络接口,那么服务质量策略就完全不适用了。
@>> 理解总结:
- 关于 QoS 出现的场景 :
在那些对网络要求比较高的环境里,比如高可用性网络,这类网络往往需要时刻保持稳定、可靠,不能轻易出现故障导致服务中断,像银行的网上交易系统、大型电商平台的服务器网络等,一旦网络出问题影响交易等业务就麻烦了。还有实时控制系统,像工业生产线上的自动化控制系统,必须实时准确地传输控制指令,要是网络延迟大或者不稳定,生产就可能出乱子。在这些场景下,服务质量(QoS)就显得尤为重要了,人们很关注网络能不能满足相应的性能要求。- 在 Qnet 中的含义 :
在 Qnet 这个特定的网络相关环境里,它对 QoS 的定义聚焦在了传输介质的选择上。可以想象一个设备有多个 "通道"(也就是网络接口)能连接网络,就好比一个人有好几条路可以去上班一样,那到底走哪条路(用哪个网络接口)更好呢,这时候 Qnet 就会按照事先设定好的策略来决定,这个策略可能会考虑网络的速度、稳定性、当前的负载情况等各种因素,从而选出最合适的网络接口来传输数据,以此保障网络服务质量。- 单接口情况 :
但如果设备只有一个网络接口,那就不存在选择的问题了呀,就像只有一条路可走,没有其他路能对比、挑选了,所以这时候那些关于如何选择不同网络接口来保障服务质量的策略自然就派不上用场了,也就是完全不适用了。
1.3.1. QoS的策略
Qnet 支持通过多个网络进行传输,并提供了若干策略来指定 Qnet 应如何选择网络接口进行传输。这些服务质量(Quality of Service,QoS)策略包括:
- 负载均衡( loadbalance, 默认策略):Qnet 可以使用所有可用的网络链路,并将在这些链路间平均分配传输任务。
- 首选( preferred ):Qnet 会使用某一指定链路,而忽略所有其他网络(除非首选链路出现故障)。
- 排他性( exclusive ):Qnet 会使用且仅使用一条链路,忽略所有其他链路,即便这条排他性链路出现故障也如此。
要充分受益于 Qnet 的服务质量功能,你需要有物理上相互独立的网络。例如,考虑这样一个网络,它有两个节点和一个集线器,每个节点都有两条连接到该集线器的线路:
Qnet和单一网络。
如果当前正在使用的链路出现故障,Qnet 能够检测到故障,但不会切换到另一条链路,因为两条链路都连接到同一个集线器。此时要由应用程序来从该错误中恢复;当应用程序重新建立连接时,Qnet 会切换到正常工作的链路。
现在,考虑同样的网络,但有两个集线器的情况:
Qnet和物理上分离的网络。
如果网络在物理上是相互独立的,并且某条链路出现故障,Qnet 会根据你所选择的服务质量策略自动切换到另一条链路。应用程序并不会察觉到第一条链路出现了故障。
你可以使用传递给lsm-qnet.so
的tx_retries
选项来限制 Qnet 重试传输的次数,进而控制 Qnet 在判定某条链路出现故障之前等待的时长。需要注意的是,如果重试次数设置得过低,Qnet 将无法容忍任何数据包丢失情况,并且可能过早地判定某条链路已失效。
@>> 理解总结:
- Qnet 的 QoS 策略 :
- 负载均衡策略:就好比有好几辆车(代表数据传输任务)要送货,有多条路(网络链路)可以走,采用这个策略的话,会把这些车平均分配到每条路上,让每条路都承担一部分运输工作,这样能充分利用所有可用的网络资源,保证整体传输的平稳和高效,而且这是系统默认就采用的方式。
- 首选策略:相当于提前指定了一条最喜欢、最优先使用的路,不管其他路状况如何,只要这条首选的路没问题,就只走这条路来传输数据,只有当它出故障走不了了,才可能考虑别的路。
- 排他性策略:这种策略更加 "固执" 了,一旦选定了某条链路,就只认准这一条,哪怕它出现故障不能用了,也还是只盯着它,不会主动去切换使用其他链路,除非人工干预或者有其他特殊设定改变这种情况。
- 物理网络结构对 Qnet 功能的影响 :
- 单集线器情况:当两个节点都通过两条线连接到同一个集线器时,这两条线路其实可以看作是同一网络路径的不同分支,它们关联性很强,所以即便其中一条线坏了,Qnet 觉得它们本质上还是 "一体" 的,不会自动切换到另一条线,得靠应用程序自己去发现问题然后重新建立连接,这时候 Qnet 才会跟着切换过去,有点像要人工提醒一下它才行。
- 双集线器情况:如果是连接到两个不同的集线器,那就意味着网络在物理层面是相对独立的,有了各自不同的 "通道"。这时候要是某条链路出故障了,Qnet 就能根据之前设定好的 QoS 策略,聪明地自动切换到另一条可用链路,而且应用程序还感觉不到链路出问题了,整个过程对应用程序来说是比较 "无感" 的,这样就保证了数据传输能持续稳定进行。
- 关于 tx_retries 选项 :
- 这个选项就像是给 Qnet 设定的一个"耐心"程度指标,它决定了 Qnet 在觉得某条链路不行了、判定它故障之前,愿意去尝试重新传输数据的次数。如果设得太低,就像一个人太没"耐心"了,稍微有点数据包没传成功就觉得这条路彻底坏了,过早地判定链路失效,可能实际上链路只是暂时出了点小状况,还能恢复正常呢,这样可能会影响正常的数据传输;但如果设得太高,又可能会在链路确实有问题的时候,浪费太多时间在不断重试上,影响整体传输效率,所以要根据实际网络情况合理设置这个参数。
让我们更详细地了解一下这些服务质量(QoS)策略:
-
负载均衡(loadbalance) :
Qnet 会根据由io-pkt*
确定的当前负载以及链路速度来决定使用哪些链路发送数据包。数据包会被排入能够最快将其传送到远端的链路所对应的队列中。当链路正常运行时,这实际上能在节点之间提供更大的带宽(带宽为所有可用链路带宽之和),并且当链路出现故障时,能够实现服务的平稳降级。如果某条链路确实发生故障,Qnet 将会切换到下一条可用链路。首次进行这种切换时需要花费几秒钟的时间,因为故障链路上的网络驱动程序会经历超时、重试,最终停止工作。但是一旦 Qnet "知晓" 某条链路已失效,它就不会再通过该链路发送用户数据了。在对处于正常状态的链路进行负载均衡操作时,Qnet 会在故障链路上发送定期的维护数据包,以便检测链路是否恢复。当链路恢复正常时,Qnet 会将其重新纳入可用链路池中。 -
首选(preferred) :
采用这种策略时,你需要指定一条用于传输的首选链路。Qnet 在该链路出现故障之前只会使用这一条链路。如果你的首选链路出现故障,Qnet 将会转而使用其他可用链路,并采用负载均衡策略恢复传输。一旦首选链路再次可用,Qnet 会再次只使用该链路,而忽略所有其他链路(除非首选链路再次出现故障)。 -
排他性(exclusive) :
当你希望将传输锁定在仅一条链路上时,可使用这种策略。无论还有多少其他可用链路,Qnet 都会紧紧依附于你指定的那个接口。而且如果这条排他性链路出现故障,Qnet 将不会使用任何其他链路。你为什么会想要使用排他性策略呢?假设你有两个网络,其中一个网络的速度比另一个快得多,并且你有一个需要传输大量数据的应用程序。你可能希望将传输限制在仅那个快速网络上,以避免在出现故障情况时使慢速网络陷入瘫痪。
注意:默认使用的是 loadbalance QoS 策略。
@>> 理解总结:负载均衡策略(loadbalance)
- 决策依据和工作方式 :
Qnet 就像一个智能的交通指挥员,它会综合考虑各个 "道路"(链路)当前的拥堵情况(负载)以及 "道路限速"(链路速度)这些信息(这些信息由io-pkt*
来帮忙确定),然后决定把数据包这个 "包裹" 安排到哪条链路去运输,目标就是让数据包能以最快速度到达目的地(远端)。例如,有链路 A 和链路 B,链路 A 现在负载小、速度快,那大部分数据包就可能被优先安排到链路 A 上去排队传输了。- 带宽优势和故障应对 :
当所有链路都正常工作时,由于它能灵活分配数据包到不同链路,相当于把所有链路的带宽都利用起来了,整体节点之间可使用的带宽就变成了各条链路带宽相加的总和,就像多条车道同时通车能让更多车辆通过一样,大大提升了传输能力。而一旦有链路出故障了,它也不会一下子就乱套,而是能有条不紊地切换到其他可用链路继续工作,服务质量虽然会有所下降,但能尽量平稳过渡,不至于完全中断传输。比如原本三条链路共同承担传输任务,一条坏了,剩下两条就接着分担工作。- 链路故障切换和恢复检测 :
首次发现链路故障并切换链路时,之所以需要花几秒钟,是因为背后涉及到网络驱动程序一系列复杂的处理流程,先是超时等待看链路能不能恢复,然后重试传输,最后实在不行才判定链路彻底坏了。在链路故障后,Qnet 还挺 "负责" 的,会时不时在这条坏链路上发一些维护数据包看看它有没有恢复正常,一旦恢复了,就又把它当作可用链路重新利用起来,继续参与负载均衡的工作分配了。首选策略(preferred)
- 使用规则 :
这个策略就是让你先挑出一条自己最信任、最想优先使用的链路,就好像你出门有好几条路可以选,但你就认准了某一条主路走一样,Qnet 在这条你指定的首选链路不出问题的情况下,就只走这条路来传输数据,其他链路就相当于被暂时 "冷落" 了。- 故障及恢复后的处理 :
不过要是这条首选链路出故障了,那没办法,Qnet 也得变通一下,开始启用其他可用链路,并按照负载均衡策略来合理分配数据包进行传输了。等这条首选链路又恢复正常了,Qnet 就又变回 "专一" 的状态,继续只走这条链路,把其他链路又丢在一边不管了,反正就是只要首选链路能用,就只认它这一家。排他性策略(exclusive)
- 锁定传输链路 :
采用这个策略就相当于给传输上了一把 "锁",只允许在你指定的那一条链路上传输数据,不管周围还有多少别的链路空闲着或者性能也不错,Qnet 都一概不理会,就死死守着你选的那条链路干活。- 故障后的情况和应用场景 :
哪怕这条指定的排他性链路出故障了,Qnet 也不会主动去换别的链路继续传输,哪怕因此传输中断了也不会改变做法,挺 "固执" 的。那什么时候会用到这种策略呢?比如你有两个网络,一个速度超快,一个比较慢,而你运行的应用程序要传输大量的数据,这时候为了防止大量数据一股脑涌到慢网络上,把慢网络压垮,尤其是在可能出现故障等不稳定的情况下,你就可以用这个排他性策略,让数据只在那个快速网络上传输,保障整个网络传输的相对稳定和高效。
1.3.2. 指定QoS策略
你可将服务质量(QoS)策略作为路径名的一部分来进行指定。
例如,要以排他性(exclusive)的服务质量策略访问 "/net/lab2/dev/ser1
",你可以使用以下路径名:
bash
/net/lab2~exclusive:en0/dev/ser1
服务质量参数总是以波浪号(~)字符开头。在这里,我们是在告知 Qnet 要排他性地锁定到 "en0
" 接口上,即便该接口出现故障也如此。
1.3.3. 符号链接
你可以为各种带有 "服务质量(QoS)限定" 的路径名设置符号链接:
bash
ln -sP /net/lab2~preferred:en1 /remote/sql_server
这样就为节点 "lab2"(也就是通过 "en1" 链路,采用首选的服务质量策略)分配了一个抽象名称 "/remote/sql_server
"。
注意:你不能在"/net
"内部创建符号链接,因为 Qnet 接管了该名称空间。
通过一级间接引用对路径名进行抽象处理,能让你在网络中拥有多个可用的服务器,且它们都能提供相同的服务。当其中一个服务器出现故障时,抽象路径名可以被 "重新映射",使其指向另一个不同服务器的路径名。例如,如果 "lab2" 服务器出现故障,那么监控程序可以检测到这一情况,并有效地执行以下操作:
bash
# 删除 "/remote/sql_server" 这个符号链接。
rm /remote/sql_server
# 创建一个新的符号链接:ln -sP /net/lab1 /remote/sql_server,
# 即将 "/remote/sql_server" 重新指向 "/net/lab1"。
ln -sP /net/lab1 /remote/sql_server
这样就能移除对 "lab2" 的关联,并将该项服务重新分配给 "lab1"。这里的真正优势在于,应用程序可以基于抽象的 "服务名称" 进行编码,而不必绑定到特定的节点名称上。
1.4. 示例
让我们来看几个有关如何使用网络管理器的示例。
QNX Neutrino 原生网络管理器lsm-qnet.so
实际上是一个共享对象,它会被安装到可执行文件io-pkt*
中。
- 本地网络
如果你在一个小型局域网(LAN)中使用 QNX Neutrino 实时操作系统(RTOS),你可以仅使用默认的en_ionet
解析器。当需要解析当前未知的节点名称时,该解析器会在局域网上广播名称请求,拥有该名称的节点会用一个标识消息作出回应。一旦名称被解析出来,它就会被缓存起来,以供日后参考。
由于在启动lsm-qnet.so
时,en_ionet
是默认的解析器,所以你可以简单地执行如下命令:
bash
ls /net/lab2/
如果你的局域网上有一台名为 "lab2" 的机器,你就能看到它的根目录中的内容。
- 远程网络
Qnet 在解析远程名称时会使用域名系统(DNS)。要将lsm-qnet.so
与 DNS 一起使用,你需要在mount
命令行中指定这个解析器:
出于安全考虑,在连接互联网之前,你应该在网络上设置防火墙。如需了解更多信息,请参考查看位于ftp://ftp3.usa.openbsd.org/pub/OpenBSD/doc/
网络位置的 OpenBSD 文档中的pf-faq
的内容。
bash
mount -Tio-pkt -o"mount=:,resolve=dns,mount=.com:.net:.edu" /lib/dll/lsm-qnet.so
在这个示例中,Qnet 会同时使用它原生的en_ionet
解析器(由第一个mount=
命令指示)以及 DNS 来解析远程名称。
请注意,这里我们指定了多种类型的域名(mount=.com:.net:.edu
)作为挂载点,只是为了确保能更好地解析远程名称。
现在你就可以输入如下命令了:
bash
ls /net/qnet.qnx.com/repository
然后你就能获取到qnet.qnx.com
站点上的资源库目录的列表内容了。
@>> 理解总结:
关于网络管理器及本地网络使用部分
- 网络管理器相关情况 :
lsm-qnet.so
作为 QNX Neutrino 的原生网络管理器,以共享对象的形式安装到io-pkt*
可执行文件里,它就像是整个网络管理的核心 "大脑",负责协调网络相关的各种操作,比如名称解析等功能,为网络通信服务提供支撑。- 本地网络中的名称解析 :
在小型局域网环境下,默认使用的en_ionet
解析器发挥着重要作用。可以想象在一个办公室的局域网场景中,有几台电脑(节点)相互连接。当一台电脑想要访问另一台电脑(也就是要解析其节点名称),但还不知道对方具体地址等信息时,使用en_ionet
解析器的电脑就会像在办公室里大声询问一样,通过广播的方式把要找的节点名称广播出去,而那个被找的节点听到广播后,就回复一个能表明自己身份的消息,这样发起访问的电脑就知道对方是谁、在哪了(完成名称解析)。而且为了下次访问更方便,这次解析出来的结果还会被保存(缓存)起来,下次再找这个节点就不用再广播询问了,直接用缓存里的信息就行。像执行ls /net/lab2/
这个命令,就是利用这种默认解析机制,如果局域网里真有叫 "lab2" 的电脑,就能看到它根目录下的内容,这就如同我们在本地网络中很自然地去查看隔壁同事电脑共享出来的文件夹内容一样,依靠名称解析找到了对应的设备并访问其资源。关于远程网络使用部分
- 远程网络名称解析策略 :
当涉及远程网络时,情况就复杂一些了,不能仅靠本地那种广播询问的简单方式了,这时候就要借助大家熟知的域名系统(DNS)来帮忙解析远程节点的名称。通过在mount
命令行里进行特定的设置,让网络管理器知道要使用 DNS 这个强大的 "工具" 来解析远程名称。同时,它还会结合原生的en_ionet
解析器一起工作,就好比解决一个复杂问题时,既用自己熟悉的老办法(en_ionet
),又借助外部专业的新工具(DNS),双管齐下确保能准确解析远程名称。- 防火墙及挂载点设置相关 :
在连接互联网这个大的远程网络环境时,安全是很重要的,所以要提前设置好防火墙,防止外部一些不安全的网络访问进来,就像给家里房子装上坚固的防盗门一样,保障内部网络环境安全。而指定mount=.com:.net:.edu
这些域名作为挂载点,是为了让网络管理器在解析远程名称时更有针对性、更全面,因为不同域名后缀往往代表不同类型的网络主体,涵盖这些常见的,能让它在茫茫的互联网中更容易、更准确地找到对应的远程节点并解析其名称,方便后续像ls /net/qnet.qnx.com/repository
这样的命令执行,从而获取到远程站点(qnet.qnx.com
)上特定目录(repository
)里的内容,实现远程资源的查看等操作,类似于我们能访问互联网上某个网站服务器里特定文件夹下的文件列表一样。