4.lseek函数及共享文件



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
 * 项目: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;                                // fd即file descriptor,文件描述符
    int ret = -1;                               // 用于接收read返回值,判断是否读文件成功
    char buf[100] = {0};                        // 构建缓冲区存放从文件读出的内容
    char bufwrite[20] = "linux is great";       // 构建缓冲区存放要写入文件的内容

#if 0
    // 打开test.txt文件,若该文件不存在则创建(权限666),若该文件存在则报错
    fd = open("test.txt", O_RDWR | O_CREAT | O_EXCL, 0666);
#else
    // 打开test.txt文件,若文件不存在则报错
    fd = open("test.txt", O_RDONLY);
#endif
    if (-1 == fd)                               // 判断文件打开是否成功,也可这样写if (fd < 0)
    {
        //printf("open file error.\n");
        perror("open file error");              // 通过perror捕捉errno错误信息并打印输出
        //return -1;                            // 使用return或exit退出进程或程序
        exit(-1);
    }
    else
    {
        printf("open file sucess. fd = %d.\n", fd);
    }

#if 1
    ret = lseek(fd, 3, SEEK_SET);               // 使用lseek函数从文件开始位置开始向后移动3个字符位置
    printf("lseek, ret = %d.\n", ret);
#endif

#if 0
    ret = write(fd, bufwrite, strlen(bufwrite));    // 写内容到文件中
    if (ret < 0)                                    // 判断内容是否成功写入文件
    {
        //printf("write file errot.\n");
        perror("write file errot");
        //return -1;
        exit(-1);
    }
    else
    {
        printf("write %d bytes to file.\n", ret);
        printf("the content of write is [%s].\n", bufwrite);
    }
#endif

#if 1
    ret = read(fd, buf, sizeof(buf));   // 读取文件内容
    if (ret < 0)                        // 判断读取文件是否成功
    {
        //printf("read file error.\n");
        perror("read file error");
        //return -1;
        exit(-1);
    }
    else
    {
        printf("read %d bytes actual from file.\n", ret);
        printf("the content of read is [%s].\n", buf);
    }
#endif

    close(fd);                          // 关闭文件

    //return 0;
    exit(0);
}

4.lseek_callen
 /*
 * 公司:XXXX
 * 作者: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 callen(const char *pathname)
{
    int fd = -1, ret = -1;

    fd = open(pathname, O_RDONLY);
    if (-1 == fd)
    {
        perror("open file error");
        exit(-1);
    }

    // lseek将文件指针移动到末尾,返回值即文件指针距离文件开头的偏移量,即文件的长度
    ret = lseek(fd, 0, SEEK_END);
    return ret;
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("usage: %s pathname.\n", argv[0]);
        exit(-1);
    }

    printf("The size of %s is %d.\n", argv[1], callen(argv[1]));

    exit(0);
}

4.lseek_empty
/*
 * 公司:XXXX
 * 作者:Rston
 * 项目:lseek函数及共享文件
 * 功能:使用lseek构建1个空洞文件
 */
#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;                                // fd即file descriptor,文件描述符
    int ret = -1;                               // 用于接收read返回值,判断是否读文件成功
    char buf[100] = {0};                        // 构建缓冲区存放从文件读出的内容
    char bufwrite[20] = "linux is great";       // 构建缓冲区存放要写入文件的内容

    // 打开test.txt文件,若该文件不存在则创建(权限666),若该文件存在则报错
    fd = open("test.txt", O_RDWR | O_CREAT | O_EXCL, 0666);
    if (-1 == fd)                               // 判断文件打开是否成功,也可这样写if (fd < 0)
    {
        perror("open file error");              // 通过perror捕捉errno错误信息并打印输出
        exit(-1);
    }

#if 1
    ret = lseek(fd, 10, SEEK_SET);              // 使用lseek函数从文件开始位置开始向后移动10个字符位置
    printf("lseek, ret = %d.\n", ret);          // 即构建了1个空洞文件
#endif

