嵌入式 Linux系统编程(三)——标准IO库
与文件IO函数相类似,标准IO库中提供的是fopen、fclose、fread、fwrite等面向流对象的IO函数,这些函数在实现时本身就要调用linux的文件IO这些系统调用。
一、标准IO库函数的缓冲机制
由于IO设备的访问速度与CPU的速度相差好几个数量级,为了协调IO设备与CPU的速度的不匹配,对于块设备,内核使用了页高速缓存,即数据会先被拷贝到操作系统内核的页缓存区中,然后才会从操作系统内核的缓存区拷贝到应用程序的地址空间。
当应用程序尝试读取某块数据的时候,如果这块数据已经存放在页缓存中,那么这块数据就可以立即返回给应用程序,而不需要经过实际的物理读盘操作。当然,如果数据在应用程序读取之前并未被存放在页缓存中,那么就需要先将数据从磁盘读到页缓存中去。对于写操作来说,应用程序也会将数据先写到页缓存中去,数据是 否被立即写到磁盘上去取决于应用程序所采用的写操作机制:如果用户采用的是同步写机制,那么数据会立即被写回到磁 盘上,应用程序会一直等到数据被写完为止;如果用户采用的是延迟写机制,那么应用程序就完全不需要等到数据全部被 写回到磁盘,数据只要被写到页缓存中去 就可以了。在延迟写机制的情况下,操作系统会定期地将放在页缓存中的数据刷到磁盘上。与异步写机制不同的是,延迟写机制在数据完全写到磁盘上得时候不会通 知应用程序,而异步写机制在数据完全写到磁盘上得时候是会返回给应用程序的。所以延迟写机制本省是存在数据丢失的风险的,而异步写机制则不会有这方面的担心。定义多大的缓冲大小才能使IO性能达到最大呢?
从上表可以知道当缓冲达到4096大小的时候,继续增加缓冲大小对IO性能影响不大。这个4096大小由文件系统的块大小决定,由于上面的测试所用的文件系统是Linux ext2,块大小是4096。
标准IO库则很好的解决了设置缓冲大小的问题,标准IO会选择最佳的缓存大小,使得我们不用再关心设置缓存大小的问题,事实上标准IO库会对每个IO流自动进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。
标准IO提供三种类型的缓冲机制:
全缓冲:
填满标准IO缓冲区后才进行实际的IO操作。对于在磁盘上的文件通常由标准IO库实施全缓冲的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc获得需使用的缓存区。
行缓冲:
当在输入和输出中遇到换行符时,标准IO库执行IO操作。通常涉及到终端(例如标准输入和标准输出)使用的是行缓冲。
对于行缓冲有两个限制。第一,因为标准IO库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,那么即使还没有写一个换行符,也进行IO操作。第二,任何时候只要通过标准IO库要求从(a)一个不带缓存的流,或者(b)一个行缓存的流(它要求从内核得到数据)得到输入数据,那么就会造成冲洗所有行缓冲输出流(因为后面读取的数据可能就是前面输出的数据)。其实第二种情况我们会经常遇到,当我们先调用printf输出一串不带换行符的字符时,执行完这条printf语句并不会立刻在屏幕中显示我们输出的数据,当我们接下来调用scanf从标准输入读取数据时,我们才看到前面输出的数据。
不带缓冲:
标准IO库不对字符进行存储。例如,如果用标准IO函数fputs写15个字符到不带缓冲的流中,则该函数很可能直接调用write系统调用将这些字符立即写到相关的文件中。标准出错流stderr是不带缓冲的,这样为了让出错的信息可以尽快的显示出来。
二、标准IO库函数
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);//mode为操作权限
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
成功返回一个FILE文件流指针,失败返回NULL,并且设置errno全局变量。
Mode:
r:只读方式打开文件,返回的文件流指针位于文件开始
r+ :读写方式打开文件,返回的文件流指针位于文件开始
w:如果文件存在,清除文件写,如果文件不存在,创建文件写
w+:读写方式打开文件,文件不存在创建文件,存在则清空文件
a:追加写方式打开文件,如果不存在则创建文件,流指针位于文件尾
a+:读和追加写方式打开,如果文件不存在则创建文件,
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
Ptr:读取的数据存储的位置
Size:读取的每个单元的大小
Nmemb:读取的单元数量
Stream:从哪个文件流读取数据
成功返回读取的字节数,到达文件尾返回0,失败返回一个短数。
Fread不能区分到达文件尾和错误,因此必须使用feof和ferror函数确定是到达文件尾,还是发生错误。
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
Ptr:写入的数据的位置
Size:每次写入的单元的大小
Nmemb:写入的单元数
Stream:写入到哪个文件流
成功返回写入的字节数,到达文件尾返回0,失败返回一个短数。
#include <stdio.h>
int fclose(FILE *fp);
成功返回0,刷新缓存,失败返回EOF,并且设置errno。
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
rewind()函数无返回值,fseek成功返回0,ftell成功返回当前偏移量,失败返回-1,并且设置errno。
参考博文:
Linux标准IO缓存(博客园 在于思考)
Linux manual