QNX之編寫資源管理器(四)

QNX相關(guān)歷史文章:

Handling Read and Write Messages

這篇文章主要描述讀寫消息的處理馒索。

1. Handling the _IO_READ message

io_read處理函數(shù)負(fù)責(zé)處理_IO_READ消息命雀,將收到的數(shù)據(jù)返回給客戶端借杰,比如當(dāng)客戶端調(diào)用read()/readdir()/fread()/fgetc()等接口時(shí)。
消息的定義如下:

struct _io_read {
    uint16_t            type;
    uint16_t            combine_len;
    int32_t             nbytes;
    uint32_t            xtype;
};

typedef union {
    struct _io_read     i;
    /* unsigned char    data[nbytes];    */
    /* nbytes is returned with MsgReply  */
} io_read_t;
  • combine_len退腥,用于消息組合;
  • nbytes再榄,用于客戶端期望讀取的字節(jié)數(shù)狡刘;
  • xtype,這個(gè)字段下文會(huì)介紹到困鸥,主要用于擴(kuò)展嗅蔬;

下邊是一個(gè)完整的消息讀取示例代碼:

#include <errno.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.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;

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);
    }
}

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));
}

上述代碼中,ocb結(jié)構(gòu)存放了緩存的偏移位置疾就,此外指向的屬性結(jié)構(gòu)體attr中澜术,存放了緩存的實(shí)際大小。而attr中緩沖的大小是通過iofunc_attr_init()接口設(shè)置進(jìn)去的猬腰。
此外鸟废,在代碼中需要注意用到了ctp->iov,使用SETIOV()宏姑荷,將ctp->iov指向了需要回復(fù)的數(shù)據(jù)盒延。
當(dāng)沒有讀取到數(shù)據(jù)時(shí)缩擂,返回(_RESMGR_NPARTS(0)),當(dāng)讀取到數(shù)據(jù)后兰英,怎么來告知客戶端讀取了多少呢撇叁?代碼中_IO_SET_READ_NBYTES()宏將讀取的信息保存到了ctp中,當(dāng)返回到庫的時(shí)候畦贸,會(huì)將讀取的字節(jié)數(shù)當(dāng)成MsgReplyv()函數(shù)的參數(shù)陨闹,并通知內(nèi)核MsgSend()該發(fā)送什么,最終上層便能獲取到字節(jié)數(shù)薄坏。

2. Handling the _IO_WRITE message

io_write處理函數(shù)負(fù)責(zé)處理_IO_WRITE消息趋厉,將數(shù)據(jù)寫入到設(shè)備。當(dāng)客戶端調(diào)用write()/fflush()等操作時(shí)執(zhí)行胶坠。
消息定義如下:

struct _io_write {
    uint16_t            type;
    uint16_t            combine_len;
    int32_t             nbytes;
    uint32_t            xtype;
    /* unsigned char    data[nbytes]; */
};

typedef union {
    struct _io_write    i;
    /*  nbytes is returned with MsgReply  */
} io_write_t;

這個(gè)結(jié)構(gòu)中的字段與io_read_t中一致君账,下邊看一個(gè)寫操作的代碼:

int
io_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb)
{
    int     status;
    char    *buf;

    if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK)
        return (status);

    if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return(ENOSYS);

    /* set up the number of bytes (returned by client's write()) */

    _IO_SET_WRITE_NBYTES (ctp, msg->i.nbytes);

    buf = (char *) malloc(msg->i.nbytes + 1);
    if (buf == NULL)
        return(ENOMEM);

    /*
     *  Reread the data from the sender's message buffer.
     *  We're not assuming that all of the data fit into the
     *  resource manager library's receive buffer.
     */

    resmgr_msgread(ctp, buf, msg->i.nbytes, sizeof(msg->i));
    buf [msg->i.nbytes] = '\0'; /* just in case the text is not NULL terminated */
    printf ("Received %d bytes = '%s'\n", msg -> i.nbytes, buf);
    free(buf);

    if (msg->i.nbytes > 0)
        ocb->attr->flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;

    return (_RESMGR_NPARTS (0));
}

當(dāng)在寫入的時(shí)候,如果提供的buffer大小不夠沈善,而需要寫入的數(shù)據(jù)又很大乡数,此時(shí)就需要使用一個(gè)循環(huán)機(jī)制來多次寫入了。

3. Methods of return and replying

可以使用各種方式從處理程序中返回到資源管理器庫:

  • 返回錯(cuò)誤
    比如申請(qǐng)內(nèi)存不夠時(shí):return (ENOMEM)

  • 返回指向數(shù)據(jù)的IOV數(shù)組
    可以使用IOV數(shù)組來指向多片數(shù)據(jù)闻牡,比如如下代碼:

my_header_t     header;
a_buffer_t      buffers[N];

...

