qnx之resource manager(二)

来源:互联网 发布:网络产品线是干什么的 编辑:程序博客网 时间:2024/06/13 15:43

qnx之resource manager(二)

学习目的:

  1. 编写一个简单的resource manager
  2. 编写connect函数 (io_open())
  3. 编写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()函数的框架:

intio_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

具体代码:

intio_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()。

0 0