linux io操作缓冲区,Linux学习:文件IO(不带缓冲区),原子操作概念

在前面的文章我们介绍过不带缓冲区的IO,这节我们主要介绍不带缓冲区的IO相关内容。原子操作对于文件共享是十分重要的,因此我们将介绍一些原子操作相关概念。

1:文件描述符

对于内核而言,所有打开的文件都通过文件描述符引用。当我们打开或者创建一个文件的时候,都会返回一个非负的整数,我们打开和创建文件都用这个非负的整数。

同时,通常在我们执行一个进程的时候,都会默认打开三个文件描述符,他们分别是标准输入(0:STDIN_FILENO)、输出(1:STDOUT_FILENO)、错误(2:STDERR_FILENO)相关联的。他们的宏定义在unistd.h文件中。

文件描述符上限是OPEN_MAX。

2:open函数介绍

函数原型:int open(const char* pathname, int flag);

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

pathname:是打开文件的路径名。

flag:是打开文件的一些选项。

mode:只有我们需要创建文件的时候,才会将指定mode的模式。

O_RDONLY:只以读的方式打开文件。

O_WRONLY:只一写的方式打开文件。

O_EXCL:测试文件受否存在,如果不存在则创建文件。(注:如果同时指定O_CREAT并且文件已经存在则出错)

O_APPEND:每次写入都追加到文件末尾。

O_CREAT:如果文件不存在,则创建文件。使用这个选项的时候,需要第三个参数。

mode:权限位,也就是我们使用ls -l显示的各种读写和执行权限的位。

S_IRWXU:用户有读写执行权限。

S_IRUSR:用户有读权限。

S_IWUSR:用户有写权限。

S_IXUSR:用户有执行权限。

S_IRWXG:同组用户有读写执行权限。

S_IRGRP:同组用户有读权限。

S_IWGRP:同组用户有写权限。

S_IXGRP:同组用户有执行权限。

S_IRWXO:其他用户有读写执行权限。

S_IROTH:其他用户有读权限。

S_IWOTH:其他用户有写权限。

S_IXOTH:其他用户有执行权限。

O_TRUNC:如果文件存在的话,并且我们只为读写打开文件的话,那么文件长度将会截断为0。也就是文件长度会变为0,相当于我们将文件内容全部删去再重新写入。

O_NOBLOCK:指定文件为非阻塞IO,正常情况下在我们写入文件的时候,我们都等待写入完成之后再返回。如果指定此flag,那么将直接返回,而不管是否写入完成。

O_SYNC:每一次写操作完成之前都会更新文件的属性。

O_RSYNC:等待任何对文件同一部分未决写操作完成。(这个标志和下面一个标志不是很理解,如果理解的话,请在评论区讨论。最好是能有一个实际的例子)。

O_DSYNC:每次写等待物理操作完成。仅当文件属性根棍以反映文件数据变化时,此标志才会影响文件属性。

open函数返回的文件描述符一定是当前可用的最小文件描述符。

文件名和路径名的截断:

这个概念我简单介绍一下,就是我们输入打开文件的路径名的时候,由于我们输入的太长了,我们无法识别那么长的路径名,就会保留其中的一部分。这样就无法识别出具体

的打开的文件了(我们保存这部分路径名可能与其他文件的路径名重名)。

我们这里可以使用pathconf获取一些路径名长度,我发现我的机器长度限制是4096。

creat:创建文件

函数原型:create(const char *pahtname, mode_t mode);

此函数和open函数是有雷同的部分,不做了解。

close:关闭打开的文件描述符(就是我们打开一个文件一定会用到一些资源去管理这个打开的文件,那么这里关闭就是释放这些被栈用的资源)

函数原型:int close(int filedes);

lseek函数:更改当前文件的偏移量,也就是我们从文件哪里开始读写文件。

当我们打开一个文件或者创建一个新的文件,文件偏移量默认为0。也就是说从文件开始位置读写文件。同时我们也可以用lseek函数将当前文件偏移量定位到文件指定的置为。

函数原型:lseek(int filedes, off_t offset, int whence);

lseek_64(int filedes, off64_t offset, int whence);

filedes:是文件描述符。

offset:是偏移的大小。(可以为正,也可以为负)

关于off_t是否够用的问题,我在这里简单讲解一下。 但是我们看函数原型的时候,有32位和64位两种文件偏移量。如果是32位那么文件偏移量最大时2的32次方减一,64位也是如此。

32位的文件偏移量最大时2TB(2^31-1字节),64位文件最大偏移量2^33TB,应该足够使用了。如果是不同的平台会用不同的大小,这里只列举了32位和64位。具体实现还是靠硬件,

这里就不逐一列举了。

whence:是指定位置开始进行便宜。

SEEK_SET:即从文件开头为只进行便宜。

SEEK_CUR:从当前文件位置进行便宜。

SEEK_END:从文件末尾位置开始便宜。

同时lseek也可以测试文件是否设置偏移量,如果文件描述符引用的是一个管道、套接字,则lseek返回-1.

read函数:从文件中读取指定字节数目的数据。

函数原型:ssize_t read(int filedes, void *buf, size_t nbytes);

filedes:文件描述符。

buf:读取到字节存放的缓冲区。

nbytes:读取多少个字节。

返回值:实际读取到的字节数。

