文件处理

底层文件访问

每个进程都会有一些自己的文件描述符,这些文件描述符表示当前进程正在操作的文件。在一个进程打开一个文件的时候,实际上就是在自己的文件表中创建该文件的文件描述符,创建文件描述符之后,可以通过该描述符对文件进行操作。

当一个程序开始运行的时候,一般都会有三个默认存在的文件描述符。他们分别是:

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误

write系统调用

系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件,并返回实际写入的字节数。如果函数返回0,表示未写入任何数据;如果返回-1,就表示在write调用中出现了错误,错误代码保存在全局变量errno里。

errno在头文件errno.h中定义,该头文件还定义了很多表示不同错误类型的宏,如果有需要可以在头文件中查找错误代码对应的错误类型。

write系统调用的定义在头文件<unistd.h>里,其形式如下:

size_t write(int fildes, const void * buf, size_t nbytes);

下面是一个例子:

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main(void){
    if((write(1, "Here is some data\n", 18)) != 18){ //可以将这里的进程描述符1改成3 看会发生什么
        write(2, "Write error has occurred on file descriptor 1\n", 46);
        printf("An error occurred, errno = %d\n", errno);
    }   
    
    return 0;
}

read系统调用

read的作用是从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把它们放到缓冲区buf中。函数返回读到的字节数,如果返回0则还没读到数据就到文件末尾了,-1表示出现了错误。

read()在头文件unistd.h中定义,其形式为:

size_t read(int fildes, void * buf, size_t nbytes);

下面是一个使用的例子:

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main(void){
    char buffer[128];
    int nread;

    nread = read(0, buffer, 128);
    if(nread == -1){
        write(2, "A read error has occurred/n", 26);
        printf("The error code is %d\n", errno);
    }   
    if((write(1, buffer, nread)) != nread){
        write(2, "A write error has occrred\n", 27);
        printf("The error code is %d\n", errno);
    }   
    return 0;
}

open系统调用

open()如果执行成功,就会返回一个可以被readwrite和其他系统调用使用的文件描述符。这个文件描述符是唯一的,不与其他进程的文件描述符共享。如果两个进程同时打开同一个文件,这两个进程都会创建属于自己的文件描述符。如果两个进程同时写文件,则会按照先后顺序对文件进行修改。他们会接着自己上次离开的位置继续往下写, 所以会彼此覆盖。

open()fcntl.h中定义,其形式为:

int open(const char * path, int oflags);
int open(const char * path, int oflags, mode_t mode);

path表示文件的名字,oflags参数用于指定打开文件所采取的动作,用只读,只写还是读写的方式打开。后面的mode用于制定访问权限。

其中oflags参数的值必须是下面三种中的一种:

  • O_RDONLY:以只读方式打开。
  • O_WRONLY:以只写方式打开。
  • O_RDWR:以读写方式打开。

oflags被指定成上述值之后,还可以在值的后面加上|,并添加额外的值,额外的值可以是:

  • O_APPEND:在文件末尾写入数据。
  • O_TRUNC:把文件长度设置为0,丢弃已有内容。
  • O_CREAT:如果需要,就按参数mode中给定的访问权限创建文件。
  • O_EXCL:与O_CREAT一起使用,确保调用者创建出文件。open调用是一个原子操作,也就是说,它只执行一个函数调用。使用这个可选模式可以防止两个程序同时创建同一个文件。如果文件已经存在,open调用将失败。

open调用成功时返回一个新的文件描述符,失败时返回-1并将错误代码放在全局变量errno中。

当你使用O_CREAT作为参数oflags的值时,必须为mode参数指定值,mode参数可能的取值为:

  • S_IRUSR:所有者读权限。
  • S_IWUSR:所有者写权限。
  • S_IXUSR:所有者执行权限。
  • S_IRGRP:组内读权限。
  • S_IWGRP:组内写权限。
  • S_IXGRP:组内执行权限。
  • S_IROTH:其他用户读权限。
  • S_IWOTH:其他用户写权限。
  • S_IXOTH:其他用户执行权限。

