前言 - 贈送 readn / writen函數
Linux 上默認的 read 和 write 函數會被信號軟中斷. 且 read 和 write 函數中第三個參數 count測試
#include <unistd.h> extern ssize_t read(int fd, void * buf, size_t count); extern ssize_t write(int fd, const void * buf, size_t count);
也會因內部緩衝機制, 不必定保證讀取或寫入到指定 count 大小數據.spa
這裏將 read 和 write 拓展成 readn 和 writencode
// // readn - 力求讀取 n 個字節 // fd : 文件描述符 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // ssize_t readn(int fd, void * buf, size_t n) { size_t div = n; char * ptr = buf; while (div > 0) { ssize_t ret = read(fd, ptr, div); if (ret < 0) { if (errno == EINTR) continue; return -1; } if (ret == 0) break; ptr += ret; div -= ret; } return n - div; } // // writen - 力求寫入 n 個字節 // fd : 文件描述符 // buf : 緩衝區 // n : 讀取長度 // return : 返回寫入長度, -1 標識錯誤, 默認 n // ssize_t writen(int fd, const void * buf, size_t n) { size_t div = n; const char * ptr = buf; while (div > 0) { ssize_t ret = write(fd, ptr, div); if (ret <= 0) { if (errno == EINTR) continue; return -1; } ptr += ret; div -= ret; } return n; }
有了這些收穫, 不妨寫個小測試對象
#include <stdio.h> #include <errno.h> #include <assert.h> #include <unistd.h> // // readn - 力求讀取 n 個字節 // fd : 文件描述符 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // extern ssize_t readn(int fd, void * buf, size_t n); // // writen - 力求寫入 n 個字節 // fd : 文件描述符 // buf : 緩衝區 // n : 讀取長度 // return : 返回寫入長度, -1 標識錯誤, 默認 n // extern ssize_t writen(int fd, const void * buf, size_t n); /* _oo0oo_ o8888888o 88" . "88 (| -_- |) 0\ = /0 ___/`---'\___ .' \\| |// '. / \\||| : |||// \ / _||||| -:- |||||- \ | | \\\ - /// | | | \_| ''\---/'' |_/ | \ .-\__ '-' ___/-. / ___'. .' /--.--\ `. .'___ ."" '< `.___\_<|>_/___.' >' "". | | : `- \`.;`\ _ /`;.`/ - ` : | | \ \ `_. \_ __\ /__ _/ .-` / / =====`-.____`.___ \_____/___.-`___.-'===== `=---=' */ int main(int argc, char * argv[]) { ssize_t ret = writen(STDOUT_FILENO, "12345\n1", 6); printf("ret = %ld\n", ret); char buf[4]; ret = readn(STDIN_FILENO, buf, 3); buf[3] = '\0'; printf("ret = %ld, buf = %s\n", ret, buf); return 0; }
一憂一喜皆心火,一榮一枯皆眼塵,靜心看透炎涼事,千古不作夢裏人。blog
聰明人,一味向前看;智慧人,事事向後看;聰明人,是打敗別人的人;智慧人,是打敗本身的人。事件
修心當以淨心爲要,修道當以無我爲基。get
過去事,過去心,不可記得;如今事,如今心,隨緣便可;將來事,將來心,沒必要勞心。it
正文 - 緩衝讀io
在瞭解 readn 套路基礎上, 你是否有所想過那緩衝讀寫的實現思路呢. 這裏不妨借用深刻理解計算機系統
書中的思路實現一番.
struct rio { int fd; // 文件描述符 char * ptr; // 下一次讀取緩衝池 buf 起點 ssize_t cnt; // 緩衝池 buf 字符數量 char buf[BUFSIZ]; // 緩衝池 }; // rio_init - rio 初始化 inline void rio_init(struct rio * r, int fd) { assert(r && fd >= 0); r->fd = fd; r->cnt = 0; r->ptr = r->buf; } // // readn - 力求讀取 n 個字節 // r : 緩衝讀取對象 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // extern ssize_t rio_readn(struct rio * r, void * buf, size_t n); // // rio_readline - 力求讀取一行數據 // r : 緩衝讀取對象 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // extern ssize_t rio_readline(struct rio * r, void * buf, size_t n);
實現了緩衝讀固定字符和緩衝讀一行. 額外的緩衝寫也是類似的思路, 簡單點寫不了會進入寫緩衝區,
能夠當課外做業自行實現.
// rio_read - 帶緩衝版本的 read static ssize_t rio_read(struct rio * r, void * buf, size_t n) { // 當緩衝區中沒有數據, 咱們從新填充緩衝區 while (r->cnt <= 0) { r->cnt = read(r->fd, r->buf, sizeof r->buf); if (r->cnt < 0) { if (errno == EINTR) continue; return -1; } // EOF 直接返回 if (r->cnt == 0) return 0; // 從新設置 buffer ptr r->ptr = r->buf; } // 嘗試讀取數據並返回 ssize_t cnt = r->cnt < n ? r->cnt : n; memcpy(buf, r->ptr, cnt); r->cnt -= cnt; r->ptr += cnt; return cnt; } // // readn - 力求讀取 n 個字節 // r : 緩衝讀取對象 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // ssize_t rio_readn(struct rio * r, void * buf, size_t n) { size_t div = n; char * ptr = buf; while (div > 0) { ssize_t ret = rio_read(r, ptr, div); if (ret < 0) return -1; if (ret == 0) break; ptr += ret; div -= ret; } return n - div; } // // rio_readline - 力求讀取一行數據, 會吃掉最後一個 \n 字符 // r : 緩衝讀取對象 // buf : 緩衝區 // n : 讀取長度 // return : 返回讀取的長度, -1 標識錯誤, < n 標識關閉, 默認 n // ssize_t rio_readline(struct rio * r, void * buf, size_t n) { size_t i; char * ptr = buf, c; for (i = 1; i < n; ++i) { ssize_t ret = rio_read(r, &c, 1); if (ret < 0) return -1; if (c == '\n' || ret == 0) break; *ptr++ = c; } *ptr = '\0'; return i - 1; }
緩衝寫實戰包裝要複雜一點. 和業務綁定重(或者實現多策略的). 例如緩衝區滿了這時候的策略就由業務
決定, 是緩衝區擴容, 仍是等待下次寫事件觸發. 等等, 真實戰場的緩衝讀寫須要具體場景和機器打配合,
來構造滿意的讀寫策略.
後記 - 讓水倒流