实际读取到字节数可能小于我们要求读取的字节数。(eg,当前文件位置到文件末尾还有三十个可读的字节,但是我们要求读取200个字节,这就返回30个字节。但是在下次读取的时候,

返回30个字节。还有从网络中读取数据,有些时候可能由于网络延迟读取小于我们读取到的字节数)。

write函数:向文件中写入指定字节数的数据。

函数原型:ssize_t write(int filedes, const void* buf, size_t nbytes);

同上,buf是写入字节存储位置。nbytes是写入的字节数。返回值是实际写入的字节数。

同时,如果我们指定了O_APPEND,每次我们写入之前都将文件偏移量设置为文件末尾的位置。

文件共享:

在介绍文件共享之前,我们先介绍一下内核是使用什么结构表示打开的文件的。

内核使用三种数据结构表示打开的文件。

(1)每个进程都有一个记录项,记录项抱哈一张打开的文件描述符表,每个文件描述符占用一项。我们在进程中使用一个整型记录这个文件项的位置。

每个文件描述符相关联的有两部分:文件标志,指向文件表的指针。

(2)对于打开的文件内核维持一张文件表。

文件表中有文件状态标志、当前文件的偏移量和v节点指针。

(3)每个打开的文件都有一个v节点。

v点包含了文件类型和对该文件各种操作的函数指针。同时还有i节点信息,当前文件的长度。

关于这些内容了解即可。如下图所示,可以很好的理解这三者之间的关系。

61437def3c4f

file

同时,当我们两个进程同时打开一个文件的时候,其结构是下图这样的。

61437def3c4f

file

根据上述内容可以理解,如果是多个进程同时向一个文件中写入数据的时候,可能产生预期不到的结果。如果想要避免这种问题,就得理解原子操作相关的概念。

原子操作:

概念:不受其他影响的独立完成一个操作的全过程。如下程序,我们向一个文件尾部添加内容。

lseek(fd, SEEK_END, 0);

write(fd, buf, size);

如果是一个程序,那么我们这样写没什么问题。但是如果是两个独立的进程,那么这样写可能就麻烦了(如两个进程分别是进程A和进程B,

如果进程A需要写100字节,进程B需要写入1000字节。那么可能在进程A写入50字节的时候进程B开始写入,并且进程B写入1000字节。那么这是进程A写入就会覆盖

进程B写入开头的50字节,这样记录的信息可能就出现了损坏)。如下图所示,两个文件向一个文件写入内容。

61437def3c4f

file

dup和dup2函数:复制文件描述符。

函数原型: int dup(int filedes);

int dup2(int filedes, int filedes2);

前一个函数返回一个新的文件描述符,这个新的文件描述符指向的文件表项和filedes文件描述符指向的文件表项是同一个(返回的文件描述符一定是当前进程表项中最小的文件描述符)。

第二个函数使用指定的文件描述符去复制filedes文件描述符,如果filedes2已经使用,则先关闭filedes2,然后在复制filedes。

参考下述图片,去理解复制一个文件描述符的意义。

61437def3c4f

file

前面曾经提到过IO缓冲区,当我们使用缓冲区进行向磁盘写入数据的时候,有些事后我们可能无法及时将数据写入到磁盘中。当我们的机器意外出现事故的时候,我们可能丢失数据。

还有就是一些数据库需要及时的保存,因此、我们需要让写这个操作及时进行,下面有三个函数会将数据及时写入到磁盘中。

函数原型:int fsync(int filedes);将指定文件描述符数据写入到磁盘中,这里是等待写操作结束返回。

int sync();将所有修改过的缓冲区排入写操作队列。

int fdatasync(int filedes);将数据写入到磁盘的同时还将文件属性更新。

前面曾经文件的一些性质,这里呢有个函数可以通过文件描述符改变打开文件的一些性质。

函数原型:int fcntl(int filedes, int cmd, ...);

(1):复制现有的描述符。

(2):获得/设置文件描述符标志。

(3):获得/设置文件状态标志。

(4):获得/设置异步I/O所有权(异步IO是通过信号的方式实现的,在讲解套接字部分会有所讲解)。

(5):获得/设置记录所

参看man手册,我们会发现如下许多cmd。

F_DUPFD:复制一个新的文件描述符。

F_GETFD/F_SETFD:获得和设置文件描述符标记。当前的标记只有一种,就是FD_CLOEXEC(在执行exec函数的时候关闭该文件描述符)。

F_GETFL/F_SETFL:获得和设置文件状态标记。文件状态标记在前面除了O_DSYNC和O_SYNC都可以改变。(eg:读,写,追加等等)我这里参考的是我这个版本的man手册,

如果想要了解请参考你的版本man手册。

F_GETOWN/F_SETOWN:获得和设置异步IO所有权。在学习套接字部分将会了解到这里。

关于记录锁,使用如下图的结构。

F_SETLK/F_SETLKW/F_GETLK:请求或者释放一个锁/设置一个文件锁/返回一个锁(如果没有锁,那么在l_type字段设置为F_UNLCK)。

在学习多进程的时候,我会写一个实验关于文件锁的,就是多个进程读写文件,使用文件的记录锁实现文件共享。

![file](https://upload-images.jianshu.io/upload_images/25648327-668117bf6249824b.png)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值