路径名解析(Pathname Resolve)

路径名解析(Pathname Resolve)

正因为路径名非常重要,路径名解析几乎存在于QNX的每一个角落。正确地理解QNX路径名解析,对于理解QNX上系统的实现,有很深刻的意义。

比如有个服务器程序,通过“name_attach()”或是更常用的"resmgr_attach()",将自己的频道(nd0,pid0,chid0),与一个路径名 “/dev/myservice” 关联起来了,具体,客户端是怎样与这个服务器进行连接的呢?

客户端程序用我们熟悉的 open("/dev/myservice", O_RDWR), 这个调用,最后会进入到一个_connect_request() 的递归函数里,对路径名“/dev/myservice"进行解析。这个函数,看到路径名的第一个字符是’/’,就会先连接procnto里的”路径名管理器“,路径名管理器的频道是默认的,nd=ND_LOCAL_NODE, pid=PATHMGR_PID, chid=PATHMGR_CHID。连接成功后,解析程序会把”尚未解析“的路径(dev/myservice)通过_IO_CONNECT消息,发给路径名管理器。路径名管理器查表后知道,这个路径与(nd0, pid0, chid0)关联,所以路径名管理器会返回一个”请转去连接 (nd0, pid0, chid0)“的消息。客户端程序据此,会用 _connect_request() 再与 nd0, pid0, chid0, 进行连接,连接后发送_IO_CONNECT请求,如果成功返回,则连接号(Connection ID)返回,最终作为 open() 的返回值,返回给用户程序。

下面这个,是稍微复杂一的例子。如果open("/net/remote/dev/ser1", O_RDWR),具体的解析是怎么进行的呢?

客户端
发送" net/remote/dev/ser1" 给路径管理器

服务器端
路径管理器发现最长符合(longest match)的服务器是 "net/",所以返回 "请连接 (0, io-pkt pid, qnet chid)"

客户端
发送 "remote/dev/ser1" 给 (0, io-pkt pid, qnet chid)

服务器端
QNET收到这个请求,查表找到 "remote" 的机器号nd,并返回 "请连接(ndRemote, PATHMGR_PID, PATHMGR_CHID)"

客户端
发送 "dev/ser1" 给(ndRemote, PATHMGR_PID, PATHMGR_CHID),其实这也就是机器remote上的路径名管理器

服务器端
remote上的路径名管理器,发现 "dev/ser1" 是本地的 devc-ser8250这个服务器注册的,所以返回:"请连接 (ndRemote, devc-8250 pid, devc-8250 chid)"

客户端
发送"" 给(ndRemote, devc-8250 pid, devc-8250 chid),请求连接

服务器端
devc-8250收到这请求,验证客户端有足够的权限后,返回 EOK
客户端将连接号通过open()的返回值返回给程序,至此,一个与远程的串口驱动的连接就完成了。

在一个QNX系统下,多个资源管理器,如果注册了相关重叠的路径名,会发生什么事情呢?比如,devb-eide(0,eide-pid,eide- chid)注册了 “/”, (在通常的UNIX系统里,这个叫文件系统的mount,在QNX里,这个跟用resmgr_attach("/", …)没什么两样);而 devb-ram(0, ram-pid, ram-chid)注册了 “/fs”,而一个USB驱动(0, usb-pid, usb-chid)注册了 “/fs/usb”;这时候,怎么保证客户端能正确读写各服务器里的文件呢?

服务器程序   注册的路径名   拥有的文件
devb-eide    /        /home, /home/user, /fs/usb/diskfile
devb-ram    /fs       /fs/ram/ramfile
usb       /fs/usb     /fs/usb/usbfile

请注意,硬盘上有一个叫fs的子目录,里面有个myfilesys的文件夹,下面有个file0文件。这个fs子目录与devb-ram注册的 /fs 是重叠的。如果客户端open("/fs/usb/usbfile", O_RDWR)时,会发生什么事呢?

客户端
发送 "fs/usb/usbfile" 到路径名管理器

服务器
路径名管理器发现有3个服务器都有可能,这时它会把所有3个服务器的nd/pid/chid都返回给客户端。同时还保证"最长附合"(longest match)的服务器排在最上面,在这个例子里,usb的注册是 /fs/usb,相对被查询的 "fs/usb/usbfile"来说是3个服务器里最长附合的,其次是devb-ram, 然后再是 devb-eide。这个返回是这样的:

   请连接:  (0, usb-pid, usb-chid)
         (0, ram-pid, ram-chid)
         (0, eide-pid, eide-chid)

客户端
发送 "usbfile" 到 (0, usb-pid, usb-chid)

服务器
usb服务器检查发现自己有这个文件,如果客户端权限没有问题的话,返回 EOK

客户端
成功返回

如果客户端的请求是 open("/fs/ram/ramfile", O_RDWR),那么路径名管理器返回的列表里,就只有devb-ram, devb-eide两个服务器了。因为usb服务器注册的是 /fs/usb,所以 “/fs/ram/ramfile” 决不会在它上面。

当然,如果客户端的请求是 open("/fs/usb/diskfile", O_RDWR) 时,会怎么样呢?

客户端
发送 "fs/usb/diskfile" 到路径名管理器

服务器端
路径名管理器发现有3个服务器都有可能,这时它会把所有3个服务器的nd/pid/chid都返回给客户端。同时还保证 "最长附合"(longest match)的服务器排在最上面,在这个例子里,usb的注册是 /fs/usb,相对被查询的 "fs/usb/usbfile"来说是3个服务器里最长附合的,其次是devb-ram, 然后再是 devb-eide。这个返回是这样的:

   请连接:  (0, usb-pid, usb-chid)
         (0, ram-pid, ram-chid)
         (0, eide-pid, eide-chid)

