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
可见系统调用数量之多
Unix网络编程 3.9 readline函数的更多相关文章
- UNIX网络编程——getsockname和getpeername函数
UNIX网络编程--getsockname和getpeername函数 来源:网络转载 http://www.educity.cn/linux/1241293.html 这两个函数或者 ...
- UNIX网络编程——UDP 的connect函数(改进版)
上一篇我们提到,除非套接字已连接,否则异步错误是不会返回到UDP套接字的.我们确实可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相径庭:没有三次握手.内核只是检查是否存在立即可 ...
- UNIX网络编程——send与recv函数详解
#include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_ ...
- UNIX网络编程学习指南--epoll函数
epoll是select/poll的强化版,都是多路复用的函数,epoll有了很大的改进. epoll的功能 1.支持监听大数目的socket描述符 一个进程内,select能打开的fd是有限制的,有 ...
- UNIX网络编程——shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...
- Unix 网络编程 dup和dup2函数
dup和dup2也是两个很实用的调用,它们的作用都是用来复制一个文件的描写叙述符. 它们经经常使用来重定向进程的stdin.stdout和stderr.这两个函数的原形例如以下: #include & ...
- unix网络编程str_cli使用epoll实现
unix网络编程str_cli使用epoll实现 unix环境高级编程中也有这个函数,都是为了讲解IO多路转接.从本质上来看epoll就是一个改善了的select和poll,本质没发生任何变化,对于构 ...
- UNIX网络编程——select函数的并发限制和 poll 函数应用举例
一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...
- 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数
本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...
随机推荐
- 2.spring cloud eureka client配置
红色加粗内容表示修改部分 1.把server项目打成jar包并启动 在项目根目录cmd执行 mvn clean package -Dmaven.test.skip=true mavne仓库地址建议 ...
- laravel中设置表单的方式,以及获取表单的提交的数据
- firefox与ie 的javascript区别
1. Document.form.item 问题 现有问题: 现有代码中存在许多 document.formName.item("itemName") 这样的语句,不能在 ...
- 【Wannafly挑战赛9-A】找一找
链接:https://www.nowcoder.net/acm/contest/71/A 题目描述 给定n个正整数,请找出其中有多少个数x满足:在这n个数中存在数y=kx,其中k为大于1的整数 输入描 ...
- JS中关于把函数作为另一函数的参数的几点小总结
//JS中关于把函数作为函数的参数来传递的问题的小总结//第一,最简单的形式无参函数,直接形式函数的函数名放到括号中,再在执行部分这个函数即可.//当然调用时要穿另一个真正的定义好的函数/*funct ...
- PHP错误Parse error: syntax error, unexpected end of file in test.php on line 12解决方法
出现这个错误的原因就是语法错误,肯定是PHP程序的书写不规范造成,PHP语句标识符错了,没有在php.ini中开启短标签!八成是这个原因,啊啊啊! 今天在写PHP程序的时候总是出现这样的错误:Pars ...
- mssqlserver SQL注释快捷键
注释快捷键 选中语句(快捷键:光标定位到需要注释块的最顶行,按住shift+home选中行,放开再按下shift+向下键,选中块) 按住Ctrl然后依次按K,C取消注释快捷键 选中语句 按住Ctrl然 ...
- Java对多线程的支持
Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行. 在Java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程 ...
- Shell 从日志文件中选择时间段内的日志输出到另一个文件
Shell 从日志文件中选择时间段内的日志输出到另一个文件 情况是这样的,某系统的日志全部写在一个日志文件内,所以这个文件非常大,非常长,每次查阅的时候非常的不方便.所以,相关人员希望能够查询某个时间 ...
- flowable FormEngine和FormEngineConfiguration
FormEngineConfiguration 继承自 AbstractEngineConfiguration. 一.获得实例 FormEngineConfiguration提供了7个公开的静态方法: ...