Linux文件I/O函数

版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/91127921

Linux文件描述符

在Linux下当一个进程打开文件的时候,OS会返回相应的文件描述符,程序为了处理该文件必须使用这个文件描述符。文件描述符是一个正整数。一般而言,当一个进程启动的时候,他会打开3个文件:标准输入,标准输出,标准错误。这3个文件对应的文件描述符分别是0,1,2.通常使用宏:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO.文件描述符是一个索引,指向内核中打开文件的记录表。

Linux操作系统给我们提供了6个系统调用create,open,write,close,read,lseek。系统调用是不带缓冲区的。他们是POSIX标准提供的。这些函数需要的头文件#include<fcntl.h>,#include<sys/types.h>,#include<sys/stat.h>。可以使用man命令来查看这些函数的用法以及所需要的头文件。首先,使用man man命令可以知道,系统调用在第二章。

使用命令man 2+函数名字就可以看到系统调用的详细描述。

open函数和creat函数

参数pathname是路径;flags非常多,可以从手册中看到,常用的就是 O_RDONLY, O_WRONLY, O_RDWR等。mode标示了对文件的访问权限,如下所述。

mode值包含了对文件的访问权限位。正如上面描述的一样,每个文件有9个访问权限位,并且可以分为3组。

mode

含义

S_IRUSR

用户读

S_IWUSR

用户写

S_IXUSR

用户执行

S_IRGRP

组读

S_IWGRP

组写

S_IXGRP

组执行

S_IROTH

其他读

S_IWOTH

其他写

S_IXOTH

其他执行

如果打开的文件是在某个目录文件下,那么该目录必须是可执行的,因为对于目录文件而言,可执行代表着搜索位,我们可以找该目录下的文件。目录的读只代表我们可以读取该目录的文件列表,不能进行其他操作。如果当前打开了一个文件,如果是root用户的进程,那么它肯定能访问该文件。如果进程是文件所有者执行的,那么对文件的权限取决于第一组的权限;如果进程是文件所有者所在组或者附属组之一,那么对文件的权限取决于第二组权限。若进程是其他用户执行的,那么对文件的操作取决于第三组权限。

在使用open函数打开一个文件的时候,最常用的三个参数是:O_WRONLY(只写),O_RDONLY(只读),O_WRRD(可读可写)另外两种是:O_EXEC(执行),O_SEARCH(搜索,应用于目录)。另外open打开的文件,返回的文件描述符一定是最小的未使用描述符。path所指定的路径可以是绝对路径,也可以是相对路径。

flags中有些参数可以帮助我们创建文件

creat函数的不足之处是它创建的文件以只写的方式打开。当我们拥有上述参数的时候,就可以使用open函数来代替creat函数创建文件。即:

open(path,O_RDWR|O_CREAT|O_TRUNC,mode)

open函数成功时,返回一个文件描述符;若出错,返回 -1。

如果返回了-1,表示出错,我们还可以由perror函数知道出现的错误具体是什么。

当Linux系统函数出错的时候,一般会返回一个负值给errno。POSIX和ISO C将errno定义为一个符号。它可以是一个包含出错编号的整数,也可以是一个返回出错编号指针的函数,具体由开发者去实现。C定义了perror函数来打印出错信息。

perror函数首先输出参数s的内容,然后是一个冒号,一个空格,接着输出errno所对应的出错消息。然后换行。

read函数

fd是文件描述符;buf是缓冲区,用于保存从文件中读取的内容。count是读取的字节数。如果read成功,返回读取到的字节数。若已到达文件尾端,返回0。读取出错返回-1。

write函数

如果写入成功,返回以写字节数,否则,返回-1。参数的意义和read函数一致。

close函数

关闭一个文件并释放该进程加在该文件上的所有锁。当一个进程终止的时候,会自动关闭它打开的所有文件。所以有时候并不显式的使用close关闭文件。close函数返回0表示成功,返回-1表示错误。

lseek函数

每个打开文件都有一个与其相关联的“当前文件偏移量”。用于计算从文件开始处的字节数。通常,读写都是从当前文件偏移量处开始的,并使用偏移量增加所读写的字节数。系统默认该偏移量为0。可以使用lseek函数来指定一个打开文件的偏移量。

参数whence表示从哪儿开始。它有3个值,如下。

offset的值的意义具体就根据whence参数的值来决定。

一个简单的例子如下:

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

int main()
{
    int fd;
    char str[20] = {"Hello World!"};
    char buf[20] = {0};     //初始化为0,为了方便打印。
    int flag;
    //在当前路径下打开一个new.txt文件,若不存在就创建它;若存在则以可读可写方式打开,并且从长度0截断
    fd = open("./new.txt",O_CREAT|O_TRUNC|O_RDWR,0777);
    if (-1 == fd)        
    {
        perror("open fail");     //包含在stdio中的函数。
        exit(1);
    }
    
    flag = write(fd,str,20);    //给文件写入内容
    if(-1 == flag)
    {
        perror("write fail");
        exit(1);
    }

    lseek(fd,0,SEEK_SET);       //使用lseek将文件偏移量设置为文件开始处
    //如果没有这一步,那么下面的read无法读取到文件内容,因为文件偏移量已经到了字符串结束符处。

    flag = read(fd,buf,20);     //读取文件内容
    if(-1 == flag)
    {
        perror("read fail");
        exit(1);
    }

    flag = close(fd);       //关闭文件
    if(-1 == flag)
    {
        perror("close fail");
        exit(1);
    }

    printf("%s\n",buf);       //打印缓冲区中的内容。
    return 0;
}

打印结果如下:

需要注意的是,buf数组需要全部初始化为0,'\0'的ASCII就是0.这样将打开的文件中读取的文本信息打印的时候才能正常打印,不会乱码。否则不知道在哪儿终止,将会产生乱码。

注意:在使用Linux的系统调用操作文件的时候,是无缓冲的,这点很重要。当你在做少量,大批次写入的时候效率会很低。因此注意使用缓冲(用数组的之类的暂时保存一下),能提高I/O效率。

lseek不可以用于管道,FIFO,socket文件。另外lseek的文件偏移量的大小可以大于当前文件的长度,在这种情形下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。文件空洞并不要求在磁盘上占据空间。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券