这些参数在头文件sys/stat.h中定义,并且可以通过|传入多个,以设置不同等级用户的权限,例如:

open("myfile", O_CREAT, S_IRUSR | S_IXOTH);

该函数创建了一个名为myfile的文件,并设置其访问权限为所有者只读,其他用户可执行

umask环境变量

该环境变量在shell中用umask命令指定,制定了该变量之后,它将规定当前环境下运行的程序可创建的文件的权限。如果umask禁止其他用户访问新创建的文件,就算程序在使用open调用文件设置了S_IROTH,程序在执行的时候,该选项也会被移除,导致新创建的文件不可被其他用户读。

umask是由三个八进制数字组成,从左到右依次表示用户其他用户的权限,其指定的方式跟Linuxchmod命令指定权限的格式相反,chmod的数字表示赋予权限,而umask中的数字表示禁止权限。

close系统调用

该系统调用解除文件描述符与文件之间的关联,使当前的文件描述符重新变得可用。它在unistd.h中声明。定义为:

int close(int fildes);

close在成功时返回0,出错时返回1.

一个例子

这个例子实现了复制文件的过程,使用了上述介绍的系统调用。

程序将输入文件file.in逐个字符地复制到输出文件file.out中。

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void){
    char c;
    int in, out;
    //以只读的形式打开文件file.in
    in = open("file.in", O_RDONLY);
    //以读写的形式打开文件file.out,并且设置只有文件所有者可以读写
    out = open("file.out", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    while(read(in, &c, 1) == 1){
        write(out, &c, 1);
    }

    return 0;
}

其他与文件管理相关的系统调用

除了基本的文件打开,关闭,读写操作之外,系统还提供了另外一些对文件描述符进行操作的系统调用,通过这些系统调用,可以更灵活地处理文件。

lseek系统调用

该系统调用用来设置读写文件的位置,他会设置文件描述符中的读写指针的位置。通过这个调用,可以读写文件中任意一个位置的数据。

它在头文件unistd.h中定义,并且参数所用到的宏在sys/types.h中定义:

#include <unistd.h>
off_t lseek(int fildes, off_t offset, int whence);

#include <sys/types.h>
#define SEEK_SET
#define SEEK_CUR
#define SEEK_END

offset是一个表示位置的数字,该数字的含义由whence定义,whence可能的取值如下:

  • SEEK_SEToffset是一个绝对位置。
  • SEEK_CURoffset是相对于当前位置的一个相对位置。
  • SEEK_ENDoffset是相对于文件末尾的一个相对位置。

lseek返回从文件头到文件设置位置的字节偏移值,失败时返回-1

fstatstatlstat系统调用

这三个系统调用都会返回文件的信息,并且都会将文件的信息装进一个stat结构体中,而这三个系统调用的区别如下:

  • fstat:通过文件描述符查找文件。
  • stat:通过文件名查找文件,并且当接受一个链接为参数时,返回链接指向的文件的信息。
  • lstat:通过文件名查找文件,并且当接受一个链接为参数时,返回链接本身的信息。

这三个系统调用的声明如下:

#include <unsitd.h>
#include <sys/stat.h>

int fstat(int fildes, struct stat * buf);
int stat(const char * path, struct stat * buf);
int lstat(const char * path, struct stat * buf);

结构体stat的成员可以上百度搜索。

标准I/O库

当使用上面的底层系统调用操作文件的时候,可能会导致很多问题。例如不同系统提供的系统调用不同,使用底层系统调用操作文件会导致程序的可移植性很差。而且使用系统调用的时候,每一次调用都要访问一次磁盘,这样会造成额外的系统消耗,如果我们可以先把数据先放在缓冲区中,等攒到一定数量的数据之后在写入磁盘,这样就能节省系统资源。标准I/O库就为我们解决了这种问题。

标准I/O库的函数都定义在头文件stdio.h里,就是我们平常使用的fopen,fread等函数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值