文件I/O(1)

UNIX系统中大多数文件I/O只需要用到5个函数:open,read,write,lessk,close

这些函数是不带缓冲的I/O,read和write都调用内核的一个系统调用

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。

当打开/创建一个文件,内核向进程返回一个文件描述符

惯例:

在#include <unistd.h>中定义

文件描述符0 与进程的标准输入关联 STDIN_FILENO

文件描述符1 与标准输出关联 STDOUT_FILENO

文件描述符2 与标准错误关联 STDERR_FILENO

 

文件描述符的变化范围是0~OPEN_MAX -1 

函数open和openat

#include <fcntl.h>

path参数是要打开或者创建文件的名字

oflag参数可以用来说明此函数的多个选项

例如 O_RDONLY 只读打开

         O_WRONLY 只写打开

         O_RDWR    读写打开

         O_CREAT   若不存在则创建。使用时需要同时说明第3个参数mode,用mode指定该新文件的访问权限位

 

由open和openat函数返回的文件描述符一定是最小的未用描述符数值。

例如,一个程序可以先关闭标准输出,然后打开另一个文件,执行打开操作返回的文件描述符一定是1

 

例子:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
using namespace std;

int main()
{
        close(1);
        int fd = open("./test61.txt",O_RDWR|O_CREAT,777);
        cout<<fd<<endl;
        char* buf={"1234567"};
        write(fd,buf,7);
}

      首先关闭了标准输出,则目前可用最小文件描述符则是1,在打开文件时赋给的文件描述符就是1,cout向标准输出输出,因为现在的文件描述1属于test61.txt文件,则输出的内容进了test61.txt文件,而不是屏幕上显示。

结果:

fd参数把open和openat函数区分开:

(1) path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,就是open函数

(2)path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取的

(3)path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。这时,路径名在当前工作目录中获取。

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
using namespace std;

int main()
{
        int fd1 = openat(AT_FDCWD,"../../../test.txt",O_RDWR,777);
        int fd2 = openat(fd1,"Users/test/test.txt",O_RDWR,777);
        char buf[]="1234567";
        write(fd2,buf,7);
        return 0;
}

openat函数解决两个问题 

(1)可以让线程使用相对路径打开目录中的文件,而不再只能打开当前的工作目录。

(2)避免time-of-check-to-time-of-use(TOCTTOU)错误,这种错误的基本思想是:如果有两个基于文件的函数调用,其中第二个调用依赖于第一个调用的结果,那么程序是脆弱的。因为两个调用并不是原子操作。

 

函数create

等效于:

int open(path,O_CREAT|O_WRONLY|O_TRUNC,mode) == int openat(AT_FDCWD,path,.... , .....);

create的不足方式就在于它以只写的方式打开创建的文件。

 

函数close:

 

函数lseek

每次打开一个文件都有一个与之关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。

通常,读写操作都是从当前文件偏移量处开始,并使偏移量增加所读写的字节数。

当打开一个文件时,如果没有指定O_APPEND选项,则该偏移量被设置为0;

如果whence 是SEEK_SET,则将该文件的偏移量设置为距离文件开始处offset字节

如果whence 是SEEK_CUR,则将文件偏移量设置为当前值加offset,offset可为正负

如果whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正负

 

若lseek成功执行,则返回新的文件偏移量,但若文件描述符指向的是一个管道,FIFO,或者网络套接字,则lseek返回-1,可以以此来判断是否可以设置偏移量。lseek将当前的文件偏移量记录在内核中,并不引起任何I/O操作。然后用该偏移量用于下一个读或写操作。

 

#include <iostream>
#include <unistd.h>
#include <fcntl.h>

using namespace std;
int main()
{
        int fd = openat(AT_FDCWD,"test2333.txt",O_RDONLY);
        off_t currpos = -10;
        currpos = lseek(fd,currpos,SEEK_END);
        char buf[4];
        read(fd,buf,4);
        //cout<<buf<<endl;
        write(1,buf,4);
        cout<<endl<<currpos;
}

 

文件偏移量可以大于文件的当前长度,在这种情况下,对文件的下一次写将加长该文件。并在文件中构成一个位于文件中但没有写过的读为0的字节,称为空洞。文件的空洞并不要求在磁盘上占用存储区。

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

char buf1[] = "1234567";

int main()
{
        int fd = open("test65.txt",O_CREAT,777);
        lseek(fd,16384,SEEK_SET);
        write(fd,buf1,7);
        close(fd);
}

 

例子:用write和read函数复制一个文件

 

#include <iostream>
#include <fcntl.h>
#include <unistd.h>

using namespace std;

int main()
{
        int n;
        char buf[4096];
        while((n=read(STDIN_FILENO,buf,4096))>0)
                write(STDOUT_FILENO,buf,n);
}

 

 

函数dup和dup2

(成功返回新的文件描述符,失败返回-1)

#include <unistd.h>
int dup(int fd);
int dup2(int fd,int fd2);

由dup返回的新文件描述符一定是当前可用文件描述符的最小数值。

dup2 可用用fd2制定新描述符的值,如果fd2已经打开则将其关闭。如果fd==fd2,则dup2返回fd2,而不关闭它。

这些函数返回的新文件描述符和参数fd共享一个文件表项。

 

延迟写:(大多数磁盘I/O都通过缓冲区进行,当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,之后再写入磁盘。)当内核需要重用缓冲区时来存放其他磁盘块数据时,会把所有延迟写数据块写入磁盘。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值