注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Under the bule sky

Get what I want "cause I ask for it.

 
 
 

日志

 
 

Linux文件I/O及相关函数  

2014-08-14 15:40:40|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
Linux文件I/O及相关函数 - 戴↑Ω听歌 - ∞
 
Linux文件I/O及相关函数 - 戴↑Ω听歌 - ∞
 


一、文件描述符
    
    对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描叙符。当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。
    
    按照惯例,UNIX shell使文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准出错输出相结合。这是unix  shell以及很多应用程序使用的惯例,而与内核无关。尽管如此,如果不遵循这种惯例,那么很多unix应用程序不能工作 。
    
    在POSIX. 1应用程序中,幻树0,1,2应该被代换成符号常数STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。这些常数都定义在头文件<unistd.h>中。

    早期的unix版本采用的上限值是19(允许每个进程打开20个文件),现在很多系统则将其增加至63。


二、open函数

调用open可以打开或者创建一个文件
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
A.open()和creat()调用成功返回文件描述符,失败返回-1,并设置errno。 
B.open()/creat()调用返回的文件描述符一定是最小的未用描述符数字。 
C.creat()等价于open(pathname,O_CREAT | O_WRONLY | O_TRUNC,mode) 
D.open()可以打开设备文件,但是不能创建设备文件,设备文件必须使用mknod()创建。 
从以上我们可以看到open有两个造型,一个是两个参数,一个是三个参数。 
注意:对于open函数而言,仅当创建新文件时才使用第三个参数。 
 
pathname是要打开或创建文件的名字

flags参数可用来说明此函数的多个选择项,用下列一个或多个常数进行组合

A.O_RDONLY  只读打开
B.O_WRONLY  只写打开
C.O_RDWR    读、写打开

D.O_APPEND  每次写时都加到文件的尾端
E.O_CREAT   此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
F.O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
G.O_TRUNC 如果此文件存在,而且只为读或只写成功打开,则将其长度截断为0。
H.O_NOCTTY 如果pathname指的是终端设备,则不将此设备分配作为此进程的控制终端。
I.O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。

fopen的参数mode与open的参数 flags之间的等价关系? 
  r -------->O_RDONLY 
r+------->O_RDWR 
  w-------->O_WRONLY | O_CREAT | O_TRUNC,0666 
w+------>O_RDWR | O_CREAT | O_TRUNC,0666 
  a--------->O_WRONLY | O_CREAT | O_APPEND,0666 
a+-------->O_RDWR | O_CREAT | O_APPEND,0666

注意:当创建一个文件名长度超过NAME_MAX(ubuntu上默认为255)的文件时,Linux总是返回错误。
例:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <fcntl.h>
  4. #include <string.h>
  5. #include <errno.h>

  6. int main(int argc,char *argv[])
  7. {
  8.     FILE *fp;
  9.     int fd;
  10.     int ch;

  11.     if(argc < 2)
  12.     {
  13.         fprintf(stderr,"usage : %s argv[1]\n",argv[0]);
  14.         return -1;
  15.     }
  16.     
  17.     if((fd = open(argv[1],O_WRONLY | O_CREAT,0777)) < 0)
  18.     {
  19.         fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno));
  20.         return -1;
  21.     }

  22.         return 0;
  23. }
运行结果:
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
cyg文件原本不存在,运行程序后,创建了cyg文件,可以看到cyg文件的实际权限和指定的权限不一样。
注意:文件的实际权限 = mode & ~umask,umask系统默认值为0022,可以通过/etc/profile文件进行修改,也可以直接命令行输入   umask 0002来修改。
rwxr-xr-x = 0777 & ~0022 =  755

例二:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <fcntl.h>
  4. #include <string.h>
  5. #include <errno.h>

  6. int main(int argc,char *argv[])
  7. {
  8.     FILE *fp;
  9.     int fd;
  10.     int ch;

  11.     if(argc < 2)
  12.     {
  13.         fprintf(stderr,"usage : %s argv[1]\n",argv[0]);
  14.         return -1;
  15.     }
  16.     
  17.     if((fd = open(argv[1],O_WRONLY | O_CREAT,0777)) < 0)
  18.     {
  19.         fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno));
  20.         return -1;
  21.     }
  22.         
  23.         if((fp = fdopen(fd,"r")) == NULL)
  24.     {
  25.         perror("Fail to fdopen");
  26.         return -1;
  27.     }
  28.         
  29.         return 0;
  30. }
