今天遇到一个程序bug,A向B发送完整的一段数据,B却总是不能获取到结尾部分的内容。后来才发现在进行“write”操作(ngx.print)后还需要调用flush操作(ngx.flush)才能将数据写出。对数据IO,缓存的知识又忘光光了,这里整理一下:
对于文件描述符的操作比较简单,不同的操作系统都封装了比较好用的fstream库函数。基于流的操作最终会调用read和write函数进行IO操作,操作系统为了提升程序的运行效率,尽可能减少write和read操作的调用次数,通常会提供缓存。
缓存的类型分全缓存(读入/写出内存字节数等于缓冲区大小或者文件已经到结尾才进行IO操作,写磁盘一般是属于这种类型);行缓存(直到遇见“\n”才会进行IO操作,标准输入stdin和标准输出stdout属于这种类型);无缓存(立即进行IO,如stderr),我们可以在程序中设定流的缓存方式。
#include<stdio.h>
#define SIZE 1024
void check_buf_type(){
if(stdin->_flags & _IO_UNBUFFERED) {
printf("stdin unbuffered");
}
else if(stdin->_flags & _IO_LINE_BUF) {
printf("stdin line-buffered");
}
else{
printf("stdin fully-buffered");
}
printf("\n");
}
int main()
{
check_buf_type();
char buf[SIZE];
if(setvbuf(stdin, buf, _IONBF, SIZE) != 0) {
printf("error!");
return -1;
}
check_buf_type();
if(setvbuf(stdin, buf, _IOLBF, SIZE) != 0) {
printf("error!");
return -1;
}
check_buf_type();
return 0;
}
关于缓存内容,我们在进行文件IO后,不一定保证它已经写入到磁盘或者网络套接口,存在缓存的情况下可能需要调用fflush函数强制写入磁盘文件或者fpurge清空缓存内容。
今天还遇到另外一个问题,Lua文件操作的打开模式,如下面的例子,我原本想先在文件头部写入字符串,然后再在偏移100位置处再写入字符串,可是由于中间关闭了一次,又打开后前面一次写入的内容就被清除了。示例代码如下:
local out = io.open("test","wb")
out:seek("set") -- 将游标设置在文件头
out:write("Hello World")
out:close()
out = io.open("test","wb")
out:seek("set",100)
out:write("Hello World")
out:close()
其实这也是我文件操作方式不对导致的,“w”方式打开会将文件长度置零,改成下面的方式就可以了,“r+”表示读写方式
local out = io.open("test","wb")
out:seek("set") -- 将游标设置在文件头
out:write("Hello World")
out:close()
out = io.open("test","rb+")
out:seek("set",100)
out:write("Hello World")
out:close()
可是需要注意的是“r+”模式不能打开一个不存在的文件,不然out会被设置为nil,所以如果要以读写方式打开,需要事先判断改文件是否存在。