客户端
发送 "diskfile" 到 (0, usb-pid, usb-chid)

服务器端
usb服务器检查发现自己并没有这个文件,这时它返回一个标准的出错信息 ENOENT

客户端
客户端收到ENOENT,意识到列表里的第一个服务器里没有指定的路径名;它会接下去试第二个服务器
   发送 "usb/diskfile" 到 (0, ram-pid, ram-chid)

服务器端
deb-ram发现自己根本就没有 "usb" 这样的文件夹,也返回一个标准出错信息 ENOENT

客户端
客户端收到ENOENT,意识到第二个服务器里也没有指定的路径名;再试第三个服务器
发送 "fs/usb/diskfile" 到 (0, eide-pid, eide-chid)

服务器端
devb-eide收到这个请求,检查后返回成功。

客户端
成功返回

如果客户端请求 open("/fs/usb/nobody", O_RDWR),那么在同三个服务器都交换信息后,最后 open() 会收到一个 ENOENT 的出错返回,表示找不到这个文件。

还有要注意的是,只有特定的出错信息号(像这例子里的ENOENT),才会收客户端接着尝试下一个服务器。不是所有的出错都会使客户端转向下一个服务器。比如客户端请求 open("/fs/usb/usbfile", O_RDWR),但usb服务器发现客户没有权利WRITE,这时它会返回一个 EPERM。客户端在收到 EPERM 后,会直接出错返回,而不是去尝试下一个服务器。

换言之,如果客户端明明是 open("/fs/usb/diskfile", O_RDWR); 但第一个接到这个请求的usb服务器,因为文件系统有问题或是别的原因,返回一个比如 EIO;这时,虽然正确的文件是在devb-eide上,但客户端也不会去尝试devb-ram和devb-eide,而是将收到的 EIO 直接出错返回的。

最后,还有一种情形是几个服务器注册完全一样的路径名;在QNX里这是允许的。路径名解析的过程是一样的,路径名管理器会把所有汪册同一路径名的服务器一齐返回,让客户端去连接。唯一的区别是谁在最上面。默认的方法是先注册的服务器在最上面,但 resmgr_attach()有一个标志位可以改变这个顺序。有兴趣的可以去查一查。

几个服务器注册同一路径名,一种情形是为了冗余,万一某个服务器 crash了,客户端的open()也不会受影响。只是路径名管理器返回的列表里少了一项而已。还有一种情形是动态均衡服务器的负荷,在最上面的服务器接了太多请求时,可以故意返回 ENOENT,让下一个服务器去处理。

从上面可以看到,一个open()的开销是相对比较大的。虽然“最长附合”原则提供了一定程度的帮助,(尽量保证第一个服务器就能找对)但总的来说,开销还是很大,特别是企图连接一个不存在的文件时。所以一般在设计系统时,可以在初始化阶段预先打开的文件要预先open()好,这样在系统真正运行时不需要搜索不同的服务器了。

总而言之,路径名解析在QNX里有很重要的作用。要正确理解。另外,服务器的注册,可以是 /dev/ser1 这样的一个”文件“,也可以是 “/dev/socket” 这样一个目录。当”文件“与”目录“重叠”时,具体路径管理器返回的列表是怎样的,在resmgr_attach()的文档里也有很详细的说明,有用到的时候可以仔细查一下。

恩 现在有点清晰了

希望xtang多发点这样的文章。扫扫盲哈

有点复杂,顺便问一句,怎么防止解析出错的矛盾的,或者说这种情况下有没有办法呢?

呵呵,所以说来话长吧。:slight_smile:

我不知道你的“解析出错”是什么意思?

顶顶顶 :stuck_out_tongue:

唐兄,解析出错是您上面讲的“注册了相关重叠的路径名”,这是不是就是我原来遇到硬盘坏了导致CF卡也启动不了的原因呢?我想知道怎么避免这种情况出现

准确地说,这个问题不是“解析错误”,起因是 1) 有重叠注册的路径名 而且 2) 其中一个服务器(因为硬件原因)而出错了。

你的具体情况,还要对照上面的说明具体确认才行。

  1. 你的硬盘有几个QNX分区,启动后都分别mount到哪里了?
    2)你的CF卡有几个QNX分区,都分别mount到哪里了?
    3)mount的先后顺序是什么?
    4)硬盘坏了后,具体CF卡的什么文件不能读写了?
    5)指定全路径 (ls /my/cfcard/path/badfile) 也无法读写吗?

要这样一步步找,才能确认问题所在。

我的CF卡有一个分区(类型79),硬盘两个分区(类型77、78),CF卡mount到/,硬盘mount到/fs中的两个子目录,mount的顺序是先CF卡再硬盘,硬盘坏了后只能看到/bin等几个系统目录,而我自己的一些程序目录就看不到了

如果你能肯定硬盘是mount到 /fs/ 下的,那么,即使硬盘坏了,也只对 /fs/下的子目录的路径解析起干扰作用;与CF卡里的路径无关。(你CF卡上没有一个 /fs 目录吧)

你“自己的一些程序目录”是在哪个介质的哪个路径名下呢?

xtang, 对于open一个文件系统中的文件,比如flash中的一个文件,系统开销也很大吗?这时客户端是和谁建立连接呢

“开销大”,其实是相对别的操作系统来讲的。当然,如果你的路径名空间很复杂,有很大重叠,每次开文件都要同4、5个资源管理器通信的话,开销就会很大。

Flash中的文件,你先要运行什么才能读写呢?那个提供那条路径名的程序,就是客户端连接的对象。通常情况下,是devg-generic吧。

我用PxLoadImage("/fs0p1/biao.bmp",NULL);加载flash文件系统中的图片,总是返回no such file or directory,路径名应该没问题啊