Linux的基本I/O编程

系统调用和API

在Linux 中,为了保护内核空间,将程序的运行空间分为内核空间和用户空间(内核态和用户态),它们运行在不同的级别上,在逻辑上是相互隔离的,因此用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们自己在用户空间操作用户数据,调用用户空间的函数。操作系统为用户提供了两个接口:一个用户编程接口API,用户利用这些操作系统命令来组织和控制任务的执行和管理计算机系统;另一个是系统调用,编程人员使用系统调用来请求操作系统提供服务。一个API函数需要一个或几个系统调用来共同完成函数的功能,设置还有一些API函数不需要调用相应的系统调用。

Linux中,用户编程接口遵循了UNIX中流行的应用应用界面标准——POSIX标准。

我们平时常用的系统命令实际上是一个可执行程序,它的内部引用了用户编程接口来实现相应的功能,最终可能还会需要系统调用来完成相应的功能。所以,事实上上命令控制界面(系统命令)也是在系统调用的基础上开发完成的。

Linux中的文件及文件描述符

Linux中文件可以分为4种:普通文件、目录文件、链接文件和设备文件。对Linux而言,所有设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负数,它是一个索引值,并指向内核中每个进程中打开的文件的的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读/写文件时,也需要把文件描述符作为参数传递给相应的函数。



基本的I/O操作

Linux的I/O操作,通常分5个方面,打开、读取、写入、定位和关闭;对应的5个系统调用,分别是:open、read、write、lseek和close这5个函数,也称为不带缓存的I/O操作。这些函数不属于ANSIC C的组成部分,是属于POSIX的一部分。以下是它们的原型:

#include<sys/types.h>
#include<sys/stat.h>     /*声明mode_t*/
#include<fcntl.h>        /*声明调用open()时使用的flag常量*/
#include<unistd.h>       /*声明ssize_t*/
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);
off_t lseek(int fd, off_t offset, int whence);
int close(int fd)



基本的I/O函数的一个共同的特点就是,它们都是通过文件描述符(file descriptor)来完成文件的I/O操作的。文件描述符是一个整数,有效的文件描述符从0开始一直到系统定义的某个界限,这些整数实际上是进程打开文件表的索引,这个表由操作系统在内部维护,用户是不能直接访问的。

(1)open函数

传入参数:

pathname 为字符串,表示被打开的文件名称,可以包含路径。

flag是为一个或多个标志(多个时用“|”连接起来),表示文件的打开方式

mode 被打开文件的存取模式,可以使用八进制数来表示新文件的权限,也可以采用<sys/stat.h>中定义定义的符号常量,当打开已有文件时,将忽略这个参数。

open函数调用成功则返回文件描述符,出错则返回-1。常用flags标志:

标志符 含义和作用

O_RDONLY 只读方式打开

O_WRONLY 只写方式打开

O_RDWR 读/写方式打开

O_CREAT 如果文件不存在,就创建新文件

O_EXCL 如果使用O_CREAT时文件存在,则返回错误消息

O_TRUNC 如果文件已存在,且以只读或只写成功打开,则先全部删除文件中原有数据

O_APPEND 以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾

文件模式符号常量(略)。

(2)read和write函数

函数传入参数的含义:

fd 文件描述符

buf 指定存储器读出数据的缓冲区

count 指定读出或写入的字节数

函数返回值,如果发生错误,那么返回值是-1,同时设置errno变量为错误代码。如果操作成功,则返回值是实际读取或写入的字节数。每次使用这两个函数时,应该尽量采取块读/写的方式,提高I/O的效率。

(3)close函数

当使用完文件时,调用close函数,可以是缓冲区中的数据写回磁盘,并释放文件所占用的资源。

该函数返回值:若文件顺利关闭则返回0,发生错误则返回-1,并设置errno。通常文件关闭时出错并不抽奖,但也有可能,特别是关闭通过网络访问的文件。



基本I/O函数的综合实例

  /*fileIO.c*/
   #include<unistd.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<stdio.h>
   int main(void)
   {
        int fd, size;
        char s[]="This program is used to show how to use open(), write(), read(), function.\nHave fun!\n";
        char buffer[80];
      
        /*以只读/写的方式打开一个文件,如果文件不存在则创建该文件*/
        fd = open("temp.log", O_WRONLY | O_CREAT);
        if(-1 == fd)
        {
             printf("Open or create file named\"temp.log\"failed.\n");
             return -1;
        }
        write(fd, s, sizeof(s));         /*向该文件写一个字符串*/
        close(fd);

        fd = open("temp.log", O_RDONLY);
        if(-1 == fd)
        {
              printf("Open file named\"temp.log\"failed.\n");
              return -1;
        }
        /*读取文件内容保存到buffer指定的字符串数组中,返回读取的字符个数*/
        size = read(fd, buffer, sizeof(buffer));
        close(fd);
        printf("%s", buffer);

        return 0;
   }



