天天看點

《unix進階環境程式設計》進階 I/O——STREAMS(流)

        STREAMS(流)是系統 V 提供的構造核心裝置驅動程式和網絡協定包的一種通用方法。流在使用者程序和裝置驅動程式之間提供了一條全雙工通路,下面是流在使用者程序和裝置驅動程式之間的流圖:寫到流首的資料将順流而下傳送,由裝置驅動程式讀到的資料則逆流向上傳送;

《unix進階環境程式設計》進階 I/O——STREAMS(流)

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)中,隻涉及三種消息類型,他們是:

  1. M_DATA(I/O 的使用者資料);
  2. M_PROTO(協定控制資訊);
  3. M_PCPROTO(高優先級協定控制資訊);

       流中的消息都有一個排隊優先級:

  1. 高優先級消息(最高優先級);
  2. 優先級波段消息;
  3. 普通消息(最低優先級);

        普通信消息是優先級波段為 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,第三個參數是一個整型值,則其值成為該流新的寫模式,我們可以先擷取目前寫模式值,然後修改它,則進行設定。目前隻定義了兩個寫模式值。

  1. SNDZERO:對管道和 FIFO 的0長度 write 會造成順流傳送一個0長度消息。按系統預設,0長度寫不發送消息。
  2. SNDPIPE:在流上已出錯後,若調用 write 和 putmsg,則向調用程序發送 SIGPIPE 資訊。

讀模式

        讀STREAMS裝置有兩個潛在的問題:

  1. 如果讀到流中消息的記錄邊界将會怎樣?
  2. 如果調用 read,而流中下一個消息由控制資訊又将如何?

        對第一種情況的預設處理模式稱為位元組流模式。read 從流中取資料直至滿足了所要求的位元組數,或者已經不再有資料。在這種模式中,忽略流中消息的邊界。第二種情況的預設處理是,read 出錯傳回。可以改變這兩種預設處理模式。

        調用 ioctl 時,若将 request 設定成 I_GRDOPT,第三個參數又是指向一個整型單元的指針,則對該流的目前讀模式在該整型單元中傳回。如果将 request 設定為 I_SRDOPT,第三個參數是整型值,則将該流的讀模式設定為該值。讀模式可由下列三個常量指定:

  1. RNORM:普通,位元組流模式,如上述這是預設模式。
  2. RMSGN:消息不丢棄模式,read從流中取資料知道讀到所要求的位元組數,或者到達消息邊界。如果某次read隻用了消息的一部分,則其餘部分仍留在流中,以供下一次讀。
  3. RMSGD:消息丢棄模式,這與不丢棄模式的差別是,如果某次隻用了消息的一部分,則餘下部分就被丢棄,不再使用。

         在讀模式中還可指定另外三個變量,以便設定在讀到流中包含協定控制資訊的消息時read的處理方法:任一時刻,智能設定一種消息讀模式和一種協定讀模式,預設讀模式是:(RNORM | RPROTNORM)。

  1. RPROTNORM:協定-普通模式。read 出錯傳回,errno 設定為 EBADMSG。這是預設模式。
  2. RPROTDAT:協定-資料模式。read 将控制部分作為資料傳回給調用者。
  3. 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進階環境程式設計》

繼續閱讀