SETIOV(&ctp->iov[0], &header, sizeof(header));
SETIOV(&ctp->iov[1], &buffers[i], sizeof(buffers[i]));
return (_RESMGR_NPARTS(2));
  • 返回一個(gè)包含數(shù)據(jù)的緩存
    比如在響應(yīng)read()請(qǐng)求時(shí)净赴,所有的數(shù)據(jù)都在一個(gè)緩存中,那么可以以下兩種方式:
return (_RESMGR_PTR(ctp, buffer, nbytes));

和:

SETIOV (ctp->iov, buffer, nbytes);
return (_RESMGR_NPARTS(1));
  • 成功返回罩润,但是不攜帶數(shù)據(jù)
    比如:return (EOK)玖翅,不過更常見的方式是:return (_RESMGR_NPARTS(0))

  • 讓資源管理器庫去返回

  • 在服務(wù)器端執(zhí)行回復(fù)
    上邊講到的返回情況割以,都是通過資源管理器庫調(diào)用MsgReply*()/MsgError()來解除客戶端的阻塞狀態(tài)金度,在有些case中,你可能不需要這些庫來回復(fù)严沥,可以使用return (_RESMGR_NOREPLY)

  • 推遲回復(fù)猜极,讓客戶端保持阻塞
    一個(gè)例子是管道資源管理器,當(dāng)一個(gè)客戶端讀取管道時(shí)消玄,而此時(shí)管道中沒有數(shù)據(jù)魔吐,可以有兩種選擇:1)返回錯(cuò)誤,2)保持客戶端阻塞莱找,等有數(shù)據(jù)時(shí)再回復(fù)酬姆。
    另外一個(gè)例子是往一個(gè)設(shè)備寫數(shù)據(jù)時(shí),希望數(shù)據(jù)全部寫入后再回復(fù)奥溺。

  • 返回并告訴庫執(zhí)行默認(rèn)處理
    可以執(zhí)行return (_RESMGR_DEFAULT)辞色。

4. Handling other read/write details

4.1 處理xtype成員

從上文中可知,在io_read_t/io_write_t等消息結(jié)構(gòu)中浮定,有一個(gè)xtype成員相满,這個(gè)成員包含了可用于調(diào)整標(biāo)準(zhǔn)I/O函數(shù)行為的一些擴(kuò)展信息层亿,如下:

  • _IO_XTYPE_NONE,沒有提供擴(kuò)展類型信息立美;
  • _IO_XTYPE_OFFSET匿又,客戶端調(diào)用pread()/pread64()/pwrite()/pwrite64()時(shí),不希望用到OCB中的偏移建蹄,而是提供了一個(gè)一次性的偏移量碌更,該偏移量存放在消息緩沖的開頭,緊跟在struct _io_readstruct _io_write后洞慎,比如:
struct myread_offset {
    struct _io_read        read;
    struct _xtype_offset   offset;
}   
  • _IO_XTYPE_READCOND痛单,如果客戶端調(diào)用readcond(),通常會(huì)對(duì)時(shí)間和緩沖區(qū)的大小增加一些限制劲腿。這個(gè)限制放置在消息緩沖區(qū)的開頭旭绒,緊跟在struct _io_readstruct _io_write后,比如:
struct myreadcond {
    struct _io_read        read;
    struct _xtype_readcond cond;
}   
  • _IO_XFLAG_DIR_EXTRA_HINT焦人,只有在讀取目錄時(shí)才有效挥吵。

當(dāng)你不想使用擴(kuò)展功能時(shí),可以參考以下例子:

int
io_read (resmgr_context_t *ctp, io_read_t *msg,
         RESMGR_OCB_T *ocb)
{
    int    status;

    if ((status = iofunc_read_verify(ctp, msg, ocb, NULL))
         != EOK) {
        return (status);
    }

    /* No special xtypes */
    if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return (ENOSYS);

    ...
}

4.2 處理pread*()pwrite*()

客戶端調(diào)用pread*()函數(shù)時(shí)花椭,處理_IO_READ消息的示例代碼如下:

/* we are defining io_pread_t here to make the code below
   simple */
typedef struct {
    struct _io_read         read;
    struct _xtype_offset    offset;
} io_pread_t;

int
io_read (resmgr_context_t *ctp, io_read_t *msg,
         RESMGR_OCB_T *ocb)
{
    off64_t offset; /* where to read from */
    int     status;

    if ((status = iofunc_read_verify(ctp, msg, ocb, NULL))
         != EOK) {
        return(status);
    }
    
    switch(msg->i.xtype & _IO_XTYPE_MASK) {
    case _IO_XTYPE_NONE:
        offset = ocb->offset;
        break;
    case _IO_XTYPE_OFFSET:
        /*
         *  io_pread_t is defined above.
         *  Client is doing a one-shot read to this offset by
         *  calling one of the pread*() functions
         */
        offset = ((io_pread_t *) msg)->offset.offset;
        break;
    default:
        return(ENOSYS);
    }

    ...
}

