关于Unix/Linux环境下的文件操作
对文件进行操作有两种方式:一种是直接使用unix api, 一种是使用标准C库。
我对这两种方法执行效率进行了比较。
实验内容分为3个部分:
首先介绍一下实验环境和使用的工具:
实验数据为一个 50M大小的文件。
1 读文件的比较
unix api方式:
标准C方式1
标准C语言方式2
标准C语言方式3
Unix API
不使用缓存的unix api
标准C语言函数
不实用缓存的fread
实验结论
推荐:
如果是针对字符串的读写,推荐使用fread。
因为是标准C类库的文件,可移植性高。
而且如果使用buffer,效率和系统调用不相上下.
<advanced programming in unix enviroment>
中对提到的fread的不足:
1 The offset of a member within a structure can differ between compilers and systems, because of different aliagnment requirements.
2 The binary format used to store multibyte integers and floating-point values differ among machine architectures.
2 打开文件操作
3 进程所能打开的最大文件
结果分析:
杂悟:
1 小错误
2 其他搜索
3
4 关于std io实现的思考
5
6
7
对文件进行操作有两种方式:一种是直接使用unix api, 一种是使用标准C库。
我对这两种方法执行效率进行了比较。
实验内容分为3个部分:
1 读数据小路比较
2 打开文件效率比较
3 linux环境进程支持的最大打开文件数
2 打开文件效率比较
3 linux环境进程支持的最大打开文件数
首先介绍一下实验环境和使用的工具:
Unbuntu Dapper Drake 6.06
gcc 4.0.3
使用的工具有:
gcc 4.0.3
time, 输出重定向
time 程序名(命令) 参数 < 重定向文件
time的输出:
real 程序运行的实际时间
user 程序运行用户态代码使用时间
sys 程序运行系统态代码使用时间
time 程序名(命令) 参数 < 重定向文件
time的输出:
real 程序运行的实际时间
user 程序运行用户态代码使用时间
sys 程序运行系统态代码使用时间
实验数据为一个 50M大小的文件。
1 读文件的比较
unix api方式:
#include <unistd.h>
int read(fd, buf, size);
fd是文件描述符号,在unistd.h中预定义了STDIN_FILENO等标准文件描述符。
buf是char型数组
size是每次读入的缓冲区大小
返回值是实际读到字节数,
在接近文件尾的时候,返回值可能小于size的
在文件尾的时候返回0
出错返回负数
read必须自己定义缓冲区的大小,如果定义不当,可能导致效率降低。
缓冲区的定义,也不适越大越好。缓冲区的大小最好与磁盘的块大小保持一致。
在<advanced programming in the UNIX enviroment>给出的数据中:块的大小为4096.
int read(fd, buf, size);
fd是文件描述符号,在unistd.h中预定义了STDIN_FILENO等标准文件描述符。
buf是char型数组
size是每次读入的缓冲区大小
返回值是实际读到字节数,
在接近文件尾的时候,返回值可能小于size的
在文件尾的时候返回0
出错返回负数
read必须自己定义缓冲区的大小,如果定义不当,可能导致效率降低。
缓冲区的定义,也不适越大越好。缓冲区的大小最好与磁盘的块大小保持一致。
在<advanced programming in the UNIX enviroment>给出的数据中:块的大小为4096.
实验代码和结果:
#define BUFFSIZE 4096
#define G_BUFF 1073741824 // 2^30
#define M_BUFF 1048576 // 2^20
#define K_BUFF 1024 // 2^10
int n;
char buf[M_BUFF];
char c;
while ((n=(read(STDIN_FILENO, buf, BUFFSIZE) ) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m3.835s
user 0m0.112s
sys 0m0.079s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m3.436s
user 0m0.126s
sys 0m0.085s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.790s
user 0m0.143s
sys 0m0.031s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.215s
user 0m0.134s
sys 0m0.034s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.182s
user 0m0.143s
sys 0m0.021s
#define G_BUFF 1073741824 // 2^30
#define M_BUFF 1048576 // 2^20
#define K_BUFF 1024 // 2^10
int n;
char buf[M_BUFF];
char c;
while ((n=(read(STDIN_FILENO, buf, BUFFSIZE) ) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m3.835s
user 0m0.112s
sys 0m0.079s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m3.436s
user 0m0.126s
sys 0m0.085s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.790s
user 0m0.143s
sys 0m0.031s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.215s
user 0m0.134s
sys 0m0.034s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.182s
user 0m0.143s
sys 0m0.021s
实验结果分析
可以看到开开始运行的时候执行时间比较长。
我的分析是,由于我分配了1M的内存。系统调整内存分配需要一段时间。
当重复运行几次后,效果就非常好了。
顺带说一下write操作,原型和read基本一致.
返回值是实际写的字节数。如果与缓冲区的大小不一致,说明写错处。
我的分析是,由于我分配了1M的内存。系统调整内存分配需要一段时间。
当重复运行几次后,效果就非常好了。
顺带说一下write操作,原型和read基本一致.
返回值是实际写的字节数。如果与缓冲区的大小不一致,说明写错处。
标准C方式1
实验代码和结果
int c;
while ((c=getc(stdin))!=EOF)
{
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.433s
user 0m1.057s
sys 0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.128s
user 0m1.048s
sys 0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.100s
user 0m1.048s
sys 0m0.023s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.113s
user 0m1.058s
sys 0m0.018s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.162s
user 0m1.045s
sys 0m0.028s
while ((c=getc(stdin))!=EOF)
{
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.433s
user 0m1.057s
sys 0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.128s
user 0m1.048s
sys 0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.100s
user 0m1.048s
sys 0m0.023s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.113s
user 0m1.058s
sys 0m0.018s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m1.162s
user 0m1.045s
sys 0m0.028s
标准C语言方式2
实验代码和结果
char c;
while (scanf("%c",&c)>0)
{
;
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.938s
user 0m4.831s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.845s
user 0m4.742s
sys 0m0.029s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m5.224s
user 0m5.118s
sys 0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m5.300s
user 0m5.188s
sys 0m0.028s
char c;
while (scanf("%c",&c)>0)
{
;
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.938s
user 0m4.831s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.845s
user 0m4.742s
sys 0m0.029s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m5.224s
user 0m5.118s
sys 0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m5.300s
user 0m5.188s
sys 0m0.028s
标准C语言方式3
实验代码和结果
int n;
char buf[BUFFSIZE];
char c;
while ( (n=fread(buf,sizeof(char), BUFFSIZE,stdin) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
if (feof(stdin))
{
printf("eof");
}
if (ferror(stdin))
{
printf("error");
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.171s
user 0m0.133s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.177s
user 0m0.143s
sys 0m0.022s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.175s
user 0m0.152s
sys 0m0.015s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.176s
user 0m0.147s
sys 0m0.017s
root@zhongcun:~/c++/forXunLei# g++ unixIO.cpp
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.136s
sys 0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.131s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.146s
sys 0m0.019s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.173s
user 0m0.138s
sys 0m0.027s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.136s
sys 0m0.029s
int n;
char buf[BUFFSIZE];
char c;
while ( (n=fread(buf,sizeof(char), BUFFSIZE,stdin) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
if (feof(stdin))
{
printf("eof");
}
if (ferror(stdin))
{
printf("error");
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.171s
user 0m0.133s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.177s
user 0m0.143s
sys 0m0.022s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.175s
user 0m0.152s
sys 0m0.015s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.176s
user 0m0.147s
sys 0m0.017s
root@zhongcun:~/c++/forXunLei# g++ unixIO.cpp
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.136s
sys 0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.131s
sys 0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.146s
sys 0m0.019s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.173s
user 0m0.138s
sys 0m0.027s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m0.172s
user 0m0.136s
sys 0m0.029s
Unix API
不使用缓存的unix api
机器声音很大,猜测是读取硬盘的时候发出的声音.
int n;
char buf[M_BUFF];
char c;
while ((n=(read(STDIN_FILENO, buf, 1) ) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m30.612s
user 0m4.503s
sys 0m26.083s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m30.658s
user 0m4.613s
sys 0m26.021s
int n;
char buf[M_BUFF];
char c;
while ((n=(read(STDIN_FILENO, buf, 1) ) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m30.612s
user 0m4.503s
sys 0m26.083s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
0
real 0m30.658s
user 0m4.613s
sys 0m26.021s
标准C语言函数
不实用缓存的fread
int n;
char buf[BUFFSIZE];
char c;
while ( (n=fread(buf,sizeof(char), 1,stdin) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.029s
user 0m3.990s
sys 0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.034s
user 0m3.999s
sys 0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.121s
user 0m4.017s
sys 0m0.032s
char buf[BUFFSIZE];
char c;
while ( (n=fread(buf,sizeof(char), 1,stdin) ) >0)
{
for (int i=0;i<n;i++)
{
c=buf[i];
}
}
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.029s
user 0m3.990s
sys 0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.034s
user 0m3.999s
sys 0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out <infile
real 0m4.121s
user 0m4.017s
sys 0m0.032s
实验结论
需要处理文件中每个字符的时候
单位(s) read getc scanf fread read(no buf) fread(no buf)
user 0.13 1.04 4.9 0.14 4.5 4.0
sys 0.03 0.02 0.02 0.03 26 0.03
可以看到
getc, scanf, fread由于使用了std io自带的buffer, 所以系统的开销始终维持在0.03。
而read如果对buf的选择不当,系统调用时间可能很长。
然后是用户态代码执行时间:
带buff的read, fread是最快的。
但不带buf的执行时间是带buf的40倍。
而且要比getc的执行时间还要慢。
分析原因, 将数据拷贝到buf内需要时间。执行for循环体内的字符赋值语句需要时间。
getc之所以慢,std io从缓冲区取数据是需要时间的。
scanf之所以慢,格式化的操作是需要时间的。
单位(s) read getc scanf fread read(no buf) fread(no buf)
user 0.13 1.04 4.9 0.14 4.5 4.0
sys 0.03 0.02 0.02 0.03 26 0.03
可以看到
getc, scanf, fread由于使用了std io自带的buffer, 所以系统的开销始终维持在0.03。
而read如果对buf的选择不当,系统调用时间可能很长。
然后是用户态代码执行时间:
带buff的read, fread是最快的。
但不带buf的执行时间是带buf的40倍。
而且要比getc的执行时间还要慢。
分析原因, 将数据拷贝到buf内需要时间。执行for循环体内的字符赋值语句需要时间。
getc之所以慢,std io从缓冲区取数据是需要时间的。
scanf之所以慢,格式化的操作是需要时间的。
推荐:
如果是针对字符串的读写,推荐使用fread。
因为是标准C类库的文件,可移植性高。
而且如果使用buffer,效率和系统调用不相上下.
<advanced programming in unix enviroment>
中对提到的fread的不足:
1 The offset of a member within a structure can differ between compilers and systems, because of different aliagnment requirements.
2 The binary format used to store multibyte integers and floating-point values differ among machine architectures.
2 打开文件操作
系统调用和std io的差别不适很大
打开关闭1,000,000次
用户态代码平均时间0.2s
系统态代码平均时间1.7s
for (int i=0;i<M_BUFF;i++)
{
int fd=open ("infile", O_RDONLY, "r");
close(fd);
}
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m1.947s
user 0m0.202s
sys 0m1.689s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.012s
user 0m0.213s
sys 0m1.745s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m1.934s
user 0m0.203s
sys 0m1.670s
for (int i=0;i<M_BUFF;i++)
{
FILE * fp =fopen("infile", "r");
fclose(fp);
}
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.384s
user 0m0.783s
sys 0m1.549s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.409s
user 0m0.783s
sys 0m1.573s
打开关闭1,000,000次
用户态代码平均时间0.2s
系统态代码平均时间1.7s
for (int i=0;i<M_BUFF;i++)
{
int fd=open ("infile", O_RDONLY, "r");
close(fd);
}
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m1.947s
user 0m0.202s
sys 0m1.689s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.012s
user 0m0.213s
sys 0m1.745s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m1.934s
user 0m0.203s
sys 0m1.670s
for (int i=0;i<M_BUFF;i++)
{
FILE * fp =fopen("infile", "r");
fclose(fp);
}
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.384s
user 0m0.783s
sys 0m1.549s
root@zhongcun:~/c++/forXunLei# time ./a.out
real 0m2.409s
user 0m0.783s
sys 0m1.573s
3 进程所能打开的最大文件
char files[K_BUFF*3/2][3];
for (int i=0;i<K_BUFF*3/2;i++)
{
files[i][0]=i+1;
files[i][1]=i+2;
files[i][2]='/0';
}
for (int i=0;i<K_BUFF*3/2;i++)
{
int fd=creat (files[i], O_WRONLY);
printf("opened %s, fd=%d/n",files[i], fd);
}
for (int i=0;i<K_BUFF*3/2;i++)
{
files[i][0]=i+1;
files[i][1]=i+2;
files[i][2]='/0';
}
for (int i=0;i<K_BUFF*3/2;i++)
{
int fd=creat (files[i], O_WRONLY);
printf("opened %s, fd=%d/n",files[i], fd);
}
实验代码2
for (int i=0;i<1025;i++)
{
int fd=open ("infile", O_RDONLY,"r");
printf("opened infile, fd=%d/n", fd);
}
for (int i=0;i<1025;i++)
{
int fd=open ("infile", O_RDONLY,"r");
printf("opened infile, fd=%d/n", fd);
}
结果分析:
fd的取值范伟从3~1023
0, 1, 2表示标准输入,输出,错误
1023之后fd输出全部为-1
-1表示,返回一个错误
这个最大可以打开的值应该是平台相关的
0, 1, 2表示标准输入,输出,错误
1023之后fd输出全部为-1
-1表示,返回一个错误
这个最大可以打开的值应该是平台相关的
杂悟:
1 小错误
prinf无定义 printf
create无定义 creat
create无定义 creat
2 其他搜索
groff
time分析程序
time分析程序
3
系统总结std io函数的用法<stdio.h>
系统总结unix api函数的用法<fcntl.h><unistd.h>
系统总结unix api函数的用法<fcntl.h><unistd.h>
4 关于std io实现的思考
这取决于编译器堆,类库的实现
是否是直接调用os api呢?
这是最容易的实现
但intel开发的编译器?他调用哪个操作系统的api?
编译器将代码转化为指令
需要对语言标准进行支持,对标准类库进行支持。
是否是直接调用os api呢?
这是最容易的实现
但intel开发的编译器?他调用哪个操作系统的api?
编译器将代码转化为指令
需要对语言标准进行支持,对标准类库进行支持。
5
man time
6
当系统运行很多大程序(OpenOffice Word)的时候,执行时间显著变慢。
7
标准声明的变量
#include <stdio.h>
stdio是在该文件中定义的文件指针。
EOF
std io对读错误和文件结束的处理
read直接可以通过返回的整数来确认
但文件是否出错或到达末尾需要通过函数feof(), ferror()来检测
#include <stdio.h>
stdio是在该文件中定义的文件指针。
EOF
std io对读错误和文件结束的处理
read直接可以通过返回的整数来确认
但文件是否出错或到达末尾需要通过函数feof(), ferror()来检测