标准I/O操作

基本I/O函数的读写都是基于文件描述符,由基本的I/O控制的,不带缓存。在高层应用中,不带缓存的I/O操作效率低,而由用户自行维护缓冲区繁琐且易出错。标准I/O是由ANSI制定,符合ANSI C的标准的I/O处理。这些函数定义在<stdio.h>中。在这个头文件中,有许多函数名称相似,如printf(),fprintf()和spritnf(),它们代表了对三种不同类型的流的相同的操作,printf()针对标准流,fprintf()针对文件流,sprintf()针对字符流。大部分的标准I/O函数都有上述的三种不同类型流的版本,其实现的功能完全相同,但是函数名称和调用的顺序存在一定的差异。

(1)fopen()函数

打开文件有三个标准函数,分别为:fopen,fdopen和freopen,函数原型如下所示:

#include<stdio.h>
FILE *fopen(const char *pathname, const char *type);
FILE *freopen(const char *pathname, const cchar *type, FILE *fp);
FILE *fdopen(int filedes, const char *type);

它们以不同的模式打开文件,并返回一个指向文件流的FILE指针,以后的文件读/写都是通过这个FILE指针来进行。

fopen()函数可以指定打开文件的路径和模式,路径由参数path指定,模式相当于open()函数中的标志位flag。mode的取值:

mode字符串 含义

R或rb 打开只读文件,该文件必须存在

R+或者r+b 打开可读/写的文件,该文件必须存在

W或者wb 打开只写文件,若文件存在,则文件长度清为0,否则建立文件

w+或w+b 打开可读/写文件,若文件存在,则文件长度清为0,否则建立文件

a或ab 以追加的方式打开只写文件,若文件存在,则写入的文件附加到文件的尾部,不会修改文件原有的数据;若文件不存在,则建立该文件

a+或a+b 以追加的方式打开可读/写文件,若文件存在,则写入的文件附加到文件的尾部,不会修改文件原有的数据;若文件不存在,则建立该文件

凡是在mode字符串后带有b字符的,表示打开的文件为二进制文件。不同的打开方式对文件的结尾的处理方式是不相同的,不过通常linux会自动识别不同类型的文件而忽略这个符号。fdopen()函数会将参数fd的文件描述符,转换为对应的文件指针后返回。freopen()函数会将已打开的文件指针stream关闭后,打开参数path 的文件。

(2)fclose函数

int fflush(FILE *fp);

当调用close函数时,这时缓冲区的数据将写入文件中,并释放系统所提供的文件资源。而如果仅仅是想要将缓冲区的数据写入文件,而不是关闭文件,可以调用函数

fflush()。

int fflush(FILE *fp);

(3)fread和fwrite函数

  #include<stdio.h>

       size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

       size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);


返回值:读/写的记录数,成功时返回的记录数等于nmemb,出错或读到文件末尾时返回记录数小于nmemb,也可能返回0.

fread和fwrite用于读/写记录,这里的记录是指一串固定长度的字节,如一个int、一个结构体或者一个定长数组。参数size指出的长度,而nmemb指出要读或者写多少条记录,这些记录在ptr所指的内存空间中连续存放,共占size*nmemb字节,fread从文件stream中读出size*nmemb字节保存到ptr中,而fwrite把size*nmemb个字节写到文件stream中。

nmemb是请求读或写的记录数,fread和fwrite返回的记录数有可能小于nmemb指定的记录数。例如,当前读/写位置距离文件末尾只有一条记录的长度,调用fread时指定nmemb为2,则返回值为1,。如果当前的读/写位置已经在文件末尾了,或者读文件是出错了,则fread返回0.如果写文件是出错 了。则fwrite的返回值小于nmemb指定的值。


标准I/O综合实例

完成写功能的程序:

   /*writerec.c*/

     #include<stdio.h>

     #include<stdlib.h>

     struct record

     {

             char name[10];

             int age;

     };

 

     int main(void)

    {

           struct record array[2] = {{"Ken", 24}, {"Knuth", 28}};

           FILE *fp = fopen("recfile", "w");

           if(fp == NULL)

           {

                    perror("Open file recfile");

                    exit(1);

           }

          fwrite(array, sizeof(struct record), 2, fp);

          fclose(fp);

      

          return 0;

    }

   完成读功能的程序:

   /*readrec.c*/

  #include<stdio.h>

  #include<stdlib.h>

  struct record

  {

          char name[10];

          int age;

  };

  int main(void)

  {

         struct record array[2];

         FILE *fp = fopen("recfile", "r");

       

         if(fp == NULL)

         {

                  perror("Open file recfile");

                  exit(1);

         }

         fread(array, sozeof(struct record), 2, fp);

         printf("Name1:%s \tAge1:%d\n", array[0]. name, array[0].age);

         printf("Name2:%s\tAge2:%d\n", array[1].name, array[1].age);

         fclose(fp);

    

         return 0;

 }







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值