#if 1
    ret = write(fd, bufwrite, strlen(bufwrite));// 写内容到文件中
    if (ret < 0)                                // 判断内容是否成功写入文件
    {
        perror("write file errot");
        exit(-1);
    }
#endif

    close(fd);                                  // 关闭文件

    exit(0);
}

4.multi_read
/*
 * 公司:XXXX
 * 作者:Rston
 * 项目:lseek函数及共享文件
 * 功能:1个进程重复打开同一个文件读取
 */
#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 fd1 = -1, fd2 = -1;                     // fd即file descriptor,文件描述符
    int ret = -1;                               // 用于接收read返回值,判断是否读文件成功
    char buf[100] = {0};                        // 构建缓冲区存放从文件读出的内容

    fd1 = open("test.txt", O_RDONLY);
    fd2 = open("test.txt", O_RDONLY);
    if ((-1 == fd1) || (-1 == fd2))             // 判断文件打开是否成功,也可这样写if (fd < 0)
    {
        perror("open file error");              // 通过perror捕捉errno错误信息并打印输出
        exit(-1);
    }

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ret = read(fd1, buf, 2);                // 读取文件内容
        if (ret < 0)                            // 判断读取文件是否成功
        {
            perror("read file error");
            exit(-1);
        }
        else
        {
            printf("the content of read from fd1 is [%s].\n", buf);
        }

        sleep(1);

        memset(buf, 0, sizeof(buf));
        ret = read(fd2, buf, 2);                // 读取文件内容
        if (ret < 0)                            // 判断读取文件是否成功
        {
            perror("read file error");
            exit(-1);
        }
        else
        {
            printf("the content of read from fd2 is [%s].\n", buf);
        }
    }

    close(fd1);                                 // 关闭文件
    close(fd2);

    exit(0);
}

4.multi_write
/*
 * 公司:XXXX
 * 作者:Rston
 * 项目:lseek函数及共享文件
 * 功能:1个进程重复打开同一个文件写入
 */
#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 fd1 = -1, fd2 = -1;                     // fd即file descriptor,文件描述符
    int ret = -1;                               // 用于接收read返回值,判断是否读文件成功

    // 若文件存在则原来的内容消失,若文件不存在则以权限666创建该文件
#if 0
    fd1 = open("test.txt", O_RDWR | O_TRUNC | O_CREAT , 0666);              // 分别写
    fd2 = open("test.txt", O_RDWR | O_TRUNC | O_CREAT , 0666);
#else
    fd1 = open("test.txt", O_RDWR | O_TRUNC | O_CREAT | O_APPEND, 0666);    // 接续写
    fd2 = open("test.txt", O_RDWR | O_TRUNC | O_CREAT | O_APPEND, 0666);
#endif
    if ((-1 == fd1) || (-1 == fd2))             // 判断文件打开是否成功,也可这样写if (fd < 0)
    {
        perror("open file error");              // 通过perror捕捉errno错误信息并打印输出
        exit(-1);
    }

    while (1)
    {
        ret = write(fd1, "ab", 2);              // 写内容到文件中
        if (ret < 0)                            // 判断内容是否成功写入文件
        {
            perror("write file errot");
            exit(-1);
        }
        else
        {
            printf("the content of write from fd1 is [%s].\n", "ab");
        }

        sleep(1);

        ret = write(fd2, "cd", 2);              // 写内容到文件中
        if (ret < 0)                            // 判断内容是否成功写入文件
        {
            perror("write file errot");
            exit(-1);
        }
        else
        {
            printf("the content of write from fd2 is [%s].\n", "cd");
        }
    }

    close(fd1);                                 // 关闭文件
    close(fd2);

    exit(0);
}

时间: 2024-10-05 05:24:31

4.lseek函数及共享文件的相关文章

Linux文件I/O编程(二)lseek函数

