其实看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

可见系统调用数量之多

Unix网络编程 3.9 readline函数的更多相关文章

  1. UNIX网络编程——getsockname和getpeername函数

    UNIX网络编程--getsockname和getpeername函数   来源:网络转载   http://www.educity.cn/linux/1241293.html     这两个函数或者 ...

  2. UNIX网络编程——UDP 的connect函数(改进版)

    上一篇我们提到,除非套接字已连接,否则异步错误是不会返回到UDP套接字的.我们确实可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相径庭:没有三次握手.内核只是检查是否存在立即可 ...

  3. UNIX网络编程——send与recv函数详解

    #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_ ...

  4. UNIX网络编程学习指南--epoll函数

    epoll是select/poll的强化版,都是多路复用的函数,epoll有了很大的改进. epoll的功能 1.支持监听大数目的socket描述符 一个进程内,select能打开的fd是有限制的,有 ...

  5. UNIX网络编程——shutdown 与 close 函数 的区别

    假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...

  6. Unix 网络编程 dup和dup2函数

    dup和dup2也是两个很实用的调用,它们的作用都是用来复制一个文件的描写叙述符. 它们经经常使用来重定向进程的stdin.stdout和stderr.这两个函数的原形例如以下: #include & ...

  7. unix网络编程str_cli使用epoll实现

    unix网络编程str_cli使用epoll实现 unix环境高级编程中也有这个函数,都是为了讲解IO多路转接.从本质上来看epoll就是一个改善了的select和poll,本质没发生任何变化,对于构 ...

  8. UNIX网络编程——select函数的并发限制和 poll 函数应用举例

    一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置,  ...

  9. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

随机推荐

  1. Markdown - 让网络书写变得简单

    概述 宗旨 Markdown 的目标是实现「易读易写」. 可读性,无论如何,都是最重要的.一份使用 Markdown 格式撰写的文件应该可以直接以纯文本发布,并且看起来不会像是由许多标签或是格式指令所 ...

  2. dropload.js 上滑加载,下拉刷新

    https://github.com/ximan/dropload dropload a javascript implementation of pull to refresh and up to ...

  3. c# out ref 多个返回值问题个人总结

    多个返回值可以用ref或者out来实现 如 var b=string.Empty(); var c=string.Empty(); public bool Test(string a, out str ...

  4. ZOJ 3696 Alien's Organ(泊松定理,期望值)

    Alien's Organ Time Limit: 2 Seconds      Memory Limit: 65536 KB There's an alien whose name is Marja ...

  5. 通过消费者和生产者的多线程程序,了解Java的wait()和notify()用法

    仓库类 public class Store { private int size = 0;//当前容量 private final int MAX = 10;//最大容量 //向仓库中增加货物 pu ...

  6. 从userAgent判断浏览器是什么(chorme ie 火狐)浏览器类型检测、浏览器检测

    一.正确的方法: 通过navigator对象的userAgent属性来判断, 主要是判断userAgent 的信息里是否含有以下字段信息: js代码(非完整版) /************ navig ...

  7. 【移动互联网开发】Zepto 使用中的一些注意点 【转】

    前段时间完成了公司一个产品的 HTML5 触屏版,开发中使用了 Zepto 这个著名的 DOM 操作库. 为什么不是 jQuery 呢?因为 jQuery 的目标是兼容所有主流浏览器,这就意味着它的大 ...

  8. 【javascript基础】 原生JSON.parse解析异常问题

    [问题背景]用jQ,ajax返回json,返回的字符串如:{name:"suntiger035"},status:200,readyState:4,代码无任何问题,结果,succe ...

  9. 如何让VS2013编写的程序在xp下运行

    总体分c++程序和c#程序 1.c++程序 这个用C++编写的程序可以经过设置后在XP下运行,主要的“平台工具集”里修改就可以. 额外说明:(1)程序必须为Dotnet 4.0及以下版本.(XP只支持 ...

  10. zabbix_agent中使用.pgpass

    在配置zabbix过程中,使用了.pgpass 原理: psql -h 192.168.5.XXX -p 5433 -d mydb -U postgres 这个时候要输入密码:user_test 但是 ...