运行结果:
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
fdopen函数的功能是通过以打开的文件描述符,获取对应的流指针,这样就可以通过标准I/O的库函数来操作此文件。
以上我们在操作的过程中,open打开文件是以只写形式打开,而在fdopen的时候,指定的权限是只读,导致调用fdopen函数失败。

注意:fdopen在指定权限时,应该和打开文件时的操作权限一样。

例三:
  1. #include <sys/types.h>
  2. #include <fcntl.h>
  3. #include <string.h>
  4. #include <errno.h>

  5. int main(int argc,char *argv[])
  6. {
  7.     FILE *fp;
  8.     int fd;
  9.     int ch;

  10.     if(argc < 2)
  11.     {
  12.         fprintf(stderr,"usage : %s argv[1]\n",argv[0]);
  13.         return -1;
  14.     }
  15.     
  16.     if((fd = open(argv[1],O_WRONLY | O_CREAT,0744)) < 0)
  17.     {
  18.         fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno));
  19.         return -1;
  20.     }


  21.     if((fp = fdopen(fd,"w")) == NULL)
  22.     {
  23.         perror("Fail to fdopen");
  24.         return -1;
  25.     }

  26.     if((ch = fgetc(fp)) == EOF)
  27.     {
  28.         perror("Fail to fgetc");
  29.         return -1;
  30.     }


  31.     return 0;
  32. }
运行结果:
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞

我们打开的时候,指定的操作权限是只写,所以后面读操作就失败了。可以看出,打开文件时候指定的操作权限,限制了后期对文件的操作权限。


三、close函数
int close(int fd);
功能:关闭打开的文件。
返回值:
成功返回0,出错返回-1,并置errno

四、read函数
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
函数功能:从打开的文件中读取数据
返回值  : 成功,读到的字节数; 0,已到达文件尾;-1 ,出错.
fd  :  文件描述符
buf  :  从指定文件读出数据存放的位置  一般定义一个字符数组 char buf[ N ];
count :  指定读出的字节数

以下情况会导致读到的子节数小于count
A.读取普通文件时,读到文件末尾还不够count字节。例如:如果文件只有30字节,而我们想读取100字节,那么实际读到的只有30字节,read函数返回30。此时再使用read函数作用于这个文件会导致read返回0。
B.从终端设备(treminal device)读取时,一般情况下每次只能读取一行。
C.从网络读取时,网络缓存可能导致读取的字节数小于count字节
D.读取pipe或者FIFO时,pipe或FIFO里的字节数可能小于count
E.从面向记录(recond-oriented)的设备读取时,某些面向记录的设备(如磁带)每次最多只能返回一个记录.
F.在读取部分数据时被信号中断

注意:读操作从当前偏移量开始,在成功返回之前,偏移量增加实际读取的字节数.

五、write函数
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
函数功能 :  向已经打开的文件写数据
返回值  :  成功,已写入的字节数;-1,出错
fd  :  文件描述符
buf  :  从内存的这个地址开始,向内存写count个字节到文件中。 
count : 指定写入的字节数

对于普通文件,写操作始于偏移量。如果打开文件时使用了O_APPEND,则每次写操作都将数据写入文件末尾。成功写入后,偏移量增加,增量为实际写入的字节数。

