qnx之resource manager(二)

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()函数的框架:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值