QNX之編寫(xiě)資源管理器(八)

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

Unblocking Clients and Handling Interrupts

這篇文章主要描述資源管理器對(duì)客戶端的解阻塞屋厘,以及中斷的處理谈火。

1. Handling client unblocking due to signals or timeouts

資源管理器提供的另一個(gè)方便的服務(wù)就是解除阻塞香府。
當(dāng)一個(gè)客戶端發(fā)起請(qǐng)求绅喉,比如調(diào)用read(),會(huì)轉(zhuǎn)變成MsgSend()向資源管理器發(fā)送消息症脂,這是一個(gè)阻塞調(diào)用朴摊。如果客戶端在MsgSend()未完成期間收到一個(gè)信號(hào),資源管理器需要給出一些指示型檀,以便它可以中止請(qǐng)求。

因?yàn)閹?kù)在調(diào)用ChannelCreate()時(shí)設(shè)置了_NTO_CHF_UNBLOCK標(biāo)志听盖,所以當(dāng)客戶端試圖從MsgSend()(并且已經(jīng)通過(guò)MsgReceive()收到消息)中解除阻塞時(shí)胀溺,都會(huì)收到一個(gè)脈沖。之前也講過(guò)當(dāng)客戶端發(fā)送消息給資源管理器時(shí)皆看,可能處在兩種阻塞狀態(tài):

  • SEND-blocked仓坞,此時(shí)資源管理器還沒(méi)收到;
  • REPLY-blocked腰吟,此時(shí)資源管理器還沒(méi)有調(diào)用MsgReply()回復(fù)无埃;
    當(dāng)產(chǎn)生脈沖時(shí),資源管理器庫(kù)會(huì)處理這個(gè)脈沖消息毛雇,并合成一個(gè)_IO_UNBLOCK消息嫉称。

resmgr_io_funcs_tresmgr_connect_funcs_t結(jié)構(gòu)體中,有兩個(gè)unblock message的處理函數(shù):一個(gè)在I/O函數(shù)中灵疮,一個(gè)在連接函數(shù)中织阅。有兩個(gè)處理函數(shù)的原因是因?yàn)榭赡茉谏线呥@兩種情況中中止。

一旦執(zhí)行了_IO_CONNECT消息的處理震捣,I/O函數(shù)中的unblock成員將用于處理unblock pulse荔棉。因此在提供自己的io_open處理程序時(shí)闹炉,先確保在調(diào)用resmgr_open_bind()之前設(shè)置好了OCB中所有相關(guān)字段,否則I/O函數(shù)中的unblock處理程序調(diào)用時(shí)可能會(huì)用到OCB中無(wú)效數(shù)據(jù)润樱。(注意渣触,只有在資源管理器中有多個(gè)線程在運(yùn)行時(shí),才會(huì)出現(xiàn)消息處理期間脈沖中止的問(wèn)題祥国,如果只有一個(gè)線程昵观,那么消息將由庫(kù)的MsgReceive()函數(shù)序列化)

當(dāng)客戶端處于SEND-blocked狀態(tài)時(shí),服務(wù)器不需要知道用戶正在中止請(qǐng)求舌稀,因?yàn)樗€沒(méi)收到消息啊犬。只有收到后并對(duì)該請(qǐng)求執(zhí)行處理的情況下,才需要知道客戶端的中止需求壁查。

如果要覆蓋默認(rèn)的unblock處理函數(shù)iofunc_unblock_default()觉至,應(yīng)該首先調(diào)用這個(gè)默認(rèn)的處理函數(shù),保證能處理通用的情況睡腿。這可以確保在資源管理器列表中的客戶端能被解阻塞语御,需要一些方法來(lái)遍歷阻塞的客戶端rcvid表,找到匹配的然后進(jìn)行阻塞解除席怪。

例程應(yīng)該通過(guò)調(diào)用MsgInfo()檢查_NTO_MI_UNBLOCK_REQ標(biāo)志來(lái)確認(rèn)unblock仍然處于掛起狀態(tài)(避免出現(xiàn)競(jìng)爭(zhēng)條件)应闯。如果找不到匹配的客戶端,可以通過(guò)返回_RESMGR_NOREPLY來(lái)忽略unblock請(qǐng)求挂捻。

/* Check if rcvid is still valid and still has an unblock
   request pending. */
if (MsgInfo(ctp->rcvid, &info) == -1 ||
    !(info.flags & _NTO_MI_UNBLOCK_REQ)) {
    return _RESMGR_NOREPLY;
}

如果沒(méi)有提供unblock處理程序碉纺,可以讓客戶端處在REPLY-blocked狀態(tài),當(dāng)客戶端終止時(shí)刻撒,服務(wù)器必須有機(jī)會(huì)來(lái)清理客戶端數(shù)據(jù)結(jié)構(gòu)骨田。