例:文件拷贝
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <string.h>
  6. #include <errno.h>

  7. #define MAX 100

  8. int do_copy(int rfd,int wfd)
  9. {
  10.     char buf[MAX];
  11.     int n;

  12.     while(1)
  13.     {
  14.         if((= read(rfd,buf,sizeof(buf))) < 0)
  15.         {
  16.             perror("Fail to read");
  17.             return -1;
  18.         }
  19.         
  20.         if(== 0)
  21.         {
  22.             printf("Read end of file!\n");
  23.             return 0;
  24.         }

  25.         write(wfd,buf,n);
  26.     }

  27.     return 0;
  28. }

  29. int main(int argc,char *argv[])
  30. {
  31.     int rfd,wfd;

  32.     if(argc < 3)
  33.     {
  34.         fprintf(stderr,"usage : %s argv[1] argv[2].\n",argv[0]);
  35.         return -1;
  36.     }

  37.     if((rfd = open(argv[1],O_RDONLY)) < 0)
  38.     {
  39.         fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno));
  40.         return -1;
  41.     }

  42.     if((wfd = open(argv[2],O_WRONLY | O_CREAT,0666)) < 0)
  43.     {
  44.         fprintf(stderr,"Fail to open %s : %s\n",argv[2],strerror(errno));
  45.         return -1;
  46.     }

  47.     if(do_copy(rfd,wfd) < 0)
  48.     {
  49.         return -1;
  50.     }

  51.     return 0;
  52. }
运行结果:
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞

六、定位文件:lseek函数
所有打开的文件都有一个当前文件偏移量(current file offset),以下简称cfo,cfo通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于cfo,并且使cfo增加所读写的字节数。文件被打开时,cfo会被初始化为0,除非使用了O_APPEND。
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞
返回值:成功,文件的当前位移;-1,出错
fd:文件描述符
offset:偏移量,单位是字节的数量,可正可负(向前、向后移)。
whence:
1.如果whence是SEEK_SET,文件偏移量将被设置为offset。
2.如果whence是SEEK_CUR,文件偏移量被设置为cfo加上offset,offset可以为正也可以为负。
3.如果whence是SEEK_END,文件偏移量将被设置为 文件长度加上offset,offset可以为正也可以为负。

lseek函数只修改文件表项中的文件当前偏移量,没有进行任何的I/O操作

例:将file.c文件的前一半放在file1.c,后一半放在file2.c 
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>

  8. #define MAX 100

  9. int half_store(int fd,int fd1,int fd2)
  10. {
  11.     char ch;
  12.     int len = 0,count = 0,= 0;

  13.     len = lseek(fd,0,SEEK_END);
  14.     lseek(fd,0,SEEK_SET);
  15.     
  16.     while(1)
  17.     {
  18.         if((= read(fd,&ch,1)) < 0)
  19.         {
  20.             perror("Fail to read");
  21.             return -1;
  22.         }
  23.         
  24.         if(== 0)
  25.         {
  26.             printf("Read end of file.\n");
  27.             return 0;
  28.         }
  29.         
  30.         count ++;

  31.         if(count <= len / 2)
  32.         {
  33.             write(fd1,&ch,1);
  34.         
  35.         }else{
  36.             
  37.             write(fd2,&ch,1);
  38.         }
  39.     }
  40.     
  41.     return 0;
  42. }

  43. int main(int argc,char *argv[])
  44. {
  45.     int fd,fd1,fd2;
  46.     
  47.     if(argc < 3)
  48.     {
  49.         fprintf(stderr,"usage : %s argv[1] argv[2].\n",argv[0]);
  50.         return -1;
  51.     }

  52.     if((fd = open(argv[1],O_RDONLY)) < 0)
  53.     {
  54.         fprintf(stderr,"Fail to open %s : %s.\n",argv[1],strerror(errno));
  55.         return -1;
  56.     }

  57.     if((fd1 = open(argv[2],O_WRONLY | O_CREAT,0666)) < 0)
  58.     {
  59.         fprintf(stderr,"Fail to open %s : %s.\n",argv[2],strerror(errno));
  60.         return -1;
  61.     }

  62.     if((fd2 = open(argv[3],O_WRONLY | O_CREAT,0666)) < 0)
  63.     {
  64.         fprintf(stderr,"Fail to open %s : %s.\n",argv[3],strerror(errno));
  65.         return -1;
  66.     }

  67.     if(half_store(fd,fd1,fd2) < 0)
  68.     {
  69.         printf("Fail to half_store.\n");
  70.         return -1;
  71.     }

  72.     return 0;
  73. }

运行结果如下:
Linux文件I/O相关函数 - 戴↑Ω听歌 - ∞

注意:如果offset值大于文件实际大小,并且进行了写操作,会在文件中产生空洞,从之前的文件末尾到现在的文件末尾全部为'\0'

  评论这张
 
阅读(7)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017