UNIX环境编程—文件I/O

一、打开/关闭文件

1

名称:

open

目标:

打开一个文件。

头文件:

#include <sys/types.h>

#include <sys/stat.h>

#include < fcntl.h>

函数原形:

int open(const char * pathname,int flags);

int open(const char * pathname,int flags,mode_t mode);

参数:

pathname    文件名

    

flags          打开模式

返回值:

-1     遇到错误

    

int    打开成功,返回文件描述符。

这个系统调用在进程和文件之间建立一条连接 ,这个连接被称为文件描述符,它就像一条由进程通向内核的管道。

要打开一个文件,必须指定文件名和打开模式,有3种打开模式:只读,只写,可读可写,分别对应于O_RDONLY,O_WRONLY,O_RDWR,这在头文件/usr/include/fcntl.h中有定义。

       打开文件是内核提供的服务,如果在打开过程中内核检测到任何错误,这个系统调用就会返回-1。错误的类型是各种各样的,如:要打开的文件不存在。即使文件存在可能因为权限不够而无法打开,在open的联机帮助中列出了各种可能的错误,大家可以看看。

       UNIX允许一个文件被多个进程访问,也就是说当一个文件被一个进程打开后,这个文件还可以被其它进程打开。

      如果文件被顺利打开内核会返回一个正整数的值,这个数值就叫文件描述符,文件描述符是是一个简单的整数,用以标明每一个被进程所打开的文件,描述符0代表标准输出,对应的宏是 STDOUT_FILENO,描述符1代表标准输入,对应的宏为STDIN_FILENO,描述符2代表标准错误输出,对应的宏为STDERR_FILENO,系统给进程分配描述符都是从3开始的,如果同时打开好几个文件,它们所对应的的文件描述符是不同的,如果一个文件打开多次,对应的文件描述符也不相同。必须通过文件描述符对文件操作。下面的程序可以证明这一点。

/*1_1.c*/

#include <stdio.h>

#include <fcntl.h>

#include <unistd.h>

 

int main(int ac,char *av[])

{

int fd;

int size;

char buf[1024];

 

if(ac==1)

    printf(“please input file!/n”);

else

{

    while(--ac)

    {

         printf(“file:%s/n”,av[ac]);

         fd=open(av[ac],O_RDONLY);

         printf(“fd,%d/n”,fd);

         size=read(fd,buf,sizeof(buf));

         printf(“fd,%d/n”,size);

         printf(“%s”,buf);

    }

}

close(fd);

}

 

我们编译一下

[root@LINUX root]# cc –o show_read show_read.c

运行

[root@LINUX root]# ./show_read show_read.c

下面是运行结果。

file: show_read.c

fd:3

size:423

#include <stdio.h>

……

 

 

我们可以看出此次打开文件的文件描述符是3,如果我们执行下面的语句。

[root@LINUX root]# ./show_read show_read.c show_read.c

下面是运行结果:

file: 3

size: 423

#include <stdio.h>

……

 

file: 4

size 432

#include <stdio.h>

……

 

可以看到,我们第一次打开文件的描述符是3,第二次打开文件的文件描述符是4

 

Open 函数的第二个功能是创建一个新文件并把它打开,其中有几个宏定义对于着flags参数:

       O_CREAT      如果打开文件不存在open就创建一个文件。

       O_TRUNC   如果打开的文件已经存在open就把原文件清空,长度置为0

所有我们利用openpathname, O_WRONLY| O_CREAT| O_TRUNC,0777;

       下面是一个例子

/*1_2.c*/

#include <unistd.h>

#include <sys/stat.h>

#include <sys/fcntl.h>

#include <stdio.h>

 

char buf[]=”abcdefg”;

int main(int argc,char *argv[ ])

{

    int  fd;

    if((fd=open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0777))==-1)

        perror (“error”);

if(write(fd,buf,7)!=7)

    perror(“error”);

if(read(fd,buf,sizeof(buf))<0)

    perror (“error”);

printf(“%s/n”,buf);:

}

 

2

名称:

close

目标:

关闭一个文件。

头文件:

#include < unistd.h>

函数原形:

int close(int fd)

参数:

fd    文件描述符

返回值:

-1     遇到错误

    

int    关闭成功,返回文件描述符。

       Close这个系统调用会关闭进程和文件fd之间的连接,如果关闭过程中出现错误,close返回-1,如:fd所指的文件并不存在。关闭成功则返回文件描述符。

 

3.

名称:

creat

目标:

创建/重写一个文件

头文件:

#include <sys/types.h>

#include <stat.h>

#include < fcntl.h>

函数原形:

int creat(const char *pathname,mode_t mode)

参数:

pathname   文件名

 

mode      访问模式

返回值:

-1         遇到错误

    /

fd         创建成功,返回文件描述符

       Creat告诉内核创建一个名为filename的文件,如果这个文件不存在,就创建它,如果已经存在,就把它的内容清空,把文件的长度设为0    

       如果内核成功地创建了文件,那么文件的许可位(permission bits)被设置为由第二个参数mode所指定的值.如:

