Linux中缓存IO的概念:
缓存IO机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中 ,IO输入输出的数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。
这种机制的
优点
显而易见:
-
缓存 I/O 使用了操作系统内核缓冲区,在一定程度上分离了应用程序空间和实际的物理
设备 -
因为我们的数据被缓存到了页缓存中,所以缓存 I/O 可以减少读盘的次数,直接从页缓存中获取数据,从而提高性能。
缺点
:
不能直接在应用程序地址空间和磁盘之间进行数据传输 ,数据拷贝操作所带来的 CPU 以及内存开销是非常大。
Linux系统调用中关于文件的操作,需要包含一些头文件:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
这些头文件中包含了打开,关闭,创建,读文件,写文件的函数,还有标志位,以及一些宏变量定义。具体需要包含的头文件可以使用man查找帮助。
1、文件描述符
文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用
open
或
creat
返回的文件描述符标识该文件,将其作为参数传送给
read
或
write
。
2、open函数
open函数存在两个函数原型,
函数原型1:
int open(const char *pathname, int oflag) ;
返回值:若成功为文件描述符,出错返回-1
参数说明:
pathname
即为需要打开的文件名,可以包含文件路径
oflag
参数可用来说明此函数的多个选择项。用下列一个或多个常数进行或运算构成
oflag
参数(这些常数定义在< fcntl.h >头文件中):
-
O_RDONLY
只读打开。 -
O_WRONLY
只写打开。 -
O_RDWR
读、写打开。
上面的这三个参数是打开方式,只指定一个。
还可以搭配下面的参数: -
O_APPEND
追加到文件末端。 -
O_TRUNC
如果此文件存在,而且为只读或只写成功打开,则将其长度截短为 0。 -
O_CREAT
若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数
mode
,用其说明该新文件的存取许可权位。 -
O_EXCL
如果同时指定了
O_ CREAT
,而文件已经存在,则出错。这可测试一个文件是
否存在,如果不存在则创建此文件成为一个原子操作。
例如,使用
open
函数创建并打开一个文件:
int open(pathname, O_RDWR|O_CREAT|O_TRUNC, mode) ;
函数原型2:
int open(const char *path, int oflags,mode_t mode);
当我们使用open打开并且创建一个不存在的文件时才会使用原型2,mode参数是为创建的文件添加的权限,可以取值如下:
其实也可以使用数字组合作为
mode
的值,使用
0777
即为赋予全部权限。
3、creat函数
创建一个新文件
函数原型:
int creat(const char *pathname, mode_t mode) ;
参数说明:
mode
文件的权限,具体可以参考
open
函数。
返回值:若成功为文件描述符,出错返回-1
4、close函数
关闭一个已经打开的文件
需要包含
#include <unistd.h>
头文件,原型:
int close (int filedes);
返回:若成功为 0,若出错为- 1
filedes
为已打开的文件描述符。
5、lseek函数
lseek
函数可以设置当前文件位移量,定位打开的文件。原型:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence) ;
返回:若成功为新的文件位移,若出错为- 1。
filedes
为文件描述符
offset
为偏移量吗,与
whence
相关
whence
说明:
• 若whence是
SEEK_SET
,则将该文件的位移量设置为
距文件开始处
offset 个字节。
• 若whence是
SEEK_CUR
,则将该文件的位移量设置为
其当前值加offset
, offset可为正或负。
• 若whence是
SEEK_END
,则将该文件的位移量设置为
文件长度加offset
, offset可为正或负。
6、read函数
read函数从打开的文件中读数据。
ssize_t read(int fd,void *buf,size_t len)
参数说明:
buf
:读取文件后存放数据的地址
len:读取的字节大小
返回值:读到的字节数,若已到达文件尾,返回0;出错返回-1。
7、write函数
ssize_t write(int fd,const void *buf,size_t count)
参数说明:
buf
:为需要写入数据
count
:写入的数据的大小,单位字节
返回值
:若成功返回已写的字节数,出错返回-1。
综合代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
char buf[] = "hello file!";
char buf1[] = "set offset!\n";
int main(int argc, char **argv)
{
int i;
int fd;
if(argc<2)
{
printf("no filename to creat!");
}
else
{
for(i=1;i<argc;i++)
{
if((fd=open(argv[i], O_CREAT|O_RDWR, 777))==-1)
{
printf("creat %s failed!\n",argv[i]);
}
else
{
printf("%s creat success!\n",argv[i]);
if(write(fd, buf, sizeof(buf))>=0)
{
printf("write success!\n");
if(lseek(fd, 20, SEEK_SET)>=0)
{
printf("set offset success!\n");
if(write(fd, buf1, sizeof(buf1))>=0)
{
printf("rewrite success!\n");
}
close(fd);
}
}else{
printf("write failed!\n");
}
}
}
}
exit(0);
}
根据main函数传入的参数,先
open
创建并以读写方式打开一个文件,创建成功后写入
"hello file!"
,然后
lseek(fd, 20, SEEK_SET)
设置当前文件偏移量距开始处20,再次写入
set offset success!\n
,结果如下:
可以看到已经生成了hello.c和a.txt了,内容与我们期望的一致,在开头偏移20处写入了
set offset!\n
。
hello.c和a.txt写入的内容完全一致。
示例2,简单的创建文件并且进行读写:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
char *test_string = "import me!\n";
//不能使用字符串常量,否则无法读出数据存入buf
char user_buf1[] = {0};
char user_buf2[]= {0};
int main(int argc, char *argv[])
{
int fd1, fd2;
int count;
int size;
printf("sizeof(test_string) = %d\n", (int)sizeof(test_string));
printf("strlen(test_string) = %d\n", (int)strlen(test_string));
/*/home/boy/Unix/*/
if((fd1 = creat("main.py", 0777)) != -1)
{
count = write(fd1, test_string, 11);//sizeof(test_string)
if(count < 0)
{
printf("write main.py failed!\r\n");
return -1;
}
//close(fd1);
printf("write main.py success!\n");
fd1 = open("main.py", O_RDWR); //需要再次打开文件才能读
if((count = read(fd1, user_buf1, 11)) == -1)
{
printf("read main.py failed!\r\n");
return -1;
}
printf("userbuf1 = %s\n", user_buf1);
close(fd1);
}
if((fd2 = open("haha.c", O_RDWR | O_CREAT, 0777)) > 0)
{
printf("haha.c creat success!\n");
if((count = read(fd2, user_buf2, sizeof("hello file\n")) < 0 ))
{
printf("read haha.c failed!\n");
return -1;
}
printf("user_buf2 = %s\n", user_buf2);
close(fd2);
}
exit(0);
}
首先从文件
creat
创建文件
main.py
,然后向其写入,写入之后需要重新打开,才能进行读操作,获取文件内容进行验证,接着打开已经存在的文件
haha.c
,读取文件内容,进行验证,haha.c内容如下:
执行结果: