readline.c
/*
* 抄自《UNIX网络编程:卷1》, 稍作修改。
* 仅仅用于学习目的。学无止境,进步每一天。
*
* slickedit编辑。
*
* 254008829@qq.com
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
ssize_t
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
nleft = n;
ptr = vptr;
while (nleft > 0) {
/*
由于内核缓冲区的原因,一次系统调用read不一定可以读取到n个字节,nread>0的时候返回的是一次系统调用读到的实际
字节数。
*/
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR) {
nread = 0; // call read() again.
} else
return -1;
} else if (nread == 0) { // 我们希望从fd指定的设备中读取n个字节,但是可能实际设备里面不足n,
// 设备中没有可读数据了,就会返回0。这个就是EOF(end of file)。
break;
}
nleft -= nread;
ptr += nread;
}
return n - nleft; // 返回实际读取的字节数。
}
ssize_t
writen(int fd, char *vptr, size_t n)
{
size_t nleft;
ssize_t nwrite;
char *ptr;
nleft = n;
ptr = vptr;
while (nleft > 0) {
/*
内核里面由于缓冲的原因,一次write可能没有写完我们所要求的n个字节。没有关系,返回nwrite>0,
表明实际写了nwrite个字节。下次接着写。
在不出错的情况下,writen返回的一定是n。这个和readn有区别。
*/
if ( (nwrite = write(fd, ptr, nleft)) <= 0) {
if ( (nwrite < 0 && nwrite == EINTR)) {
nwrite = 0; // call write() again.
} else
return -1; // error
}
nleft -= nwrite;
ptr += nwrite;
}
return n;
}
/* 十分低效的readline函数,不带缓冲,每次只read一个字节, 每read一次都要进行一次系统调用。 */
ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n=1; n<maxlen; n++) {
again:
if ( (rc = read(fd, &c, 1)) == 1) {
*ptr++ = c;
if (c == '\n') {
break;
}
} else if (rc == 0) { // eof
return n-1;
} else {
if (errno == EINTR) {
goto again;
}
return -1;
}
}
*ptr = 0;
return n;
}
int main(int argc, char **argv)
{
/* Test code */
exit(0);
}
- read,write函数在实际应用过程中,应用层本身不带缓冲。每次read、write函数的执行会进行一次系统调用, 在第三个参数maxlen传到内核的时候,内核不一定一次可以处理这么大的数据量,所以可能只处理一部分。 根据read、write实际返回的值,来做多次系统调用,确保我们所要求写入或者读取的字节数满足我们的要求。
- readline函数是一个低效的版本,每次read只读一个字节,系统开销十分大。这个版本唯一的好处是可以确保我们所要求读取的行不会漏掉字节,在系统性能要求不高的时候可以用用。最好不要使用,只用了解我们的库函数readline的实现原理就行了。