关于c:Linux学习文件IO不带缓冲区原子操作概念

32次阅读

共计 4367 个字符,预计需要花费 11 分钟才能阅读完成。

在后面的文章咱们介绍过不带缓冲区的 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 节点信息,以后文件的长度。对于这些内容理解即可。如下图所示,能够很好的了解这三者之间的关系。


同时,当咱们两个过程同时关上一个文件的时候,其构造是下图这样的。


根据上述内容能够了解,如果是多个过程同时向一个文件中写入数据的时候,可能产生预期不到的后果。如果想要防止这种问题,就得了解原子操作相干的概念。原子操作:概念:不受其余影响的独立实现一个操作的全过程。如下程序,咱们向一个文件尾部增加内容。lseek(fd, SEEK_END, 0);
        write(fd, buf, size);
        如果是一个程序,那么咱们这样写没什么问题。然而如果是两个独立的过程,那么这样写可能就麻烦了(如两个过程别离是过程 A 和过程 B,如果过程 A 须要写 100 字节,过程 B 须要写入 1000 字节。那么可能在过程 A 写入 50 字节的时候过程 B 开始写入,并且过程 B 写入 1000 字节。那么这是过程 A 写入就会笼罩
        过程 B 写入结尾的 50 字节,这样记录的信息可能就呈现了损坏)。如下图所示,两个文件向一个文件写入内容。

dup 和 dup2 函数:复制文件描述符。函数原型:int dup(int filedes);
                                            int dup2(int filedes, int filedes2);
前一个函数返回一个新的文件描述符,这个新的文件描述符指向的文件表项和 filedes 文件描述符指向的文件表项是同一个(返回的文件描述符肯定是以后过程表项中最小的文件描述符)。第二个函数应用指定的文件描述符去复制 filedes 文件描述符,如果 filedes2 曾经应用,则先敞开 filedes2,而后在复制 filedes。参考下述图片,去了解复制一个文件描述符的意义。

 后面已经提到过 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](/img/bVcNPlq)




    
 

正文完
 0