Redis中涉及到多種io,如socket與file,爲了統一對它們的操做,redis設計了一個抽象層,即rio,使用rio能夠實現將數據寫入到不一樣的底層io,可是接口相同。rio的實如今rio.h與rio.c源文件中,支持內存、文件、socket集合三類底層io。redis
struct rio中聲明瞭統一的io操做接口,而且包含一個底層io對象的union結構。使用不一樣的底層io初始化rio實例後,調用rio的抽象接口即會調用對應底層io的實現。以面向對象的思想便是,rio爲抽象類,它擁有三個子類:buffer、file及fdset,這三個子類實現了抽象類聲明的接口。使用者可使用它們的父類rio進行編程,實現多態性。編程
如下是struct rio中抽象接口的聲明(此處省略了一些其它的成員):緩存
struct _rio { /* Backend functions. * Since this functions do not tolerate short writes or reads the return * value is simplified to: zero on error, non zero on complete success. */ size_t (*read)(struct _rio *, void *buf, size_t len); size_t (*write)(struct _rio *, const void *buf, size_t len); off_t (*tell)(struct _rio *); int (*flush)(struct _rio *); /* The update_cksum method if not NULL is used to compute the checksum of * all the data that was read or written so far. The method should be * designed so that can be called with the current checksum, and the buf * and len fields pointing to the new block of data to add to the checksum * computation. */ void (*update_cksum)(struct _rio *, const void *buf, size_t len);
...
/* Backend-specific vars. */
... };
每個底層對象都須要實現它須要支持的接口,實例化rio時,rio結構中的函數指針將指向底io的實現。Redis是C語言實現,所以針對 三個底層io聲明瞭三個對應的初始化函數:app
void rioInitWithFile(rio *r, FILE *fp); void rioInitWithBuffer(rio *r, sds s); void rioInitWithFdset(rio *r, int *fds, int numfds);
這三個函數將初始化rio實例中的函數指針爲它對應的抽象接口實現,並初始化union結構指向正確的底層io對象。socket
注意:rio接口中雖然聲明瞭write操做與read操做,可是redis中僅將它們用於單向操做,即一個rio實例或者使用write操做,或者使用read操做,同一個rio實例不能既讀又寫。函數
以buffer爲底層io的rio實例,write操做將參數buf中的數據copy到一個sds中(redis的字符串實現)。反之,它的read操做將會從一個sds中讀取數據到參數buf指向的地址中。this
抽象接口不支持seek操做,所以寫操做僅能append,而讀操做也只能從當前位置讀數據。buffer對象的結構聲明以下:spa
/* In-memory buffer target. */ struct { sds ptr; off_t pos; } buffer;
這裏的pos記錄了讀寫操做在buffer中的當前位置。設計
以file爲底層io的rio實例,write操做將參數buf中的數據寫入到文件中,而read操做則將file中的數據讀到參數buf指向的內存地址中。file對象的抽象接口實現只須要簡單的調用c語言的庫函數便可。指針
一樣因爲抽象接口未聲明seek操做,它的具體實現也沒有實現seek操做。file對象的結構聲明以下:
/* Stdio file pointer target. */ struct { FILE *fp; off_t buffered; /* Bytes written since last fsync. */ off_t autosync; /* fsync after 'autosync' bytes written. */ } file;
這裏的buffered記錄了寫操做的累計數據量,而autosync爲設置一個同步值,當buffered值超過autosync值後,會執行sync操做使數據同步到磁盤上,sync操做後將buffered值清零。
以fdset爲底層io的rio實例能夠同時將數據向多個目標寫,在redis中主要用做master向它的多個slave發送同步數據,即便用fdset的write操做能夠將一份數據向多個socket發送。對fdset的抽象大大地簡化了redis的master向它的多個slave發送同步數據的 io操做
fdset不支持read操做。此外,它使用了相似buffer的一個sds實例做爲緩存,數據首先被寫入到該緩存中,當緩存中的數據超過必定數量,或者調用了flush操做,再將緩存中的數據發送到全部的socket中。fdset的結構聲明以下:
/* Multiple FDs target (used to write to N sockets). */ struct { int *fds; /* File descriptors. */ int *state; /* Error state of each fd. 0 (if ok) or errno. */ int numfds; off_t pos; sds buf; } fdset;
fds即全部的目標socket的文件描述符集合,state記錄了這些文件描述符的狀態(是否發生寫錯誤),numfds記錄了集合的大小,buf爲緩存,pos表明buf中下一個應該發送的數據的位置。