本文重點說明下面內容:
- 什么是標准IO,什么是文件IO?
- 什么是Direct IO? O_SYNC標識有什么意義?
- 各個層面的緩存如何同步?
- 還在page cache中的臟頁可以讀寫嗎?
IO路徑上的各層buff
Application buff
|
clib buff
|
page cache
|
disk cache
標准IO
- 標准IO操作的是流(File對象)
- 標准IO可以設置緩存,這個緩存是用戶態buffer,一般稱為clib buff
api
#include <stdio.h>
//打開流
FILE *fopen(const char *pathname, const char *type);
//關閉流
int fclose(File *fp);
// 刷新流
int fflush(FILE *fp);
// 一次讀寫一個字符
int fgetc(FILE *fp);
int fputc(FILE *fp);
// 一次讀寫一行
char* fgets(char* buf, int n, FILE* fp);
int fputs(const char *str, FILE* fp);
// 二進制讀寫
size_t fread(void *ptr, size_t size, size_t nobj, FILE *fp);
size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp);
// 格式化輸入輸出
int fprintf(FILE *fp, const char* format, ...);
int fscanf(FILE *fp, const char *format, ...);
// 示例
#include <stdio.h>
#include <stdlib.h>
int main(void){
char buf[1024];
while (fgets(buf, 1024, stdin) != NULL)
if (fputs(buf, stdout) == EOF)
printf("output error");
if (ferror(stdin))
printf("input error");
exit(0);
}
說明
- 調用fwrite, fputc, fputs系列函數后,數據被保存到clib buf中,依然處於用戶態,如果此時應用進程crash掉,這些數據將丟失。
- 在調用fflush可將clib buf中的數據寫入內核的page cache中。
- 調用fclose也會將clib buff中的數據刷新到內核,並且把clib buff中的輸入數據丟棄。
從這些標准IO的API可看出,標准IO比文件IO要簡潔很多,沒有各種標識,沒有sync, nonblock等。
上列API具體使用細節可參考《unix環境高級編程》第5章。
文件IO
文件IO是直接操作linux系統調用,大部分的問題都是使用文件IO帶來的。
api
int open(const char *pathname, int oflag);
int close(int filedes);
ssize_t read(int filedes, void* buff, size_t nbytes);
ssize_t write(int filedes, const void* buff, size_t nbytes);
int fsync(int filedes);
int fcntl(int filedes, int cmd);
int ioctl(int filedes, int request);
說明
- O_SYNC標識打開的文件,會在write系統調用時,會等待IO從底層返回;O_SYNC僅對寫有意義。
- O_DIRECT標識打開的文件不經過page cache; O_DIRECT對讀寫都是有意義的。
- O_NONBLOCK標識打開的文件(一般是網絡IO,終端設備IO) ,在不可讀寫時立即返回EAGAIN等錯誤碼。
- O_SYNC, O_DIRECT有區別如下,
size_t wirte_file()
{
if(o_DIRECT)
direct_io();
else
buffered_io();
if( O_SYNC )
wait_data_synced();
}
上述API細節可參考《unix環境高級編程》第3章。
mmap
api
#include <sys/mman.h>
void *mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap在calling process的虛擬地址空間中創建一個映射,主要有以下兩種常用方式:
-
對文件創建一個mapping,讀寫文件可以用讀寫內存替代。
-
匿名映射,傳入的fd為-1
創建mapping后,省掉了數據從在用戶態buff和內核page cache的拷貝
后續整理下linux系統文件IO流程