4.lseek函数及共享文件

本文详细介绍了Linux中的lseek函数,用于访问文件指针并改变其位置。内容包括lseek函数在动态文件操作中的作用,如何计算文件长度和创建空洞文件,以及在多线程文件操作中的应用。同时,讨论了文件共享的实现方式,包括O_APPEND标志的作用和原子操作性,并解析了文件描述符的本质和管理。
摘要由CSDN通过智能技术生成

4.1.lseek函数介绍
(1)当我们要对1个文件进行读写时,首先需打开该文件,则我们读写的所有文件都是动态文件;动态文件在内存中就是以文件流的形式存在的。
(2)文件流很长,里面有很多个字符,我们需要确定当前正在操作的是哪个位置;GUI模式下的软件用光标来标识当前正在操作文件流的哪个位置;在动态文件中则通过文件指针(即Vnode结构体中的1个元素)来标识当前正在操作文件流哪个位置;文件指针不能被直接访问,linux系统使用lseek函数来访问该文件指针。
(3)当我们打开1个空文件时,默认情况下文件指针指向文件流的起始位置,则这时候去读写该文件时就是从文件流的起始位置开始的;write和read函数本身自带移动文件指针的功能,所以当我读写了n个字节后,文件指针会自动向后移动n位;如果需要人为的随意更改文件指针,那就只能通过lseek函数了。
(4)read和write函数都是从当前文件指针处开始操作的,所以当我们用lseek显式的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的;则当我们操作空文件时先write写了12字节,然后read时是空的,但是此时我们打开文件后发现12字节确实写进来了。


4.2.计算文件长度和构建空洞文件
(1)linux中并没有1个函数可以直接返回1个文件的长度,但是我们做项目时经常会需要知道1个文件的长度,我们自己利用lseek来写1个函数得到文件长度即可。
(2)空洞文件即该文件中有1段是空的;普通文件中间是不能有空的,因为我们write时文件指针是依次从前到后去移动的,我们不可能绕过前面直接到后面;我们打开某个文件后,用lseek往后跳过1段,再write写入1段,则会构成1个空洞文件。
(3)空洞文件方法对多线程共同操作文件是及其有用的,有时候我们创建1个很大的文件,如果从头开始依次构建时间很长,有1种思路就是将文件分为多段,然后多线程来操作,每个线程负责其中1段的写入。


4.3.重复打开同一文件读取
(1)1个进程中两次打开同一个文件,然后分别读取,1种情况是fd1和fd2分别读,1种情况是接续读;经过实验验证,证明了结果是fd1和fd2分别读。
(2)分别读说明我们使用open两次打开同一个文件时,fd1和fd2所对应的文件指针(fd->文件表指针->文件表->文件指针)是不同的2个独立的指针;文件指针是包含在文件表中的,所以可以看出linux系统的进程中不同fd对应的是不同的独立的文件表。


4.4.重复打开同一文件写入
(1)1个进程中两次打开同一个文件,然后分别写入,1种情况是fd1和fd2分别写,1种情况是接续写;经过实验验证,证明了结果是fd1和fd2分别写。
(2)正常情况下我们有时候需要分别写,有时候又需要接续写,所以这两种本身是没有好坏之分的,关键看用户需求;有时候我们希望接续写而不是分别写,办法就是在open时加O_APPEND标志即可。


4.5.O_APPEND实现原理及原子操作性说明
(1)分别写的内部原理就是2个fd拥有不同的文件指针,并且彼此只考虑自己的位移;但是O_APPEND标志可以让write和read函数内部多做1件事情,即移动自己的文件指针的同时也移动别人的文件指针(即fd1和fd2还是各自拥有1个独立的文件指针,但是这两个文件指针关联起来了,1个动了会通知另1个跟着动)。
(2)原子操作的含义即整个操作一旦开始是不会被打断的,必须直到操作结束其它代码才能得以调度运行,这就叫原子操作;每种操作系统中都有一些机制来实现原子操作,以保证那些需要原子操作的任务可以运行;O_APPEND对文件指针的影响,对文件的读写是原子的。


4.6.文件共享及实现方式
(1)文件共享即同一个文件(即同一个inode,同一个pathname)被多个独立的读写体(即多个文件描述符)同时(一个已打开尚未关闭的同时另一个去操作)操作;文件共享的意义有很多,譬如我们可以通过文件共享来实现多线程同时操作同1个大文件,以减少文件读写时间,提升效率。
(2)文件共享的核心即制造多个文件描述符指向同一个文件;常见的有3种文件共享的情况,第1种是同一个进程中多次使用open打开同一个文件;第2种是在不同进程中去分别使用open打开同一个文件(两个fd在不同的进程中,则两个fd的数字可以相同也可以不同);第3种情况是linux系统提供了dup和dup2两个API来让进程复制文件描述符;分析文件共享时的核心关注点在于确认是分别写/读还是接续写/读。
(3)当两个文件指针分别独立且互不关联->分别写/读;当两个文件指针分别独立且相互关联/两个文件指针相同->接续写/读(见图1)。


4.7.再论文件描述符
(1)文件描述符的本质是1个数字,该数字本质上是进程表中文件描述符表的1个表项,进程通过文件描述符作为index去索引查表得到文件表指针,再间接访问得到该文件对应的文件表。
(2)文件描述符是open系统调用内部由操作系统自动分配的,操作系统规定fd从0开始依次增加;fd也是有最大限制的,在linux的早期版本中(0.11)fd最大是20,所以当时1个进程最多允许打开20个文件;linux中文件描述符表是个指针数组(不是链表),其中fd是index,文件表指针是value。
(3)当我们去open时,内核会从文件描述符表中挑选1个最小的未被使用的数字给我们返回;即如果之前fd已经占满了0-9,那我们下次open得到的一定是10(但是如果上一个fd得到的是9,下一个不一定是10,这是因为可能前面更小的一个fd已经被close释放掉了)。
(4)fd中0、1、2已经默认被系统内核占用了,因此用户进程得到的最小的fd就是3了;当我们运行1个程序得到1个进程时,内部默认已打开3个文件,其对应的fd就是0、1、2;这3个文件分别叫stdin、stdout、stderr,即标准输入、标准输出、标准错误。
(5)标准输入一般对应的是键盘(0这个fd对应的是键盘的设备文件),标准输出一般是LCD显示器(1对应LCD的设备文件);printf函数其实就是默认输出到标准输出stdout上了,fpirntf函数可以指定输出到哪个文件描述符中。


这里写图片描述


4.lseek_example
/*
 * 公司:XXXX
 * 作者:Rston
 * 博客:http://blog.csdn.net/rston
 * GitHub:https://github.com/rston
 * 项目:lseek函数及共享文件
 * 功能:演示lseek函数的基本使用。
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    int fd = -1;                                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值