QNX相關(guān)歷史文章:
Combine Messages
這篇文章主要描述消息組合機(jī)制。
1. Where combine messages are used
消息組合機(jī)制,可以節(jié)省網(wǎng)絡(luò)帶寬,并支持原子操作。消息組合由客戶端的C庫(kù)來(lái)組建,由多個(gè)I/O消息和連接消息打包在一起組成。下邊來(lái)看看它的使用。
1.1 Atomic operations
考慮這么一種情況,當(dāng)兩個(gè)線程執(zhí)行以下代碼,讀取同一個(gè)文件描述符:
a_thread ()
{
char buf [BUFSIZ];
lseek (fd, position, SEEK_SET);
read (fd, buf, BUFSIZ);
…
}
當(dāng)?shù)谝粋€(gè)線程執(zhí)行lseek()時(shí),被第二個(gè)線程搶占,當(dāng)?shù)谝粋€(gè)線程恢復(fù)執(zhí)行時(shí),文件的偏移值已經(jīng)被修改了,有三種方法可以解決這個(gè)問(wèn)題:
- 使用
mutex互斥訪問(wèn)同一個(gè)文件描述符。使用mutex的問(wèn)題在于,每次讀寫操作等都需要加鎖,如果線程沒(méi)有按照約定執(zhí)行的話,還是可能會(huì)存在一樣的問(wèn)題; - 每個(gè)線程分別打開這個(gè)文件,維護(hù)兩個(gè)文件描述符。這是一種通用的好的解決方法;
- 這兩個(gè)線程可以使用
readblock()函數(shù),這個(gè)函數(shù)會(huì)原子的執(zhí)行lseek()和read()。通過(guò)將_IO_LSEEK和_IO_READ組合成一個(gè)消息,可以執(zhí)行原子的操作;
1.2 Bandwidth considerations
另外一個(gè)有用的地方就是stat()函數(shù),這個(gè)函數(shù)相當(dāng)于順序調(diào)用open()、fstat()、close()三個(gè)函數(shù),可以將這三個(gè)函數(shù)對(duì)應(yīng)的消息組合成一個(gè)。這樣的操作可以提高效率,尤其在網(wǎng)絡(luò)連接中,此外也簡(jiǎn)化了資源管理器,因?yàn)榭梢圆槐厥褂眠B接函數(shù)來(lái)處理stat()。
2. The library's combine-message handling
資源管理器在處理消息組合的時(shí)候,會(huì)將消息傳遞給不同的處理函數(shù),比如當(dāng)_IO_LSEEK和_IO_READ組合時(shí),資源管理器庫(kù)會(huì)調(diào)用io_lseek()和io_read()來(lái)處理。
當(dāng)有多個(gè)線程發(fā)送組合的消息時(shí),那么就會(huì)出現(xiàn)同步的問(wèn)題,解決方法就是資源管理器庫(kù)提供了鎖住OCB的機(jī)制來(lái)互斥訪問(wèn),比如處理readblock()消息組合時(shí),可以按以下方式處理:
- lock_ocb handler
- _IO_LSEEK message handler
- _IO_READ message handler
- unlock_ocb handler
看看幾個(gè)與消息組合相關(guān)的問(wèn)題:
2.1 Component responses
消息組合就是將常規(guī)的資源管理器消息組合成一個(gè)消息,資源管理器在收到組合的消息后再進(jìn)行拆解并調(diào)用對(duì)應(yīng)的處理函數(shù)處理。通常這樣不會(huì)帶來(lái)額外的處理,但有一個(gè)case是例外:readblock()。
通常,在處理完_IO_LSEEK消息后,處理程序?qū)⒎祷匚募漠?dāng)前位置,但是下一條消息_IO_READ也返回?cái)?shù)據(jù),按照約定,只有組合消息中的最后一個(gè)消息才返回?cái)?shù)據(jù),中間消息只允許返回通過(guò)/失敗的指示。因此在lseek()中需要去判斷是否在組合消息中,代碼如下:
int
iofunc_lseek_default (resmgr_context_t *ctp,
io_lseek_t *msg,
iofunc_ocb_t *ocb)
{
/*
* performs the lseek processing here
* may "early-out" on error conditions
*/
. . .
/* decision re: combine messages done here */
if (msg -> i.combine_len & _IO_COMBINE_FLAG) {
return (EOK);
}
msg -> o = offset;
return (_RESMGR_PTR (ctp, &msg -> o, sizeof (msg -> o)));
}
如果在處理函數(shù)中返回的不是EOK,則組合消息的處理會(huì)被中止,并將狀態(tài)發(fā)送給客戶端。
2.2 Component data access
第二個(gè)與消息組合相關(guān)的問(wèn)題是如何去訪問(wèn)后續(xù)消息組件的數(shù)據(jù)區(qū)域。比如writeblock()需要處理的消息就包括:_IO_SEEK, _IO_WRITE, data三部分,其中數(shù)據(jù)也是以消息的形式來(lái)傳送。資源管理器提供了resmgr_msgread()函數(shù),用于獲取與消息組件對(duì)應(yīng)的數(shù)據(jù),因此在io_write()處理函數(shù)中,應(yīng)該使用resmgr_msgread()替換MsgRead()。實(shí)際上,資源管理器應(yīng)該始終都使用resmgr_msgread()函數(shù),它的實(shí)現(xiàn)如下:
int resmgr_msgread( resmgr_context_t *ctp,
void *msg,
int nbytes,
int offset)
{
return MsgRead(ctp->rcvid, msg, nbytes, ctp->offset + offset);
}
對(duì)應(yīng)的還提供了一個(gè)`resmgr_msgwrite()·函數(shù)。
2.3 Locking and unlocking the attribute structure
對(duì)屬性結(jié)構(gòu)需要互斥訪問(wèn),屬性結(jié)構(gòu)存放在OCB中,提供了加鎖的機(jī)制:
- lock_ocb
- unlock_ocb
比如在訪問(wèn)屬性結(jié)構(gòu)的時(shí)候,可以使用iofunc_attr_lock()和iofunc_attr_unlock()。
2.4 Connect message types
可以看看一般的case:io_open處理程序,并不總是對(duì)應(yīng)于客戶端的open()調(diào)用,比如客戶端可能調(diào)用stat()和access()。
_IO_CONNECT_COMBINE_CLOSE
當(dāng)調(diào)用stat()函數(shù)時(shí),相當(dāng)于順序執(zhí)行open()/fstat()/close(),進(jìn)行消息組合后可以提高效率,包括消息:
_IO_CONNECT_COMBINE_CLOSE, _IO_STAT,對(duì)應(yīng)執(zhí)行的操作:io_open,io_lock_ocb,io_stat,io_unlock_ocb,io_close。_IO_CONNECT_COMBINE_CLOSE消息會(huì)調(diào)用io_open處理函數(shù),同時(shí)也會(huì)隱式調(diào)用io_close_ocb處理函數(shù)。_IO_CONNECT_COMBINE
當(dāng)客戶端調(diào)用access()接口時(shí),C庫(kù)底層會(huì)去執(zhí)行stat()調(diào)用,并根據(jù)stat()調(diào)用的結(jié)果,可選的執(zhí)行devctl()來(lái)獲取更多信息,最后還需要調(diào)用close()來(lái)關(guān)閉設(shè)備。
包括消息:_IO_CONNECT_COMBINE,_IO_STAT,_IO_DEVCTL(optional),_IO_CLOSE;涉及的操作有:io_open,io_lock_ocb,io_stat,io_unlock_ocb,io_lock_ocb (optional),io_devctl (optional),io_unlock_ocb (optional),io_close。