zoukankan      html  css  js  c++  java
  • 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行并打印出来

    用strace查看系统调用如下

    可以发现只调用了1次read(不包含进入main前的部分),然后write了5次。

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

    可见系统调用数量之多

  • 相关阅读:
    前言
    echarts踩坑---容器高度自适应
    vue中刷新页面时去闪烁,提升体验方法
    2018.11.7
    07-sel-express 框架快速搭建案例
    第三方包 vue-resource
    zepto.js-定制zepto步骤
    CSS-单位em 和 rem
    ES6-个人学习大纲
    响应式布局
  • 原文地址:https://www.cnblogs.com/Harley-Quinn/p/6562335.html
Copyright © 2011-2022 走看看