fd=creat(“addressbook”,0644);

       创建一个名为addressbook的文件,如果文件不存在,那么文件的许可位被设为 rw-r-r—.

如果文件已存在它的内容会被清空。任一情况下,fd都会是指向addressbook的文件描述符。

 

二、文件的读取和写入

4

名称:

read

目标:

把数据读到缓冲区。

头文件:

#include < unistd.h>

函数原形:

ssize_t read(int fd, void *buf, size_t count)

参数:

fd       文件描述符

 

buf      用来存放数据的目的缓冲区

 

count     要读取的字节数

返回值:

-1       遇到错误

    

numread  成功关闭,返回所读取的字节数目。

read这个系统调用请求内核从fd所指定的文件中读取qty字节的数据,存放到buf所指定的内存空间中,内核如果成功地读取了数据,就返回所读取的字节数目。否则返回-1

当文件的字节数没有你想要的那么多时,read就会判断下一个数值是不是’/0’,如果是就停止读取,然后退出。numread返回的是’/0’之前的字节数,也就是是原文件的字节数而不是你想读的字节数。

 

 

 

5.

名称:

write

目标:

将内存中的数据写入文件。

头文件:

#include < unistd.h>

函数原形:

size_t write(int fd, const void *buf, size_t count)

参数:

fd       文件描述符

 

buf      内存数据

 

count    要写的字节数

返回值:

-1       遇到错误

    

Num written  成功写入,返回写入的字节数目。

在实际的写入过程中,可能会出现写入的字节数少于所要求的。这可能有两个原因,第一是有的系统对文件的最大尺寸有限制,第二是磁盘空间接近满了。在上述两种情况下内核都会尽力把数据往文件中写,并将实际写入的字节数返回,所以调用write后都必须检查返回值是否与要写入的相同,如果不同就要采取相应的措施。

       学完上面几个系统调用,我们就可以自己编写的cp命令了。它的基本思路是从原文件读取数据写入缓冲,再将缓冲的数据写入目标文件。

/*1_3.c*/

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <stdlib.h>

 

#define BUFFERSIZE 4096

#define COPYMODE 0644 

 

void oops(char *s1,char *s2);

 

main(int argc,char *argv[])

{

       int in_fd,out_fd,n_chars;

       char buf[BUFFERSIZE];

 

       if(argc!=3)

       {

              fprintf(stderr,”usage:%s source destination/n”,*argv);

              exit(1);

       }

       if((in_fd=open(argv[1],O_RDONLY))==-1)

              oops(“Cannot open”,argv[1]);

       if((out_fd=creat(argv[2], COPYMODE))==-1)

              oops(“Cannot creat”,argv[2]);

       while((n_chars=read(in_fd,buf,BUFFERSIZE))>0)

       {

              if(write(out_fd,buf,n_chars)!=n_chars)

                     oops(“Write error to”,argv[2]);

       }

       if(n_chars==-1)

              opps(“Read error form”,argv[1]);

       if(close(in_fd)==-1||close(out_fd)==-1)

              oops(“Error clising files”);

}

 

void oops(char *s1,char *s2)

{

       fprintf(stderr,”Error:%s”,s1);

       perror(s2);

       exit(1);

}

 

三、文件描述符操作函数

6

名称:

lseek

目标:

使指针指向文件中的指定位置。

头文件:

#include <sys/types.h>

#include <unistd.h>

函数原形:

off_t  lseek(int fildes,off_t offset,int whence)

参数:

fildes    文件描述符

offset    移动的距离

wence   SEEK_SET=>文件的开始

        SEEK_CUR=>当前位子

        SEEK_END=>文件结束

返回值:

-1      遇到错误

Ildpos   指针变化前的位子

       lseek改变文件描述符所关联的指针的位置,新的位置由offsetwence来指定,wence是基准位置,基准位子可以是文件的开始(0)、当前位子(1)或文件的结束(2)。offset是从基准位子开始的偏移量。若wenceSEEK_SET,该文件的偏移设置为距文件开始处offset个字节数。若wenceSEEK_CUR,该文件的偏移设置为其当前值加offset, offset可为正或负。若wenceSEEK_END,该文件的偏移设置为文件长度加offset, offset可为正或负。

       lseek(fd,0,SEEK_END);

       文件位偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中没有写过的字节都被读为0

下面是一个例子:

/*1_4.c*/

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdlib.h>

 

char buf1[]=”abcdefg”;

char buf2[]=”ABCDEFG”;

 

int main(void)

{

int fd;

if((fd=creat(“file.hole”,0644))==-1)

    printf(“creat error”);

if(write(fd,buf1,7)!=7)

    printf (“buf1 write error”);

if(lseek(fd,40,SEEK_SET)==-1)

    printf (“lseek error”);

if(write(fd,buf2,7)!=7)

printf (“buf2 write error”);

exit(0);

}

 

 

7.

名称:

dup/dup2

目标:

复制一个现存的文件描述符.

