Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1909538
  • 博文数量: 383
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 4061
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-24 18:53
文章分类

全部博文(383)

文章存档

2011年(1)

2010年(9)

2009年(276)

2008年(97)

我的朋友

分类: LINUX

2009-04-09 21:56:12

关于Unix/Linux环境下的文件操作

对文件进行操作有两种方式:一种是直接使用unix api, 一种是使用标准C库。
我对这两种方法执行效率进行了比较。
实验内容分为3个部分:

1 读数据小路比较
2 打开文件效率比较
3 linux环境进程支持的最大打开文件数

首先介绍一下实验环境和使用的工具:

Unbuntu Dapper Drake 6.06
gcc 4.0.3

使用的工具有:

time, 输出重定向 

time 程序名(命令) 参数 < 重定向文件 

time的输出:
real   程序运行的实际时间
user   程序运行用户态代码使用时间
sys    程序运行系统态代码使用时间

实验数据为一个 50M大小的文件.

1 读文件的比较

unix api方式:

#include 
int read(fd, buf, size);

fd是文件描述符号,在unistd.h中预定义了STDIN_FILENO等标准文件描述符。
buf是char型数组
size是每次读入的缓冲区大小
返回值是实际读到字节数, 
    在接近文件尾的时候,返回值可能小于size的
    在文件尾的时候返回0
     出错返回负数

read必须自己定义缓冲区的大小,如果定义不当,可能导致效率降低。
缓冲区的定义,也不适越大越好。缓冲区的大小最好与磁盘的块大小保持一致。
给出的数据中:块的大小为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    {
       c=buf[i];
    }        
}

root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m3.835s
user    0m0.112s
sys     0m0.079s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m3.436s
user    0m0.126s
sys     0m0.085s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.790s
user    0m0.143s
sys     0m0.031s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.215s
user    0m0.134s
sys     0m0.034s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.182s
user    0m0.143s
sys     0m0.021s

实验结果分析

可以看到开始运行的时候执行时间比较长。
我的分析是,由于我分配了1M的内存。系统调整内存分配需要一段时间。

当重复运行几次后,效果就非常好了。

顺带说一下write操作,原型和read基本一致.
返回值是实际写的字节数。如果与缓冲区的大小不一致,说明写错处。

标准C方式1

实验代码和结果

int c;
while ((c=getc(stdin))!=EOF)
{
        
}

root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m1.433s
user    0m1.057s
sys     0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m1.128s
user    0m1.048s
sys     0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m1.100s
user    0m1.048s
sys     0m0.023s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m1.113s
user    0m1.058s
sys     0m0.018s
root@zhongcun:~/c++/forXunLei# time ./a.out 
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 
real    0m4.938s
user    0m4.831s
sys     0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m4.845s
user    0m4.742s
sys     0m0.029s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m5.224s
user    0m5.118s
sys     0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out 
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        {
            c=buf[i];
        }        
    }

    if (feof(stdin))
    {
        printf("eof");
    }

    if (ferror(stdin))
    {
        printf("error");
    }

root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.171s
user    0m0.133s
sys     0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.177s
user    0m0.143s
sys     0m0.022s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.175s
user    0m0.152s
sys     0m0.015s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.176s
user    0m0.147s
sys     0m0.017s
root@zhongcun:~/c++/forXunLei# g++ unixIO.cpp
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.172s
user    0m0.136s
sys     0m0.028s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.172s
user    0m0.131s
sys     0m0.032s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.172s
user    0m0.146s
sys     0m0.019s
root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m0.173s
user    0m0.138s
sys     0m0.027s
root@zhongcun:~/c++/forXunLei# time ./a.out 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        {
            c=buf[i];
        }        
    }

root@zhongcun:~/c++/forXunLei# time ./a.out 0
real    0m30.612s
user    0m4.503s
sys     0m26.083s
root@zhongcun:~/c++/forXunLei# time ./a.out 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        {
            c=buf[i];
        }        
    }
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m4.029s
user    0m3.990s
sys     0m0.030s
root@zhongcun:~/c++/forXunLei# time ./a.out 
real    0m4.034s
user    0m3.999s
sys     0m0.025s
root@zhongcun:~/c++/forXunLei# time ./a.out 
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之所以慢,格式化的操作是需要时间的。

推荐:
    如果是针对字符串的读写,推荐使用fread。
    因为是标准C类库的文件,可移植性高。
    而且如果使用buffer,效率和系统调用不相上下.

    
    中对提到的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{
    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{

    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{
    files[i][0]=i+1;
    files[i][1]=i+2;
    files[i][2]='\0';
}

for (int i=0;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);
}
 
结果分析:

fd的取值范伟从3~1023
0, 1, 2表示标准输入,输出,错误
1023之后fd输出全部为-1
-1表示,返回一个错误
这个最大可以打开的值应该是平台相关的


杂悟:
1 小错误

prinf无定义    printf
create无定义   creat

2 其他搜索

groff
time分析程序

3

系统总结std io函数的用法
系统总结unix api函数的用法

4 关于std io实现的思考

这取决于编译器堆,类库的实现
是否是直接调用os api呢?
这是最容易的实现

但intel开发的编译器?他调用哪个操作系统的api?

编译器将代码转化为指令
需要对语言标准进行支持,对标准类库进行支持。 

5
man time

6
当系统运行很多大程序(OpenOffice Word)的时候,执行时间显著变慢。

7
标准声明的变量
#include 
stdio是在该文件中定义的文件指针。
EOF

std io对读错误和文件结束的处理
read直接可以通过返回的整数来确认
但文件是否出错或到达末尾需要通过函数feof(), ferror()来检测
阅读(1914) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~