2. Unblocking if someone closes a file descriptor

假設(shè)有以下情況:

  • 客戶端中一個(gè)線程打開(kāi)文件描述符,并調(diào)用read()函數(shù)声怔;
  • 資源管理器在io_read處理函數(shù)中沒(méi)有回復(fù)态贤,客戶端處于阻塞狀態(tài);
  • 客戶端中的另一個(gè)線程關(guān)閉同一個(gè)文件描述符醋火;

如果資源管理器不處理這種情況悠汽,則第一個(gè)線程會(huì)無(wú)限阻塞在read()操作中。在其他阻塞操作中胎撇,比如write()介粘、devctl()等中也可能出現(xiàn)這種情況。

為了避免這種情況出現(xiàn)晚树,資源管理器需要為阻塞的客戶端維護(hù)一個(gè)列表,當(dāng)有客戶端阻塞時(shí)便添加到該列表中雅采,解除阻塞時(shí)則移除爵憎。

在執(zhí)行close()時(shí)慨亲,會(huì)調(diào)用資源管理器框架提供的io_close_dup()的處理函數(shù),在這個(gè)io_close_dup()函數(shù)中宝鼓,應(yīng)該遍歷列表刑棵,并根據(jù)server connection ID (scoid)connection ID (coid)來(lái)定位哪個(gè)客戶端阻塞在該文件描述符上,并對(duì)它們進(jìn)行回復(fù)以便解除阻塞愚铡。比如:

int io_close_dup (resmgr_context_t *ctp, io_close_t *msg, RESMGR_OCB_T *ocb)
{
    // unblock any clients blocked on the file descriptor being closed
    blocked_client_t *client, *prev;
    prev = NULL;
    client = blocked_clients;
    while ( client != NULL )
    {
        if ( (client->coid == ctp->info.coid) && (client->scoid == ctp->info.scoid) )
        {
            MsgError( client -> rcvid, EBADF );
            if (prev != NULL) // anywhere but the head of the list
            {
                prev->next = client->next;
                free(client);
                client = prev->next;
            }
            else // head of the list case
            {
                blocked_clients = client->next;
                free(client);
                client = blocked_clients;
            }
        }
        else // no match, move to the next entry and track previous entry
        {
            prev = client;
            client = client->next;
        }
    }
    // do rest of the regular close handling
    return iofunc_close_dup_default(ctp, msg, ocb );
}

3. Handling interrupts

管理硬件的資源管理器需要處理硬件的中斷蛉签。當(dāng)中斷處理函數(shù)中發(fā)生了重要事件時(shí),處理程序需要通知資源管理器的線程沥寥,這通常通過(guò)一個(gè)脈沖來(lái)完成碍舍,也可以使用SIGEV_INTR事件通知類(lèi)型來(lái)完成。
當(dāng)資源管理器啟動(dòng)時(shí)邑雅,它將控制權(quán)轉(zhuǎn)交給thread_pool_start()片橡,這個(gè)函數(shù)可能返回,也可能不返回淮野,這個(gè)取決于傳遞給thread_pool_create()的標(biāo)志(如果沒(méi)有傳遞標(biāo)志捧书,函數(shù)在創(chuàng)建線程池之后返回)。這意味著需要在啟動(dòng)線程池之前進(jìn)行設(shè)置骤星。
如果使用SIGEV_INTR事件通知類(lèi)型经瓷,就會(huì)遇到一個(gè)問(wèn)題,那就是連接中斷的線程(通過(guò)InterruptAttach()InterruptAttachEvent())必須與調(diào)用InterruptWait()的線程是同一個(gè)洞难。
下邊是一個(gè)關(guān)于中斷的示例代碼:

#define INTNUM 0
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>

static resmgr_connect_funcs_t   connect_funcs;
static resmgr_io_funcs_t        io_funcs;
static iofunc_attr_t            attr;

void *
interrupt_thread (void * data)
{
    struct sigevent event;
    int             id;

    /* fill in "event" structure */
    memset(&event, 0, sizeof(event));
    event.sigev_notify = SIGEV_INTR;

    /* Obtain I/O privileges */
    ThreadCtl( _NTO_TCTL_IO, 0 );

    /* intNum is the desired interrupt level */
    id = InterruptAttachEvent (INTNUM, &event, 0);

    /*... insert your code here ... */

    while (1) {
        InterruptWait (NULL, NULL);
        /*  do something about the interrupt,
         *  perhaps updating some shared
         *  structures in the resource manager 
         *
         *  unmask the interrupt when done
         */
        InterruptUnmask(INTNUM, id);
    }
}