头文件:

#include <unistd.h>

函数原形:

int dup(int oldfd)

int dup2(int oldfd,int newfd)

参数:

oldfd    原有文件描述符

newfd   新的文件描述符

返回值:

-1      遇到错误

int      新文件描述符

  系统调用dup是用来复制一个文件描述符,也就是将进程u区的文件描述符表中的一项复制一份,使得这两项同时指向系统稳健表的同一表项。

  系统调用dup复制由参数oldfd指定的文件描述到进程文件描述符表的第一个空表项处。而系统调用dup2复制由参数oldfd指定的文件描述到参数newfd指定的文件描述符表项处。老的文件描述符和新复制的文件描述符可以互换使用。它们共享锁、文件指针和文件状态。例如,对其中一个文件描述符使用系统调用lseek修改文件指针的位置,对另一文件描述符来说文件指针也改变了,其实我们了解了内核的工作原理,这一点很容易理解。因为我们知道,文件指针是放在系统文件表中的。但这两个文件描述符具有不同的close-on-exec标志,因为该标志是存放在文件描述符表中的。

  该调用成功时,返回值为新的描述符;错误时,返回-1,并设置相应的错误代码errno

  • EBADF:参数oldfd不是一个已经打开的文件描述符;或者参数newfd超出允许的文件描述符的取值范围。
  • EMFILE:进程打开的文件描述符数量已经到达最大值,但仍然企图打开新的文件描述符。

  下面我们来看一个简单的例子。在这个例子中,我们将标准输出(文件描述符为1)关闭,并将一个打开了普通文件“output”的文件描述符复制到标准输出上,因为刚关闭了文件描述符1,所以,文件描述符表的第一个空表项是1。所以,程序以后的printf等向标准输出写的内容都写到了文件中。

利用这个功能我们可以把输出/输入重定向到文件中。下面是一个例子。

/*1_5.c*/

#include <stdio.h>

#include <unistd.h>

#include <systypes.h>

#include <sys/stat.h>

#include <string.h>

#include <errno.h>

#include <fcntl.h>

 

main(int argc,char *argv[])
{
  int fd;
  if ((fd=open(argv[1],O_CREAT|O_RDWR,0644))==-1)
    printf("cannot open output file ");


  close(1); /* 关闭标准输出 */
  if(dup(fd)==-1); /* 复制fd到文件描述符1
 */
      perror(
error);

close(fd); /* 即时关闭不用的文件描述符是一个好习惯 */
  printf("This line will write to file ");

}

  该程序执行过程的屏幕拷贝:

  [wap@wapgw /tmp]$ gcc -o 1_5 1_5.c
  [wap@wapgw /tmp]$ ./1_5 test1
  [wap@wapgw /tmp]$ more test1
  This line will write to file

 

dup2的功能和dup基本相同,只不过是dup2复制oldfdnewfd上。下面是用dup2实现同样的例子。

/*1_6.c*/

#include <stdio.h>

#include <unistd.h>

#include <systypes.h>

#include <sys/stat.h>

#include <string.h>

#include <errno.h>

#include <fcntl.h>

 

main(int argc,char *argv[])

{

int fd;

if((fd=open(argv[1],O_WRONLY|O_CREAT,0644))==-1)

    perror(“error”);

close(1);

if(dup2(fd,1)==-1)

    perror(“error”);

close(fd);

printf(“This line will write to file /n”);

close(fd);

}

 

利用这两个函数我们可以实现管道的功能,有关管道的内容将在后面介绍。

 

8

名称:

fcntl

目标:

改变已经打开文件的性质。

头文件:

#include <unistd.h>

#include <sys/types.h>

#include <fcntl.h>

函数原形:

int fcntl(int fd,int cmd);

int fcntl(int fd,int cmd,long arg);

int fcntl(int fd,int cmd,struct flock *lock);

参数:

fd       文件描述符

cmd     功能描述位

返回值:

-1      遇到错误

V-node

int      依赖于cmd

fcntl函数有5种功能:

1    复制一个现存的文件描述符(cmd=F_DUPFD

2         获得/设置文件描述符标记(cmd=F_GETFDF_SETFD

3         获得/设置文件状态标志(cmd=F_GETFLF_SETFL

4         获得/设置异步I/O所有权(cmd=F_GETOWN F_SETOWN

5         获得/设置记录锁(cmd=F_GRTLK,F_SETLK或F_SETLKW

 

fcntl的功能之一是重复文件描述字。

fcntl (FileDescriptor, F_DUPFD, 0)等价于Dup(FileDescriptor)

close (New); fcntl(Old, F_DUPFD, New)等价于dup2 (Old,New)

 

fcntl的功能之二获得/设置文件描述符标记。

获得文件描述标记fcntl (FileDescriptor, F_GETFD)

设置文件描述标记fcntl(FileDescriptor, F_SETFD, flags)

 

fcntl的功能之三获得/设置文件状态标签,文件状态标签分为3类它们是文件访问方式、打开时标志和I/O操作方式。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值