客戶端調(diào)用pwrite*()時(shí)忽匈,處理_IO_WRITE消息的示例代碼如下:

/* we are defining io_pwrite_t here to make the code below
   simple */
typedef struct {
    struct _io_write        write;
    struct _xtype_offset    offset;
} io_pwrite_t;

int
io_write (resmgr_context_t *ctp, io_write_t *msg,
          RESMGR_OCB_T *ocb)
{
    off64_t offset; /* where to write */
    int     status;
    size_t  skip;   /* offset into msg to where the data
                       resides */

    if ((status = iofunc_write_verify(ctp, msg, ocb, NULL))
         != EOK) {
        return(status);
    }
    
    switch(msg->i.xtype & _IO_XTYPE_MASK) {
    case _IO_XTYPE_NONE:
        offset = ocb->offset;
        skip = sizeof(io_write_t);
        break;
    case _IO_XTYPE_OFFSET:
        /* 
         *  io_pwrite_t is defined above
         *  client is doing a one-shot write to this offset by
         *  calling one of the pwrite*() functions
         */
        offset = ((io_pwrite_t *) msg)->offset.offset;
        skip = sizeof(io_pwrite_t);
        break;
    default:
        return(ENOSYS);
    }

    ...
    
    /* 
     *  get the data from the sender's message buffer, 
     *  skipping all possible header information
     */
    resmgr_msgreadv(ctp, iovs, niovs, skip);
    
    ...
}

需要注意的是,通常情況下在發(fā)送消息緩沖中个从,數(shù)據(jù)會(huì)緊跟著struct _io_write脉幢,而在上邊這種情況下歪沃,數(shù)據(jù)緊跟在struct _xtype_offset后邊嗦锐,這里邊會(huì)存在一個(gè)偏移。

4.3 處理readcond()

與處理pread()/_IO_XTYPE_OFFSET類似沪曙,如下:

typedef struct {
    struct _io_read        read;
    struct _xtype_readcond cond;
} io_readcond_t

struct _xtype_readcond *cond
...
    CASE _IO_XTYPE_READCOND:
        cond = &((io_readcond_t *)msg)->cond
        break;
}

5. Updating the time for reads and writes

在讀操作的那個(gè)示例代碼中奕污,有如下代碼:

if (msg->i.nbytes > 0)
        ocb->attr->flags |= IOFUNC_ATTR_ATIME;

根據(jù)POSIX標(biāo)準(zhǔn),當(dāng)讀取大于0字節(jié)的數(shù)據(jù)并且成功后液走,需要去更新訪問時(shí)間碳默,但是POSIX標(biāo)準(zhǔn)中并沒有說需要立刻去更新,因此當(dāng)有多次讀的時(shí)候缘眶,可能并不想每次都從內(nèi)核中獲取時(shí)間嘱根,可以在需要更新的時(shí)候再統(tǒng)一更新,這樣做的缺點(diǎn)是時(shí)間記錄的不是每一次讀操作的時(shí)間巷懈。如果想記錄每次讀的時(shí)間该抒,那么可以調(diào)用iofunc_time_update()接口,代碼如下:

if (msg->i.nbytes > 0) {
        ocb->attr->flags |= IOFUNC_ATTR_ATIME;
        iofunc_time_update(ocb->attr);
}

寫操作也是一樣的原理顶燕。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凑保,一起剝皮案震驚了整個(gè)濱河市冈爹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欧引,老刑警劉巖频伤,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芝此,居然都是意外死亡憋肖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門癌蓖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞬哼,“玉大人,你說我怎么就攤上這事租副∽浚” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵用僧,是天一觀的道長结胀。 經(jīng)常有香客問我,道長责循,這世上最難降的妖魔是什么糟港? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮院仿,結(jié)果婚禮上秸抚,老公的妹妹穿的比我還像新娘。我一直安慰自己歹垫,他們只是感情好剥汤,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著排惨,像睡著了一般吭敢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上暮芭,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天鹿驼,我揣著相機(jī)與錄音,去河邊找鬼辕宏。 笑死畜晰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瑞筐。 我是一名探鬼主播凄鼻,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了野宜?” 一聲冷哼從身側(cè)響起扫步,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匈子,沒想到半個(gè)月后河胎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虎敦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年游岳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片其徙。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胚迫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唾那,到底是詐尸還是另有隱情访锻,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布闹获,位于F島的核電站期犬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏避诽。R本人自食惡果不足惜龟虎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沙庐。 院中可真熱鬧鲤妥,春花似錦、人聲如沸拱雏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽古涧。三九已至垂券,卻和暖如春花盐,著一層夾襖步出監(jiān)牢的瞬間羡滑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工算芯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柒昏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓熙揍,卻偏偏與公主長得像职祷,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容