int
main(int argc, char **argv) {
    thread_pool_attr_t    pool_attr;
    resmgr_attr_t         resmgr_attr;
    dispatch_t            *dpp;
    thread_pool_t         *tpp;
    int                   id;


    if((dpp = dispatch_create()) == NULL) {
        fprintf(stderr,
                "%s: Unable to allocate dispatch handle.\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    memset(&pool_attr, 0, sizeof pool_attr);
    pool_attr.handle = dpp; 
    pool_attr.context_alloc = dispatch_context_alloc; 
    pool_attr.block_func = dispatch_block;  
    pool_attr.unblock_func = dispatch_unblock; 
    pool_attr.handler_func = dispatch_handler; 
    pool_attr.context_free = dispatch_context_free;
    pool_attr.lo_water = 2;
    pool_attr.hi_water = 4;
    pool_attr.increment = 1;
    pool_attr.maximum = 50;

    if((tpp = thread_pool_create(&pool_attr, 
                                 POOL_FLAG_EXIT_SELF)) == NULL) {
        fprintf(stderr, "%s: Unable to initialize thread pool.\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                     _RESMGR_IO_NFUNCS, &io_funcs);
    iofunc_attr_init(&attr, S_IFNAM | 0666, 0, 0);
        
    memset(&resmgr_attr, 0, sizeof resmgr_attr);
    resmgr_attr.nparts_max = 1;
    resmgr_attr.msg_max_size = 2048;

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

    /* Start the thread that will handle interrupt events. */
    pthread_create (NULL, NULL, interrupt_thread, NULL);

    /* Never returns */
    thread_pool_start(tpp);
}

這里的interrupt_thread()函數(shù)使用了InterruptAttachEvent()將中斷源(intNum)綁定到事件上舆吮,然后等待事件的發(fā)生。
這個(gè)方法相對(duì)于使用脈沖有一個(gè)優(yōu)勢(shì)廊营,脈沖作為消息傳遞給資源管理器歪泳,意味著如果資源管理器的消息處理線程正在忙著處理請(qǐng)求,那么這個(gè)脈沖就需要排隊(duì)露筒,直到一個(gè)線程執(zhí)行MsgReceive()呐伞。使用InterruptWait()方法,如果執(zhí)行InterruptWait()的線程具有足夠的優(yōu)先級(jí)慎式,那么它在生成SIGEV_INTR之后立即解除阻塞并運(yùn)行伶氢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘪吏,隨后出現(xiàn)的幾起案子癣防,更是在濱河造成了極大的恐慌,老刑警劉巖掌眠,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕾盯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蓝丙,警方通過(guò)查閱死者的電腦和手機(jī)级遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)望拖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人挫鸽,你說(shuō)我怎么就攤上這事说敏。” “怎么了丢郊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵盔沫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我枫匾,道長(zhǎng)架诞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任婿牍,我火速辦了婚禮侈贷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘等脂。我一直安慰自己俏蛮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布上遥。 她就那樣靜靜地躺著搏屑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粉楚。 梳的紋絲不亂的頭發(fā)上辣恋,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音模软,去河邊找鬼伟骨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛燃异,可吹牛的內(nèi)容都是我干的携狭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼回俐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逛腿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起仅颇,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤单默,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后忘瓦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體搁廓,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枚抵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片线欲。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡明场,死狀恐怖汽摹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苦锨,我是刑警寧澤逼泣,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站舟舒,受9級(jí)特大地震影響拉庶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秃励,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一氏仗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夺鲜,春花似錦皆尔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至食呻,卻和暖如春流炕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仅胞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工每辟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人干旧。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓渠欺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親莱革。 傳聞我的和親對(duì)象是個(gè)殘疾皇子峻堰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • 前言 處理視頻有時(shí)候需要將視頻和音頻分離,得到單獨(dú)的音頻流或視頻流文件盅视。如果想實(shí)現(xiàn)無(wú)損分離音視頻捐名,就要在分離時(shí)不進(jìn)...
    Cherry_rua閱讀 3,184評(píng)論 0 2
  • 文學(xué)不是一味自我炫耀镶蹋、自我表現(xiàn),文學(xué),不是聒噪的囂張贺归。 文學(xué)淆两,或許有一種力量,使青年可以向內(nèi)對(duì)自己做更深的生命質(zhì)問(wèn)...
    拈花一笑sun閱讀 338評(píng)論 0 1
  • Markdown 的設(shè)計(jì)哲學(xué) Markdown 的目標(biāo)是實(shí)現(xiàn) 易讀易寫(xiě).不過(guò)最需要強(qiáng)調(diào)的便是它的可讀性. 一份使用...
    夢(mèng)的預(yù)見(jiàn)閱讀 665評(píng)論 0 2
  • 故事怎么開(kāi)始的拂酣,文小魚(yú)一直都很困惑秋冰,回憶起來(lái),大概就是自己同情心泛濫后惹出來(lái)的婶熬。夏天也不總是悶熱悶熱的剑勾,比如今天,...
    bubbleheadfm閱讀 292評(píng)論 0 0