STREAMS(流)是系統 V 提供的構造核心裝置驅動程式和網絡協定包的一種通用方法。流在使用者程序和裝置驅動程式之間提供了一條全雙工通路,下面是流在使用者程序和裝置驅動程式之間的流圖:寫到流首的資料将順流而下傳送,由裝置驅動程式讀到的資料則逆流向上傳送;
STREAMS 消息
STREAMS 的所有輸入和輸出都是基于消息,流首和使用者程序使用 read、write、ioctl、getmsg、getpmsg、putmsg 和 putpmsg 交換資訊。在流首、各處理子產品和裝置驅動程式之間,消息可順流而下,也可逆流而上。
在使用者程序和流首之間,消息由以下幾部分組成:消息類型、控制資訊和資料;其中控制資訊和資料由以下結構指定:
struct strbuf
{
int maxlen; /* size of buffer */
int len; /* number of bytes currently in buffer */
char *buf; /* pointer to buffer */
};
當使用putmsg 或 putpmsg 發送消息時,len 指定緩沖區中資料的位元組數;當使用 getmsg 或 getpmsg 接收消息時,maxlen 指定緩沖區長度,而 len 則由核心設定為存放緩沖區的資料量;消息長度允許為0,len 為 -1 時說明沒有控制資訊和資料。
在我們所使用的函數(read,write,getmsg,getpmsg,putmsg 和 putpmsg)中,隻涉及三種消息類型,他們是:
- M_DATA(I/O 的使用者資料);
- M_PROTO(協定控制資訊);
- M_PCPROTO(高優先級協定控制資訊);
流中的消息都有一個排隊優先級:
- 高優先級消息(最高優先級);
- 優先級波段消息;
- 普通消息(最低優先級);
普通信消息是優先級波段為 0 的消息,優先級波段可在 1~255 之間,波段愈高,優先級也愈高,在任何時刻流首隻有一個高優先級消息排隊,若在流首讀隊列已有一個高優先級消息,則另外的高優先級消息會被丢棄。
putmsg 和 putpmsg 函數
putmsg 和 putpmsg 函數用于 STREAMS 消息寫至流中,這兩個函數的差別是後者允許對消息指定一個優先級波段。
/* 流 */
/*
* 函數功能:将STREAMS消息寫至流;
* 傳回值:若成功則傳回0,若出錯則傳回-1;
* 函數原型:
*/
#include <stropts.h>
int putmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int flags);
int putpmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int band, int flags);
/*
* 說明:
* 對流使用write函數等價于不帶任何控制資訊、flags為0的putmsg函數;
* 這兩函數可以産生三種不同優先級的消息:普通、優先級波段和高優先級;
*/
STREAMS 的 ioctl 操作
ioctl 函數如下:
/* Perform the I/O control operation specified by REQUEST on FD.
One argument may follow; its presence and type depend on REQUEST.
Return value depends on REQUEST. Usually -1 indicates error. */
int ioctl (int __fd, unsigned long int __request, ...) ;
ioctl 的第二個參數request說明執行哪一個操作。所有request都以I_開始。第三個參數的作用與request有關,有時它是一個整型值,有時它是指向一個整型或一個資料結構的指針。
/*
* 函數功能:判斷描述符是否引入一個流;
* 傳回值:若為STREAMS裝置則傳回1,否則傳回0;
* 函數原型:
*/
#include <stropts.h>
int isastream(int filedes);
/*
* 說明:
* 該函數是通過ioctl函數來進行的,可有如下實作:
*/
#include <stropts.h>
#include <unistd.h>
int isastream(int fd)
{
return(ioctl(fd, I_CANPUT, 0) != -1);
}
寫模式
可以使用兩個 ioctl 指令取得和設定一個流的寫模式,如果将 request 設定為 I_GWPORT,第三個參數設定為指向一個整型變量的指針,則該流的目前寫模式在該整型變量中傳回。如果将 request 設定為 I_SWPORT,第三個參數是一個整型值,則其值成為該流新的寫模式,我們可以先擷取目前寫模式值,然後修改它,則進行設定。目前隻定義了兩個寫模式值。
- SNDZERO:對管道和 FIFO 的0長度 write 會造成順流傳送一個0長度消息。按系統預設,0長度寫不發送消息。
- SNDPIPE:在流上已出錯後,若調用 write 和 putmsg,則向調用程序發送 SIGPIPE 資訊。
讀模式
讀STREAMS裝置有兩個潛在的問題:
- 如果讀到流中消息的記錄邊界将會怎樣?
- 如果調用 read,而流中下一個消息由控制資訊又将如何?
對第一種情況的預設處理模式稱為位元組流模式。read 從流中取資料直至滿足了所要求的位元組數,或者已經不再有資料。在這種模式中,忽略流中消息的邊界。第二種情況的預設處理是,read 出錯傳回。可以改變這兩種預設處理模式。
調用 ioctl 時,若将 request 設定成 I_GRDOPT,第三個參數又是指向一個整型單元的指針,則對該流的目前讀模式在該整型單元中傳回。如果将 request 設定為 I_SRDOPT,第三個參數是整型值,則将該流的讀模式設定為該值。讀模式可由下列三個常量指定:
- RNORM:普通,位元組流模式,如上述這是預設模式。
- RMSGN:消息不丢棄模式,read從流中取資料知道讀到所要求的位元組數,或者到達消息邊界。如果某次read隻用了消息的一部分,則其餘部分仍留在流中,以供下一次讀。
- RMSGD:消息丢棄模式,這與不丢棄模式的差別是,如果某次隻用了消息的一部分,則餘下部分就被丢棄,不再使用。
在讀模式中還可指定另外三個變量,以便設定在讀到流中包含協定控制資訊的消息時read的處理方法:任一時刻,智能設定一種消息讀模式和一種協定讀模式,預設讀模式是:(RNORM | RPROTNORM)。
- RPROTNORM:協定-普通模式。read 出錯傳回,errno 設定為 EBADMSG。這是預設模式。
- RPROTDAT:協定-資料模式。read 将控制部分作為資料傳回給調用者。
- RPROTDIS:協定-丢棄模式。read 丢棄消息中的控制資訊。但是傳回消息中的資料。
getmsg 和 getpmsg 函數
/*
* 函數功能:将從流讀STREAMS消息;
* 傳回值:若成功則傳回非負值,若出錯則傳回-1;
* 函數原型:
*/
#include <stropts.h>
int getmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int *flagptr);
int getpmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int *bandptr, int *flagptr);
/*
* 說明:
* 如果flagptr指向的整型單元的值是0,則getmsg傳回流首讀隊列中的下一個消息;
* 如果下一個消息是最高優先級消息,則在傳回時,flagptr所指向的整型單元設定為RS_HIPRI;
* 如果隻希望接收高優先級消息,則在調用getmsg之前必須将flagptr所指向的整型單元設定為RS_HIPRI;
* getmsg可以設定待接收消息的優先級波段;
*/
參考資料:
《UNIX進階環境程式設計》