qnx之resource manager(三)
device-specific and per-open data
学习目的
1.举例引入概念
2.代码编写步骤
1.举例引入概念
a time resource manager,如下所示:
该resource manager必须要有以下特性:
1.操作多个设备
•/dev/time/now
•/dev/time/min
•/dev/time/hour
2.在read时,能返回多个数据
•返回一次数据
•在后面的reads中,返回0以表示读到了文件的最后
3.维护open和read之间的上下文
注:要实现以上需求,必须要满足以下要求:a.name和device的数量都知道;b.device的数量不是特别大(少于100)
2.代码编写步骤:
1.注册这3个新name
resmgr_attach (dpp, &rattr, “/dev/time/now”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &nowattr);
resmgr_attach (dpp, &rattr, “/dev/time/hour”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &hourattr);
resmgr_attach (dpp, &rattr, “/dev/time/min”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &minattr);
或者
char *devnames [NumDevices] = { … initializers … };
…
for (i = 0; i < NumDevices; i++) {
resmgr_attach (…, devnames [i], …);
}
图解:
2.编写I/O和connect 函数相关
每一个open()都匹配一个ocb,并且指向我们打开的设备的attribute structure。
attribute structure指向更高级别的structure(mount structure),它描述了 普通mountpoint 信息。
重写attr,以包含我们自己的数据:
struct Timeattr_s;
#define IOFUNC_ATTR_T struct Timeattr_s
#include
#include
typedef struct Timeattr_s {
iofunc_attr_t attr; // encapsulate iofunc layer’s
// attr
char * format; // our data
} Timeattr_
导致io_open函数原型变化:
int io_open (..., RESMGR_HANDLE_T *handle, ...)
变为
int io_open (..., Timeattr_t *tattr, ...)
重写ocb,以包含我们自己的数据:
struct Timeocb_s;
#define IOFUNC_OCB_T struct Timeocb_s
#include
#include
typedef struct Timeocb_s {
iofunc_ocb_t ocb; // encapsulate iofunc layer’s ocb
char *buffer; // for buffer of data we return
int bufsize; // how many bytes are in buffer
} Timeocb_t;
使io_read函数原型变化:
int io_read (..., RESMGR_OCB_T *ocb)
变为
int io_read (..., Timeocb_t *tocb)
ocb的动态分配和释放
1:先定义iofunc_funcs_t结构体,
2:然后将该结构体地址放入iofunc_mount_t结构体中,
3:再将iofunc_mount_t结构体地址放入iofunc_attr_t
IOFUNC_OCB_T *time_ocb_calloc (resmgr_context_t *ctp,
IOFUNC_ATTR_T *attr) //由iofunc_open_default()函数调用
void time_ocb_free (IOFUNC_OCB_T *ocb)//由iofunc_close_ocb_default()函数调用
Timeocb_t *
time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr)
{
Timeocb_t *tocb;
tocb = calloc (1, sizeof (Timeocb_t));
// do anything else to our per ocb data
tocb -> buffer = NULL;
return tocb;
}
void
time_ocb_free (Timeocb_t *tocb)
{
if (tocb -> buffer) {
free (tocb -> buffer);
}
free (tocb);
}
//1
iofunc_funcs_t time_ocb_funcs = {
_IOFUNC_NFUNCS,
time_ocb_calloc,
time_ocb_free
};
//2
iofunc_mount_t time_mount = { 0, 0, 0, 0, &time_ocb_funcs };
//3
for (i = 0; i < NUMDEVICES; i++) {
iofunc_attr_init (&timeattrs [i].attr, ...);
timeattrs [i].attr.mount = &time_mount;
timeattrs [i].format = formats[i];
resmgr_attach (dpp, &rattr, devnames [i], _FTYPE_ANY, 0,
&connect_funcs, &io_funcs,
&timeattrs [i]);
}
注:系统中有
typedef struct _iofunc_ocb {
IOFUNC_ATTR_T *attr;
…
} iofunc_ocb_t;
所以iofunc_ocb_t包含了我们重写的IOFUNC_ATTR_T,并且IOFUNC_ATTR_T包含了iofunc_attr_t
以上几个结构体包含关系如图:
总体代码:
/*
* time.c
*
* This module contains the source code for the /dev/time
* resource manager. This illustrates returning data to a client
* that is different on a per-device basis.
*
* This module contains all of the functions necessary.
*
*/
#include
#include
#include
#include
#include
/*
* we are extending the ocb and the attr (Timeocb_s and Timeattr_s are
* really Timeocb_t and Timeattr_t below)
*/
struct Timeattr_s;
#define IOFUNC_ATTR_T struct Timeattr_s
struct Timeocb_s;
#define IOFUNC_OCB_T struct Timeocb_s
#include
#include
#include
#include
/*
* Define our device attributes structure.
*/
typedef struct Timeattr_s {
iofunc_attr_t attr;
char * format; // output format of each device
} Timeattr_t;
/*
* Define our open context block structure.
*/
typedef struct Timeocb_s {
iofunc_ocb_t ocb; // this has 'IOFUNC_ATTR_T *attr' at its top
// (ocb.offset is the current position
// in the buffer)
char *buffer; // the data returned to the client
int bufsize; // the size of the buffer
} Timeocb_t;
/*
* Number of devices, and how long or format strings are allowed to be.
*/
#define NUMDEVICES 3
#define MAX_FORMAT_SIZE 64
/*
* Declare the tables used by the resource manager.
*/
/* for a shared target, please change the time directory to something
* unique.
*/
#define TIME_DIR "/dev/time"
//#define TIME_DIR "/david/time"
// device names table
char *devnames [NUMDEVICES] =
{
TIME_DIR "/now", // offset HNow
TIME_DIR "/hour", // offset HHour
TIME_DIR "/min" // offset HMin
};
// formats for each device
char formats [NUMDEVICES][MAX_FORMAT_SIZE + 1] =
{
"%Y %m %d %H:%M:%S\n",
"%H\n",
"%M\n"
};
// pathname ID table
int pathnameID [NUMDEVICES];
// device information table
Timeattr_t timeattrs [NUMDEVICES];
/*
* some forward declarations
*/
void options (int argc, char **argv);
// I/O functions
int io_read (resmgr_context_t *ctp, io_read_t *msg, Timeocb_t *tocb);
int io_write (resmgr_context_t *ctp, io_write_t *msg, Timeocb_t *tocb);
Timeocb_t *time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr);
void time_ocb_free (Timeocb_t *tocb);
// miscellaneous support functions
char *format_time (char *format, int time_offset);
/*
* our connect and I/O functions
*/
resmgr_connect_funcs_t connect_funcs;
resmgr_io_funcs_t io_funcs;
/*
* our ocb allocating and freeing functions
*/
iofunc_funcs_t time_ocb_funcs = {
_IOFUNC_NFUNCS,
time_ocb_calloc,
time_ocb_free
};
/*
* the mount structure, we only have one so we statically declare it
*/
iofunc_mount_t time_mount = { 0, 0, 0, 0, &time_ocb_funcs };
/*
* our dispatch, resource manager and iofunc variables
*/
dispatch_t *dpp;
resmgr_attr_t rattr;
dispatch_context_t *ctp;
/*
* some miscellaneous variables
*/
char *progname = "time"; // for diagnostic messages
int optv; // -v for verbose operation
int main (int argc, char **argv)
{
int i;
printf ("%s: starting...\n", progname);
options (argc, argv);
dpp = dispatch_create ();
memset (&rattr, 0, sizeof (rattr));
/*
* initialize the connect functions and I/O functions tables to
* their defaults and then override the defaults with the
* functions that we are providing.
*/
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
io_funcs.read = io_read;
io_funcs.write = io_write;
/*
* call resmgr_attach to register our 3 prefixes with the
* process manager, and also to let it know about our connect
* and I/O functions.
*
* On error, returns -1 and errno is set.
*/
for (i = 0; i < NUMDEVICES; i++) {
if (optv) {
printf ("%s: attaching pathname %s\n", progname, devnames [i]);
}
/*
* for this sample program we are using the same mount structure
* for all devices, we are using it solely for providing the
* addresses of our ocb calloc and free functions.
*/
iofunc_attr_init (&timeattrs [i].attr, S_IFCHR | 0666, NULL, NULL);
timeattrs [i].attr.mount = &time_mount;
timeattrs [i].format = formats[i];
pathnameID [i] = resmgr_attach (dpp, &rattr, devnames [i],
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &timeattrs [i]);
if (pathnameID [i] == -1) {
fprintf (stderr, "%s: couldn't attach pathname %s, errno %d\n",
progname, devnames [i], errno);
exit (1);
}
}
if (optv) {
printf ("%s: entering dispatch loop\n",
progname);
}
ctp = dispatch_context_alloc (dpp);
while (1) {
if ((ctp = dispatch_block (ctp)) == NULL) {
fprintf (stderr, "%s: dispatch_block failed: %s\n",
progname, strerror (errno));
exit (1);
}
dispatch_handler (ctp);
}
}
/*
* time_ocb_calloc
*
* The purpose of this is to give us a place to allocate our own ocb.
* It is called as a result of the open being done
* (e.g. iofunc_open_default causes it to be called). We register
* it through the mount structure.
*/
Timeocb_t *
time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr)
{
Timeocb_t *tocb;
if (optv) {
printf ("%s: in time_ocb_calloc \n", progname);
}
if ((tocb = calloc (1, sizeof (Timeocb_t))) == NULL) {
if (optv) {
printf ("%s: couldn't allocate %d bytes\n",
progname, sizeof (Timeocb_t));
}
return (NULL);
}
// do anything else to our part of the ocb we wish
tocb -> buffer = NULL;
return (tocb);
}
/*
* time_ocb_free
*
* The purpose of this is to give us a place to free our ocb.
* It is called as a result of the close being done
* (e.g. iofunc_close_ocb_default causes it to be called). We register
* it through the mount structure.
*/
void
time_ocb_free (Timeocb_t *tocb)
{
if (optv) {
printf ("%s: in time_ocb_free \n", progname );
}
/*
* if we actually allocated a buffer (ie: if the
* client called "read", as opposed to just open/close),
* return the memory associated with the buffer.
*/
if (tocb -> buffer) {
free (tocb -> buffer);
}
free (tocb);
}
/*
* io_read
*
* At this point, the client has called their library "read"
* function, and expects zero or more bytes.
* We are getting the format which we are handling for
* this request via the "tocb -> ocb . attr -> format" parameter.
*/
int
io_read (resmgr_context_t *ctp, io_read_t *msg, Timeocb_t *tocb)
{
int nleft;
int onbytes;
int status;
if (optv) {
printf ("%s: in io_read, offset is %lld, nbytes %d\n",
progname, tocb -> ocb . offset, msg -> i.nbytes);
}
if ((status = iofunc_read_verify(ctp, msg, &tocb->ocb, NULL)) != EOK)
return (status);
// No special xtypes
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return(ENOSYS);
}
/*
* the first time in, get the time and set up
* the size
*/
if (tocb -> buffer == NULL) {
/* format the output based on the format string for the device currently being used */
tocb -> buffer = format_time (tocb -> ocb.attr -> format, 0);
tocb -> bufsize = strlen (tocb -> buffer) + 1;
}
/*
* 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 = tocb -> bufsize - tocb -> ocb . offset;
onbytes = min (msg -> i.nbytes, nleft);
/*
* do the MsgReply here. Why are we replying instead of having
* the resmgr API do it? To show that you can.
*/
if (onbytes) {
MsgReply (ctp -> rcvid, onbytes, tocb -> buffer + tocb -> ocb . offset, onbytes);
} else {
MsgReply (ctp -> rcvid, 0, NULL, 0);
}
/*
* advance the offset to reflect the number
* of bytes returned to the client.
*/
tocb -> ocb . offset += onbytes;
if (msg->i.nbytes > 0)
tocb->ocb.attr->attr.flags |= IOFUNC_ATTR_ATIME;
/*
* return _RESMGR_NOREPLY because we've done the
* MsgReply ourselves...
*/
return (_RESMGR_NOREPLY);
}
/*
* io_write
*
* At this point, the client has called their library "write"
* function. Writing is not defined for /dev/time, so we just
* swallow any bytes that they may have written, just like
* /dev/null. An alternate approach is to return an error at
* open time if we detect that the device has been opened for
* writing.
*/
int
io_write (resmgr_context_t *ctp, io_write_t *msg, Timeocb_t *tocb)
{
int status;
if (optv) {
printf ("%s: in io_write\n", progname);
}
if ((status = iofunc_write_verify(ctp, msg, &tocb->ocb, NULL)) != EOK)
return (status);
// No special xtypes
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return(ENOSYS);
}
_IO_SET_WRITE_NBYTES (ctp, msg -> i.nbytes); // indicate how many we wrote
if (msg->i.nbytes > 0)
tocb->ocb.attr->attr.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;
return (_RESMGR_NPARTS (0));
}
/*
* format_time()
*
* Format a time based on the correct device and time offset
* value.
*
* We assume that the malloc always works.
*/
char *
format_time (char *format, int time_offset)
{
char *ptr;
time_t now;
ptr = malloc (64);
time (&now);
now += time_offset;
strftime (ptr, 64, format, localtime (&now));
return (ptr);
}
/*
* options
*
* This routine handles the command line options.
* For our /dev/time family, we support:
* -v verbose operation
*/
void
options (int argc, char **argv)
{
int opt;
optv = 0;
while ((opt = getopt (argc, argv, "v")) != -1) {
switch (opt) {
case 'v':
optv = 1;
break;
}
}
}