这一章会让你想起一些尘封的记忆
上大学的时候,教C语言的老师会教大家文件IO,那个时候讲的都是标准输入输出,都是C库的实现,和第三章Unbuffered IO要区别开来,目的之前讲过
减少System Call的调用次数,提高Performance
Java上也有类似的实现,只不过Java的实现会更加Common一些,类似BufferedInputStream/BufferedOutputStream,介质则分为很多种,例如FileInputStream
Android Bionic C Lib跟其他C Lib一样样子都是类似 FILE* 里面会封装上管理流所需要的信息: 真正IO操作的file descriptor;缓冲区指针和大小...
对于缓冲一般
stderr是不带缓冲的
如果是终端设备则是行缓冲,否则是全缓冲
然后这里会顺带提到freopen,这个东西会让你想到你启蒙的时光,略表想念,读了一下Bionic 中 freopen的实现贴在下面
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "local.h"
/**//*
* Re-direct an existing, open (probably) file to some other file.
* ANSI is written such that the original file gets closed if at
* all possible, no matter what.
*/
FILE *
freopen(const char *file, const char *mode, FILE *fp)
{
int f;
int flags, isopen, oflags, sverrno, wantfd;
if ((flags = __sflags(mode, &oflags)) == 0) {
//做(r,w,+)到(O_RDONLY,O_WRONLY,O_RDWR,O_TRUNC)的转换.
(void) fclose(fp);
return (NULL);
}
if (!__sdidinit)
__sinit();
FLOCKFILE(fp);
/**//*
* There are actually programs that depend on being able to "freopen"
* descriptors that weren't originally open. Keep this from breaking.
* Remember whether the stream was open to begin with, and which file
* descriptor (if any) was associated with it. If it was attached to
* a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin)
* should work. This is unnecessary if it was not a Unix file.
*/
if (fp->_flags == 0) {
fp->_flags = __SEOF; /**//* hold on to it */
isopen = 0;
wantfd = -1;
} else {
/**//* flush the stream; ANSI doesn't require this. */
if (fp->_flags & __SWR)
(void) __sflush(fp);
/**//* if close is NULL, closing is a no-op, hence pointless */
isopen = fp->_close != NULL;
if ((wantfd = fp->_file) < 0 && isopen) {
(void) (*fp->_close)(fp->_cookie);
isopen = 0;
}
}
//对fp做一些clean的动作
/**//* Get a new descriptor to refer to the new file. */
f = open(file, oflags, DEFFILEMODE);
//DEFFILEMODE默认为RWRWRW
if (f < 0 && isopen) {
/**//* If out of fd's close the old one and try again. */
if (errno == ENFILE || errno == EMFILE) {
(void) (*fp->_close)(fp->_cookie);
isopen = 0;
f = open(file, oflags, DEFFILEMODE);
}
}
sverrno = errno;
/**//*
* Finish closing fp. Even if the open succeeded above, we cannot
* keep fp->_base: it may be the wrong size. This loses the effect
* of any setbuffer calls, but stdio has always done this before.
*/
if (isopen && f != wantfd)
(void) (*fp->_close)(fp->_cookie);
if (fp->_flags & __SMBF)
free((char *)fp->_bf._base);
fp->_w = 0;
fp->_r = 0;
fp->_p = NULL;
fp->_bf._base = NULL;
fp->_bf._size = 0;
fp->_lbfsize = 0;
if (HASUB(fp))
FREEUB(fp);
_UB(fp)._size = 0;
WCIO_FREE(fp);
if (HASLB(fp))
FREELB(fp);
fp->_lb._size = 0;
if (f < 0) { /**//* did not get it after all */
fp->_flags = 0; /**//* set it free */
FUNLOCKFILE(fp);
errno = sverrno; /**//* restore in case _close clobbered */
return (NULL);
}
/**//*
* If reopening something that was open before on a real file, try
* to maintain the descriptor. Various C library routines (perror)
* assume stderr is always fd STDERR_FILENO, even if being freopen'd.
*/
if (wantfd >= 0 && f != wantfd) {
if (dup2(f, wantfd) >= 0) {
(void) close(f);
f = wantfd;
}
}
fp->_flags = flags;
fp->_file = f;
fp->_cookie = fp;
fp->_read = __sread;
fp->_write = __swrite;
fp->_seek = __sseek;
fp->_close = __sclose;
/**//*
* When opening in append mode, even though we use O_APPEND,
* we need to seek to the end so that ftell() gets the right
* answer. If the user then alters the seek pointer, or
* the file extends, this will fail, but there is not much
* we can do about this. (We could set __SAPP and check in
* fseek and ftell.)
*/
if (oflags & O_APPEND)
(void) __sseek((void *)fp, (fpos_t)0, SEEK_END);
FUNLOCKFILE(fp);
return (fp);
}
这里以w or a+之类创建文件的时候没办法指定access mode,C lib就没开这种接口,应该就是默认行为,linux上应该就是unmask后的值,猜想这也与各大平台差异太大,没办法在C lib上做wrap吧.
这个时候做作业吧,++困...沉下心来,做完...
5.1 setvbuf 实现 setbuf
Bionic库也是这样做的 setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFSIZ)
5.2
fgets, fputs
这两个虽热是和行缓冲相关的函数,但要注意的是fgets读数据读到行末or缓冲区满为止,always会给留一个字符给null; fputs也是将缓冲区内容全部输出,并不care是否有换行符。
5.3 printf返回值是0意味着什么也没输出,输出为空。
5.4 getchar()返回的是int不是char...EOF通常会被定义为 -1 如果char是一个U8,那么就会陷入死循环。
5.5 要在标准IO上使用fsync,先fflush流,把buffer从userspace都写到kernel,然后用fileno拿到fd,fsync(fd)就好了
5.6 这里要说的是,一般情况下(除开输入输出设备)行缓冲,fputs中没有换行符后续不fflush,fgets应该是读不到的。