QNX相关历史文章:
Fleshing Out the Skeleton
在讲完大体框架后,这篇文章将涉及到更多细节:消息类型;资源管理器属性;添加功能;安全事项等。
1. Message types
在前文中也提到过,资源管理器需要处理两类消息:
- connect message,连接消息
- I/O message, IO消息
1.1 connect message
客户端发出connect message来执行基于路径名的操作。当调用resmgr_attach()函数时,会将一个指针传递给resmgr_connect_funcs_t结构,该结构定义了一系列连接函数:
typedef struct _resmgr_connect_funcs {
unsigned nfuncs;
int (*open) (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra);
int (*unlink) (resmgr_context_t *ctp, io_unlink_t *msg,
RESMGR_HANDLE_T *handle, void *reserved);
int (*rename) (resmgr_context_t *ctp, io_rename_t *msg,
RESMGR_HANDLE_T *handle,
io_rename_extra_t *extra);
int (*mknod) (resmgr_context_t *ctp, io_mknod_t *msg,
RESMGR_HANDLE_T *handle, void *reserved);
int (*readlink) (resmgr_context_t *ctp, io_readlink_t *msg,
RESMGR_HANDLE_T *handle, void *reserved);
int (*link) (resmgr_context_t *ctp, io_link_t *msg,
RESMGR_HANDLE_T *handle,
io_link_extra_t *extra);
int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg,
RESMGR_HANDLE_T *handle, void *reserved);
int (*mount) (resmgr_context_t *ctp, io_mount_t *msg,
RESMGR_HANDLE_T *handle,
io_mount_extra_t *extra);
} resmgr_connect_funcs_t;
可以调用iofunc_func_init()接口来用默认的处理程序指针来初始化这个结构。自己也可以重写某些接口,进行覆盖即可。
需要注意的是,resmgr_attach()接口只是将函数指针拷贝到resmgr_connect_func_t和resmgr_io_funcs_t结构中,而不是拷贝整个结构。需要分配这些结构,将它们声明为静态的或者全局变量。如果资源管理器用于具有不同处理程序的多个设备,则应该分开定义独立的结构。
这些连接消息都有一个_IO_CONNECT类型,此外还有子类型来进行分类,各个字段介绍如下:
-
nfuncs,结构中函数的个数,可用于扩展; -
open,处理客户端的open()/fopen()/sopen()等请求,消息子类型包括_IO_CONNECT_COMBINE,_IO_CONNECT_COMBINE_CLOSE,_IO_CONNECT_OPEN等; -
unlink,处理客户端unlink()请求,消息子类型为_IO_CONNECT_UNLINK; -
rename,处理客户端rename()请求,消息子类型为_IO_CONNECT_RENAME; -
mknod,处理客户端mkdir()/mkfifo()/mknod()请求,消息子类型为_IO_CONNECT_MKNOD; -
readlink,处理客户端readlink()请求,消息子类型为_IO_CONNECT_READLINK; -
link,处理客户端link()请求,消息子类型为_IO_CONNECT_LINK; -
unlock,处理来自内核的请求,以便在连接消息阶段接触对客户端的阻塞; -
mount,处理客户端mount()请求,消息子类型为_IO_CONNECT_MOUNT;
1.2 I/O messages
I/O消息依赖于客户端和资源管理器之间已有的绑定关系,比如当客户端调用read()函数发送_IO_READ消息时,需要先通过open()函数来与资源管理器建立绑定关系,进而获取到文件描述符。
regmgr_io_funcs_t结构体定义了I/O消息处理函数:
typedef struct _resmgr_io_funcs {
unsigned nfuncs;
int (*read) (resmgr_context_t *ctp, io_read_t *msg,
RESMGR_OCB_T *ocb);
int (*write) (resmgr_context_t *ctp, io_write_t *msg,
RESMGR_OCB_T *ocb);
int (*close_ocb) (resmgr_context_t *ctp, void *reserved,
RESMGR_OCB_T *ocb);
int (*stat) (resmgr_context_t *ctp, io_stat_t *msg,
RESMGR_OCB_T *ocb);
int (*notify) (resmgr_context_t *ctp, io_notify_t *msg,
RESMGR_OCB_T *ocb);
int (*devctl) (resmgr_context_t *ctp, io_devctl_t *msg,
RESMGR_OCB_T *ocb);
int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg,
RESMGR_OCB_T *ocb);
int (*pathconf) (resmgr_context_t *ctp, io_pathconf_t *msg,
RESMGR_OCB_T *ocb);
int (*lseek) (resmgr_context_t *ctp, io_lseek_t *msg,
RESMGR_OCB_T *ocb);
int (*chmod) (resmgr_context_t *ctp, io_chmod_t *msg,
RESMGR_OCB_T *ocb);
int (*chown) (resmgr_context_t *ctp, io_chown_t *msg,
RESMGR_OCB_T *ocb);
int (*utime) (resmgr_context_t *ctp, io_utime_t *msg,
RESMGR_OCB_T *ocb);
int (*openfd) (resmgr_context_t *ctp, io_openfd_t *msg,
RESMGR_OCB_T *ocb);
int (*fdinfo) (resmgr_context_t *ctp, io_fdinfo_t *msg,
RESMGR_OCB_T *ocb);
int (*lock) (resmgr_context_t *ctp, io_lock_t *msg,
RESMGR_OCB_T *ocb);
int (*space) (resmgr_context_t *ctp, io_space_t *msg,
RESMGR_OCB_T *ocb);
int (*shutdown) (resmgr_context_t *ctp, io_shutdown_t *msg,
RESMGR_OCB_T *ocb);
int (*mmap) (resmgr_context_t *ctp, io_mmap_t *msg,
RESMGR_OCB_T *ocb);
int (*msg) (resmgr_context_t *ctp, io_msg_t *msg,
RESMGR_OCB_T *ocb);
int (*reserved) (resmgr_context_t *ctp, void *msg,
RESMGR_OCB_T *ocb);
int (*dup) (resmgr_context_t *ctp, io_dup_t *msg,
RESMGR_OCB_T *ocb);
int (*close_dup) (resmgr_context_t *ctp, io_close_t *msg,
RESMGR_OCB_T *ocb);
int (*lock_ocb) (resmgr_context_t *ctp, void *reserved,
RESMGR_OCB_T *ocb);
int (*unlock_ocb) (resmgr_context_t *ctp, void *reserved,
RESMGR_OCB_T *ocb);
int (*sync) (resmgr_context_t *ctp, io_sync_t *msg,
RESMGR_OCB_T *ocb);
int (*power) (resmgr_context_t *ctp, io_power_t *msg,
RESMGR_OCB_T *ocb);
} resmgr_io_funcs_t;
这个结构的使用与resmgr_connect_funcs_t一样,对应到客户端的不同请求,及消息类型。
1.3 Default message handling
由于资源管理器接收的大量消息处理的是一组公共的属性,因此QNX提供了一个iofunc_*()共享库,实现了一些默认的消息处理函数。目前实现的默认函数可用于处理客户端的以下请求:
chmod()chown()close()devctl()fpathconf()fseek()fstat()lockf()lseek()mmap()open()pathconf()stat()utime()
2. Setting resource manager attribute
除了定义connect和I/O函数结构体之外,resmgr_attach()函数还需要用到resmgr_attr_t来指定资源管理器的属性。定义如下:
typedef struct _resmgr_attr {
unsigned flags;
unsigned nparts_max;
unsigned msg_max_size;
int (*other_func)(resmgr_context_t *,
void *msg);
unsigned reserved[4];
} resmgr_attr_t;
各个成员介绍如下:
-
flags
可用于修改资源管理器接口的行为,可以将其设置为0,或者是以下不同状态的组合:
-
RESMGR_FLAG_ATTACH_LOCAL,设置资源管理器,不向procnto注册路径,可以向资源管理器通道发送消息; -
RESMGR_FLAG_ATTACH_OTHERFUNC,该结构中的other_func成员指向一个用于未处理I/O消息的函数; -
RESMGR_FLAG_CROSS_ENDIAN,服务器支持跨端处理,可以在服务器端做必要的转换,客户端不需要做任何事情; -
RESMGR_FLAG_NO_DEFAULT,未实现; -
RESMGR_FLAG_RCM,在处理请求时自动采取客户端的资源约束模式;
nparts_max
分配给IOV数组的组件数量。msg_max_size
消息缓冲的大小。
这些成员在实现自己的处理函数时很重要。other_func
other_func可用于指定一个例程,在资源管理器接收到不能理解的I/O消息时调用。要使用这个成员,需要在flag字段里置上RESMGR_FLAG_ATTACH_OTHERFUNC。如果other_func成员设置成NULL的话,资源管理器在收到不能理解的消息时,便会返回ENOSYS错误给客户端。
对于非I/O消息类型时,需要使用message_attach()接口来将消息绑定到dispatch handle上。
3. Ways of adding functionality to the resource manager
3.1 Using the default functions
下边是一个使用自己的io_open处理程序的示例:
main (int argc, char **argv)
{
…
/* install all of the default functions */
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
/* take over the open function */
connect_funcs.open = io_open;
…
}
int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra)
{
return (iofunc_open_default (ctp, msg, handle, extra));
}
上述的代码只是一个增量步骤,可以允许在调用默认处理函数之前或者之后执行某些操作,比如可以实现如下代码:
/* example of doing something before */
extern int accepting_opens_now;
int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra)
{
if (!accepting_opens_now) {
return (EBUSY);
}
/*
* at this point, we're okay to let the open happen,
* so let the default function do the "work".
*/
return (iofunc_open_default (ctp, msg, handle, extra));
}
或者:
/* example of doing something after */
int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra)
{
int sts;
/*
* have the default function do the checking
* and the work for us
*/
sts = iofunc_open_default (ctp, msg, handle, extra);
/*
* if the default function says it's okay to let the open
* happen, we want to log the request
*/
if (sts == EOK) {
log_open_request (ctp, msg);
}
return (sts);
}
这种方法的优势是,只需要很少的工作就可以添加到标准的默认POSIX处理程序中。
3.2 Using the helper functions
在很多默认处理函数中,都调用到了帮助函数,比如下边的iofunc_chmod_default()和iofunc_stat_default():
int
iofunc_chmod_default (resmgr_context_t *ctp, io_chmod_t *msg,
iofunc_ocb_t *ocb)
{
return (iofunc_chmod (ctp, msg, ocb, ocb -> attr));
}
int
iofunc_stat_default (resmgr_context_t *ctp, io_stat_t *msg,
iofunc_ocb_t *ocb)
{
iofunc_time_update (ocb -> attr);
iofunc_stat (ocb -> attr, &msg -> o);
return (_RESMGR_PTR (ctp, &msg -> o,
sizeof (msg -> o)));
}
在上边的代码中分别都调用到了iofunc_chmod()、iofunc_time_update()、iofunc_stat()等帮助函数。
更复杂的case如下:
int
iofunc_open_default (resmgr_context_t *ctp, io_open_t *msg,
iofunc_attr_t *attr, void *extra)
{
int status;
iofunc_attr_lock (attr);
if ((status = iofunc_open (ctp, msg, attr, 0, 0)) != EOK) {
iofunc_attr_unlock (attr);
return (status);
}
if ((status = iofunc_ocb_attach (ctp, msg, 0, attr, 0))
!= EOK) {
iofunc_attr_unlock (attr);
return (status);
}
iofunc_attr_unlock (attr);
return (EOK);
}
调用了以下帮助函数:
-
iofunc_attr_lock()接口,获取锁,用于互斥访问属性结构; -
iofunc_open(),进行权限验证; -
iofunc_ocb_attach(),绑定OCB结构; -
iofunc_attr_unlock(),释放锁;
3.3 Writing the entire function yourself
有时候默认的处理函数对特定的资源管理器来说没有用处,可以自己实现处理函数,在这些实现的处理函数中,可以去调用帮助函数,比如iofunc_read_verify()。
4. Security
资源管理器通常是一个特权进程,因此需要小心,防止客户端迫使它耗尽资源或损耗系统。在设计资源管理器时,应该考虑以下几点:
- 管理路径名空间中资源管理器条目的权限,可以将权限指定为
iofunc_attr_init()的参数; - 资源管理器通常需要运行在root权限,以便能与路径名空间绑定,但是更好的方式是运行在非root权限,而使用
procmgr_ability()去获取特权的能力。 - 如果资源管理器不是一个没有资源约束阈值的关键进程,它可以简单的运行在约束模式下,而如果是一个关键进程,应该保持
PROCMGR_AID_RCONSTRAINT能力,需要确保受约束的客户端不使用它来分配超过当前阈值的资源。 - 对客户端的能力进行检查,通常可以调用
ConnectClientInfoAble()或iofunc_client_info_able()来检查。