文件I/O编程处理open.read.write.close,等必要函数对文件进行读写操作外,lseek.fcntl也是I/O编程很重要的函数. lseek函数 lseek函数主要用来移动当前读写位置,第一个参数是文件描述符fd,第二个参数是偏移距离,第三个参数是文件内容指针宏,分别有SEEK_SET(文件头),SEEK_CURR(当前位置),SEEK_END(文件尾部).该函数的执行成功返回文件当前位置的偏移量,若是失败返回-1. 改变文件位置指针很重要,如果处理不好会出现读写文件时读取错误数

文件IO详解(七)---lseek函数详解

lseek函数用来设置当前文件偏移量. ====================================================== 函数原型: 函数参数: fd:要操作的文件描述符 offset:基于whence参数的偏移量 whence:参考点位置 返回值: 调用成功时返回当前相对于文件开头的偏移量,以字节为单位 调用失败时返回 -1,并修改errno的值 ======================================================= when

lseek函数

lseek函数用于设置文件偏移量. 每个打开的文件都有一个与其相关联的“当前文件偏移量”(current file offset).它通常是一个非负整数,用以度量从文件开始处计算的字节数.通常,读写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数.按系统默认的情况,当打开一个文件时,除非制定O_APPEND选项,否则该偏移量被设置为0. #include<unistd.h> off_t lseek(int filedes, off_t offset, int whence); 对参数

Linux ---lseek() 函数

每个打开的文件都有一个与其相关连的"当前文件偏移量"( current file offset ).它通常是一个非负整数,用以度量从文件开始处计算的字节数.通常,读.写操作都从当前文件偏移量开始,并使偏移量增加所读写的字节数.按系统默认情况,当打开一个文件时,除非指定 O_APPEND 选项,否则该偏移量设置为 0. lseek调用仅将当前的文件偏移量记录在内核中,它并不引起任何的 I/O 操作.然后,该偏移量用于下一个读或写操作. 函数原型 #include<unistd.h&

lseek()函数

lseek()有个特殊的用途,确定文件是常规文件还是设备.<pre lang="c" escaped="true">off_t currpos;ourrpos = lseek(fd, 0, SEEK_CUR);if (ourrpos == -1){ printf("this is drive file");}</pre>这种方法用来确定文件或者设备是否可以设置偏移量,常规文件都可以设置偏移量,而设备一般是不可以设置偏移量的

【UNIX环境高级编程】文件 IO 操作 - 基础函数 open close creat lseek write read 详解

博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271 一. 文件打开关闭操作相关函数介绍 1. open 函数 (1) open 函数简介 open 函数解析 : -- 函数定义 : #include <fcntl.h> int open(const char *path, int oflag, ...); -- 函数作用 : 打开或者创建一个文件; -- 返回值 : 打开文件成功, 返回文件描述符; 如果失败, 返回

lseek()函数与fseek()函数详解

C语言lseek()函数:移动文件的读写位置 头文件:  #include <sys/types.h>  #include <unistd.h> 定义函数: off_t lseek(int fildes, off_t offset, int whence); 函数说明: 每一个已打开的文件都有一个读写位置, 当打开文件时通常其读写位置是指向文件开头, 若是以附加的方式打开文件(如O_APPEND), 则读写位置会指向文件尾. 当read()或write()时, 读写位置会随之增加,

文件I/O函数(open,read,write,lseek,close)

大多数unix文件I/O只需要用到5个函数:open,read,write,lseek,close.这些函数都为不带缓存的I/O,不带缓存指的是每个read和write都调用内核中的一个系统调用.这些函数使用时要用到三个头文件:sys/types.h,sys/stat.h,fcntl.h #include<fcntl.h> #include<types.h> #include<sys/stat.h>/*此头文件里面定义了mode标志*/ open函数:int open(

linux文件函数-lseek

linux文件函数-write 一 定位文件 函数名:lseek 函数原形: off_t lseek(int fd, off_t offset, int whence) 函数功能:移动文件指针 所属头文件: #include<sys/types.h> #include<unistd.h> 返回值: 成功:返回移动后的文件指针到文件开头的字节数即偏移量 失败:返回-1 参数说明 fd:要定位的文件的描述符 offset:偏移量,整数向后移,负数向前移 whence:文件指针的位置 S