天天看點

Unix網絡程式設計 3.9 readline函數

其實看APUE時就想試着寫些簡單的stdio函數了,但是一直沒實踐,看到這裡時發現書上寫得不完整,便敲代碼試了下。

第1個readline速度非常慢原因在于每次讀取字元都執行了系統調用read(),而系統調用意味着核心态和使用者态之間的切換,系統調用數量太多會導緻切換過程非常費時。是以為了快速的進行I/O,往往會定義一個緩沖區,即第2個readline中的char read_buf[MAXLINE];以及記錄讀取數量的int read_cnt;和記錄目前讀取指針的char *read_ptr;這三個靜态變量都是static類型,也就是目前檔案可用,外部檔案無法通路static變量。

static ssize_t my_read(int fd, char* ptr)
{
    if (read_cnt <= 0) {
        again:                                                                                     
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
            if (errno == EINTR)
                goto again;
            return -1;
        } else if (read_cnt == 0)
            return 0;
    }
    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}
           

關鍵在于用my_read來替換read。my_read是從緩沖區中(而不是從檔案中)逐個讀取資料。

由于一開始緩沖中什麼都沒有,對應的狀态即read_cnt=0(讀取數量為0),read_ptr指向read_buf首部。是以最開始需要填滿緩沖區,讀取MAXLINE(行緩沖區大小),将檔案的資料讀到緩沖區中。然後read_cnt初始化為read函數實際讀取的數量。

比如我這裡MAXLINE是4096,但是若檔案實際總大小沒有這麼多,傳回的read_cnt就小于MAXLINE。

然後read_cnt大于0就意味着緩沖區有未讀的資料,于是每次調用my_read隻需要從read_buf中讀取1個字元,然後移動指針read_ptr到下個字元,并減少read_cnt。

由于read_cnt記錄的是read函數實際讀取的數量,是以歸0時代表緩沖區元素已經讀完了,需要再次調用read函數讀取最多MAXLINE個字元。

這裡采用了goto,原因就像書上所說的,在位元組流套接字上調用read/write輸入或輸出的位元組數可能比請求的數量少,此時并不一定是出錯,而是因為核心中用于套接字的緩沖區已經到達了極限,需要再次(反複)調用read/write函數來輸入或輸出剩下的位元組。

而errno被設定為EINTR的原因如下

EINTR  The call was interrupted by a signal before any data was read;

被信号打斷,此時需要反複讀取至成功為止。

原理說完了,測試代碼如下

#define MAXLINE 4096

static int read_cnt = 0;
static char read_buf[MAXLINE];
static char* read_ptr = read_buf;
// ...
// UNPv3 P74-75的readline.c代碼
// ...
int main(void)
{                                                                                                      
    char buf[MAXLINE];
    for (int i = 0; i < 5; i++) {
        ssize_t n = readline(STDIN_FILENO, buf, MAXLINE);
        write(STDOUT_FILENO, buf, n);
    }
    return 0;
}
           

測試代碼如上,從輸入流中讀取了5行并列印出來

Unix網絡程式設計 3.9 readline函數

用strace檢視系統調用如下

Unix網絡程式設計 3.9 readline函數

可以發現隻調用了1次read(不包含進入main前的部分),然後write了5次。

如果把if ((rc = my_read(fd, &c)) == 1) 改成if ((rc = read(fd, &c, 1)) == 1) 再調用strace

Unix網絡程式設計 3.9 readline函數

可見系統調用數量之多

轉載于:https://www.cnblogs.com/Harley-Quinn/p/6562335.html