qnx之resource manager(二)
学习目的:
- 编写一个简单的resource manager
- 编写connect函数 (io_open())
- 编写I/O函数(io_read(), io_write())
1. 编写一个简单的resource manager
1.1:编写resource manager的总体步骤
1 . 创建和初始化各种结构体
(a dispatch structure, list of conncet message handles, list of I/O message handles, device structure, resource-manager attribute, dispatch context)
2 . 绑定一个路径名,并传递以上参数
3 . 编写一个大循环
(• 阻塞,并等待接收消息;
• 调用相应的处理函数。该处理函数将处理相应的消息并返回给指定的路径)
1.1.1创建和初始化各种结构体
1 . 创建并初始化dpp
dispatch_t *dpp;
dpp = dispatch_create();
注:1. dpp是resource manager framework用于控制所有部分的粘合剂,是一个全局变量。 2.该结构体的成员是被隐藏的,不透明的。我们看不到它的内容。
2 . 创建并初始化resource manager attributes
resmgr_attr_t resmgr_attr;
memset(&resmgr_attr, 0, sizeof resmgr_attr);
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;
注:该结构体用于设置接收buffer。将通过resmgr_attach传递给resource manager
nparts_max: 用于当使用IOV library时,设置要处理接收buffer 的个数。比如用于MsgReplyv(),该值表示返回的buffer的个数。默认值为1。
msg_max_size: 设置接收buffer的大小。其值一般是 client请求buffer 的最大值。
3 . 初始化操作函数(connect functions和I/O functions 例如: open(filename, …), unlink(filename), read(fd, …), write(fd, …), …)
connection-functions structure 结构体详解:
typedef struct _resmgr_connect_funcs {
unsigned nfuncs;
int (*open) (/* actual prototype */);
int (*unlink) (…);
int (*rename) (…);
} resmgr_connect_funcs_t;
I/O-functions structure 结构体详解:
如上,有以下成员
nfuncs, read, write, close_ocb,
stat, notify, devctl, unblock,
pathconf, lseek, chmod, chown,
utime, openfd, fdinfo, lock,
space, shutdown, mmap, msg,
umount, dup, close_dup, lock_ocb,
unlock_ocb, sync
声明和初始化connect和I/O函数:
resmgr_connect_funcs_t connect_funcs;
resmgr_io_funcs_t io_funcs;
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
connect_funcs.open = io_open;
io_funcs.read = io_read;
io_funcs.write = io_write;
4 . 初始化设备属性结构体 device structure iofunc_attr_t
iofunc_attr_t ioattr;
iofunc_attr_init (&ioattr, S_IFCHR | 0666, NULL, NULL);
注:这是具体指定设备的数据结构;被 iofunc_*() 类函数使用;可以被扩展,用于包含自己的数据。作为参数传递给resmgr_attach()。
1.1.2 绑定一个路径名,并传递以上参数
1.resmgr_attach()
id = resmgr_attach (dpp, &rattr, path, file_type,
flags, &connect_funcs, &io_funcs, handle);
注:
dpp :dispatch_create()返回的指针。
rattr : NULL 或者resmgr_attr_t结构体
path = “/dev/example”
file_type = _FTYPE_ANY; (the usual case)
flags = 0 or control flags…
(_RESMGR_FLAG_BEFORE,_RESMGR_FLAG_AFTER,_RESMGR_FLAG_DIR)
connect_funcs and io_funcs point to the tables of functions
we just created
handle = pointer to device attribues
id = id of this pathname, used for resmgr_detach() call
当多个文件注册到该resource manager时,优先级:BEFORE>non-flagged>AFTER
2 . 分配一个 dispatch context structure
dispatch_context_t *ctp;
ctp = dispatch_context_alloc (dpp);
注:这是消息循环的操作参数;传递给阻塞函数和操作函数;它包含了如 rcvid,指向接收buffer和message info structure;将传递给connect and I/O functions的resmgr_context_t *ctp参数。
1.1.3 编写一个大循环
while (1) {
ctp = dispatch_block (ctp);
dispatch_handler (ctp);
}
注:dispatch_block用于等待消息
dispatch_handler用于操作这些消息。包括调用前面的回调函数。(connect function, I/O functions)
总体函数归纳:
#include <errno.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
int io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
static char *buffer = "Hello world\n";
static resmgr_connect_funcs_t connect_funcs;
static resmgr_io_funcs_t io_funcs;
static iofunc_attr_t attr;
int main(int argc, char **argv)
{
/* declare variables we'll be using */
resmgr_attr_t resmgr_attr;
dispatch_t *dpp;
dispatch_context_t *ctp;
int id;
/* initialize dispatch interface */
if((dpp = dispatch_create()) == NULL) {
fprintf(stderr, "%s: Unable to allocate dispatch handle.\n",
argv[0]);
return EXIT_FAILURE;
}
/* initialize resource manager attributes */
memset(&resmgr_attr, 0, sizeof resmgr_attr);
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;
/* initialize functions for handling messages */
iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
io_funcs.read = io_read;
/* initialize attribute structure used by the device */
iofunc_attr_init(&attr, S_IFNAM | 0666, 0, 0);
attr.nbytes = strlen(buffer)+1;
/* attach our device name */
if((id = resmgr_attach(dpp, &resmgr_attr, "/dev/sample", _FTYPE_ANY, 0,
&connect_funcs, &io_funcs, &attr)) == -1) {
fprintf(stderr, "%s: Unable to attach name.\n", argv[0]);
return EXIT_FAILURE;
}
/* allocate a context structure */
ctp = dispatch_context_alloc(dpp);
/* start the resource manager message loop */
while(1) {
if((ctp = dispatch_block(ctp)) == NULL) {
fprintf(stderr, "block error\n");
return EXIT_FAILURE;
}
dispatch_handler(ctp);
}
return EXIT_SUCCESS;
}
2. 编写connect函数 (io_open())
2.1 引子:
当客户端使用 /dev/example device时的效果
执行命令:
cat /dev/example
cat命令实际的执行过程:
fd = open(“/dev/example”,O_RDONLY);
while (read (fd, buf, BUFSIZ) > 0)
/* write buf to stdout */
close (fd);
客户端的行为如下:
Communications with the Process Manager
给process manager发送一个query消息:
客户端问:谁负责 /dev/example?
process manager回答:返回一个reply(nd, pid, chid),即返回负责该filename的resource manager的特征。Communications with example
1 . 一个open 消息
(包括: 1. open message“open this device for read”;2. returns a reply, “yes, open succeeded, proceed”; 3. the open() library call returns a file descriptor, fd)。
2 . 一个read消息
(包括: 1.“get me some data“; 2. returns a reply,)。
3 . 一个close消息
图解:
注:在客户端方,所有这些消息的交互均是在c库中完成。如 open(), read(), and close()等。
2.2 io_open()的编写
int io_open (resmgr_context_t *ctp,
io_open_t *msg,
RESMGR_HANDLE_T *handle,
void *extra);
参数解析:
- ctp (实际为dispatch context ctp)
1.指向 resource-manager context structure(dispatch context ctp)
2.关于接收消息的相关信息
3.包含成员
typedef struct _resmgr_context {
int rcvid;
struct _msg_info info;
resmgr_iomsgs_t *msg;
unsigned msg_max_size;
int status;
int offset;
IOV iov [1];
} resmgr_context_t;
成员解析:rcvid:MsgReceive()函数的返回值,并且会返回给客户端。
info: MsgReceive()返回的message information structure
*msg:the receive buffer;被声明为一个union,以适应所有可能的消息类型。
msg_max_size:被msg指向的receive buffer的大小
status:用于对client的reply
offset: receive buffer的header大小
iov:是由传递给resmgr_attach()的rattr确定。用于多个buffer的reply。
- msg
io_open_t *msg:
typedef union
{ // contains at least the following
struct _io_connect connect;
} io_open_t;
struct _io_connect
{ // contains at least the following
uint16_t type;
uint32_t mode;
char path [1]; // variable length
};
- handle (实际为device structure iofunc_attr_t)
用 resmgr_attach()函数初始化的。本例中的iofunc_attr_t被赋值给handle
简单的io_open()函数的框架:
int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra)
{
printf("got an open message\n");
return iofunc_open_default (ctp, msg, handle, extra);
}
3. 编写I/O函数(io_read(), io_write())
int io_read (resmgr_context_t *ctp,
io_read_t *msg,
RESMGR_OCB_T *ocb);
int io_write (resmgr_context_t *ctp,
io_write_t *msg,
RESMGR_OCB_T *ocb);
3.1 ocb的解析
- open contral block
- one ocb per open()
- 维护open()函数和I/O函数的上下文(在例子中是由 iofunc_open_default()分配并初始化,供I/O函数使用。)
- 将也可以是一个iofunc_ocb_t
- iofunc_ocb_t可以添加自己的私有数据。
- 指向已打开的设备的device structure iofunc_attr_t
图解:
3.2 io_read()函数的编写
3.2.1 read消息将由read()函数产生
bytes_read = read( fd, buf, nbytes )
{
struct _io_read hdr;
hdr.type = _IO_READ;
hdr.nbytes = nbytes;
...
return( MsgSend( fd, &hdr, sizeof(hdr), buf,
nbytes ) );
}
3.2.2 接收到的消息
typedef union
{
struct _io_read i;
} io_read_t;
struct _io_read
{ // contains at least the following
unsigned short type; // message type = _IO_READ
long nbytes; // number of bytes to be read
uint32_t xtype; // extended type
};
注:union可以用于扩展其他消息类型。
3.2.3 the reply 即read()的回应
- 如果read函数指向成功
the reply message将是你读取的数据(并且没有header)
要使得read()函数中的MsgSend()的返回值为成功读取的数据量的大小。需要进行设置。方法如下:
_IO_SET_READ_NBYTES (ctp, nbytes_read);
SETIOV (ctp->iov, data, nbytes_read);
return _RESMGR_NPARTS(1);
当从the resource manager library返回时,系统将nbytes_read 作为参数传递给MsgReplyv()。然后read()函数将会返回此值。
- 如果失败
return errno_value;
3.2.4 the xtype member
通常为_IO_XTYPE_NONE
具体代码:
int
io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
int nleft;
int nbytes;
int nparts;
int status;
if ((status = iofunc_read_verify (ctp, msg, ocb, NULL)) != EOK)
return (status);
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
return (ENOSYS);
/*
* On all reads (first and subsequent), calculate
* how many bytes we can return to the client,
* based upon the number of bytes available (nleft)
* and the client's buffer size
*/
nleft = ocb->attr->nbytes - ocb->offset;
nbytes = min (msg->i.nbytes, nleft);
if (nbytes > 0) {
/* set up the return data IOV */
SETIOV (ctp->iov, buffer + ocb->offset, nbytes);
/* set up the number of bytes (returned by client's read()) */
_IO_SET_READ_NBYTES (ctp, nbytes);
/*
* advance the offset by the number of bytes
* returned to the client.
*/
ocb->offset += nbytes;
nparts = 1;
} else {
/*
* they've asked for zero bytes or they've already previously
* read everything
*/
_IO_SET_READ_NBYTES (ctp, 0);
nparts = 0;
}
/* mark the access time as invalid (we just accessed it) */
if (msg->i.nbytes > 0)
ocb->attr->flags |= IOFUNC_ATTR_ATIME;
return (_RESMGR_NPARTS (nparts));
}
3.3 io_write()函数的编写
3.2.1 write消息将由write()函数产生
bytes_written = write( fd, buf, nbytes )
{
struct _io_write hdr;
iov_t iov[2];
hdr.type = _IO_WRITE;
hdr.nbytes = nbytes;
...
SETIOV(&iov[0], &hdr, sizeof(hdr));
SETIOV(&iov[1], buf, nbytes );
return( MsgSendv( fd, iov, 2, NULL, 0 ) );
}
3.2.2 接收到的消息
typedef union
{
struct _io_write i;
} io_write_t;
/* the data to be written usually follows the io_write_t */
struct _io_write
{ // contains at least the following
unsigned short type; // message type = _IO_WRITE
long nbytes; // number of bytes to write
uint32_t xtype; // extended type
};
3.2.3 the reply 即write()的回应
这里写代码片
if successful:
• there is no data to reply with
• the return value from the write()’s MsgSendv() would be the
number of bytes successfully written. To set this do:
_IO_SET_WRITE_NBYTES (ctp, nbytes_written);
return _RESMGR_NPARTS(0);
When you return to the resource manager library, it will pass
nbytes_written as the status parameter to MsgReplyv().
The write() will return this value.
- if failed, do:
return errno_value;
3.2.4 the xtype member
_IO_XTYPE_OFFSET
具体代码:
int io_write (resmgr_context_t *ctp, io_write_t *msg,
RESMGR_OCB_T *ocb)
{
int status;
if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK)
return status;
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
// msg -> i.nbytes is the number of byte to be written,
// we are telling it that we wrote everything (msg -> i.nbytes)
_IO_SET_WRITE_NBYTES (ctp, msg -> i.nbytes);
if (msg->i.nbytes > 0) /* mark times for update */
ocb->attr->flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;
return _RESMGR_NPARTS (0);
}
3.2.5 getting the data
io_write回调函数:
int io_write (resmgr_context_t *ctp, io_write_t *msg, …)
有这些信息:
msg->i.nbytes:发送buffer的大小
ctp->msg:接收buffer的基地址
ctp->msg_max_size:接收buffer的大小
ctp->info.msglen:实际发送到接收buffer数据的大小。包括header
ctp->offset:header的大小
1 . 发送方的buffer大小和接收方实际接收的buffer大小相等, 我们可以一次接收所有的 write data
2 . 发送方的buffer大小比接收方buffer大小大,不能一次接收所有数据
对于第一种情况,我们可以一次性直接接收所有数据。
对于第二种情况,我们不能一次接收所有数据。解决方案:需要找一个地方存数据,以及用resmgr_msgread()函数接收剩余的数据。
注:在ctp->offset以自动添加了resmgr_msgread()。
情况1的code:
`
int io_write (resmgr_context_t *ctp, io_write_t *msg,
RESMGR_OCB_T *ocb)
{
if( need_more_data ) {
char *buf;
buf = malloc( msg->i.nbytes );
...
resmgr_msgread (ctp, buf, msg -> i.nbytes,
sizeof (msg -> i)); //因为函数已经自动添加了header,所以从i开始。
// do something with buf
free( buf );
...
}
}
情况2的解决方案:
- find available cache buffers and use resmgr_msgreadv() to fill them
- use a small buffer and multiple resmgr_msgread() calls to work through the client’s message, a piece at a time
- copy the already received data from the receive buffer, and then use resmgr_msgread() for the rest
- ensure in advance that the receive buffer will be large enough for your largest write
注:我们可以通过resmgr_attr_t结构体来设置the size of the receive buffer,然后传递给resmgr_attach()。