• 文件的读取写入


    一、read系统调用

    一旦有了与一个打开文件描述相关连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节 


    函数原型:
    ssize_t read(int fd, void *buf, size_t count);
    参数:
    fd :想要读的文件的文件描述符
    buf : 指向内存块的指针,从文件中读取来的字节放到这个内存块中
    count : 从该文件复制到buf中的字节个数
    返回值:
    如果出现错误,返回-1;读文件结束,返回0;否则返回从该文件复制到规定的缓冲区中的字节数


    二、write系统调用

    用write()系统调用将数据写到一个文件中 

    函数原型:
    ssize_t write(int fd, const void *buf, size_t count);
    函数参数:
    fd:要写入的文件的文件描述符
    buf: 指向内存块的指针,从这个内存块中读取数据写入 到文件中
    count: 要写入文件的字节个数
    返回值:如果出现错误,返回-1;如果写入成功,则返回写入到文件中的字节个数


    三、ioctl 函数

    ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。

    #include <sys/ioctl.h>

    int ioctl(int d, int request, ...);
    d是某个设备的文件描述符。request是ioctl的命令,可变参数取决于request,通常是一个指向变量或结构体的指针。若出错则返回-1,若成功则返回其他值,返回值也是取决于request。
    以下程序使用TIOCGWINSZ命令获得终端设备的窗口大小。

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #include <stdio.h>
    #include <stdlib.h
    #include <unistd.h>
    #include <sys/ioctl.h
    int main(void)
    {
        struct winsize size;
        if (isatty(STDOUT_FILENO) == 0)
            exit(1);
        if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0)
        {
            perror("ioctl TIOCGWINSZ error");
            exit(1);
        }
        printf("%d rows, %d columns ", size.ws_row, size.ws_col);
        return 0;
    }

    在图形界面的终端里多次改变终端窗口的大小并运行该程序,观察结果。


    四、文件的随机读写

    到目前为止的所有文件访问都是顺序访问。这是因为所有的读和写都从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问作好准备。
    有个文件偏移这样的机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件偏移值改变到有关的位置,它将迫使下一次read()或write()发生在这一位置。(除非文件打开时标志有 O_APPEND,在这种情况下,任何write调用仍将发生在文件结束处)

    lseek系统调用:

    功能说明:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位,这取决于 lseek() 函数中指定的位置
    函数原型:off_t lseek (int  fd,    off_t offset,   int base);

    函数参数:

    fd:需要设置的文件描述符

    offset:偏移量

    base:偏移基位置

    返回值:返回新的文件偏移值

    base 表示搜索的起始位置,有以下几个值:(这些值定义在<unistd.h>)

    base 文件位置

    SEEK_SET 从文件开始处计算偏移
    SEEK_CUR 从当前文件的偏移值计算偏移
    SEEK_END 从文件的结束处计算偏移


    注意:管道和socket是不能lseek的,否则返回ESPIPE错误(Invalid seek)。


    示例程序如下:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
     
    /*************************************************************************
        > File Name: file_cp.c
        > Author: Simba
        > Mail: dameng34@163.com
        > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
     ************************************************************************/
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>

    #define ERR_EXIT(m) 
        do { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)

    int main(int argc, char *argv[])
    {
        int infd;
        int outfd;
        if (argc != 3)
        {
            fprintf(stderr, "Usage %s src dest ", argv[0]);
            exit(EXIT_FAILURE);
        }

        infd = open(argv[1], O_RDONLY);
        if (infd == -1)
            ERR_EXIT("open src error");
        if ((outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
            ERR_EXIT("open dest error");

        char buf[1024];
        ssize_t nread;
        while ((nread = read(infd, buf, 1024)) > 0)
            write(outfd, buf, nread); // 可以调用fsync同步内核缓冲区的数据到磁盘文件
        // 或者打开文件时标志为O_SYNC
        close(infd);
        close(outfd);
        /********************************************************************************************/

        int fd = open("test.txt", O_RDONLY);
        if (fd == -1)
            ERR_EXIT("open error");
        char buf2[1024] = {0};
        int ret = read(fd, buf2, 5);
        if (ret == -1)
            ERR_EXIT("read error");
        ret = lseek(fd, 0, SEEK_CUR); // 从当前位置偏移0个字节
        if (ret == -1)
            ERR_EXIT("lseek");
        printf("current offset=%d ", ret);

        fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
        if (fd == -1)
            ERR_EXIT("open error");
        write(fd, "ABCDE", 5);
        ret = lseek(fd, 1012 * 1024 * 1024, SEEK_CUR);
        if (ret == -1)
            ERR_EXIT("lseek error");
        write(fd, "hello", 5);
        /* 中间的空字符不占用磁盘空间,如ls -lh hole.txt 与 du -h hole.txt
         * 看到的文件大小不一样*/
        close(fd);

        return 0;
    }


    程序的前部分实现了拷贝文件的基本功能,后部分示例了lseek的用法,因为有些程序需要输入参数,且讨论程序输出结果也比较繁琐,比如上述关于hole.txt文件的实际大小问题,大家可以自己拷贝程序进行测试,印象也更加深刻。

    struct stat结构体中的文件长度对应st_size字段,而文件使用的块大小对应st_blksize字段,占用块数对应st_blocks字段。 大部分情况下面,st_size和st_blksize*st_blocks应该是很接近的,除非一种情况就是文件空洞。 一般对应于空洞文件来说,st_size可能很大,而实际占用磁盘空间却很少。


    参考:《APUE》

  • 相关阅读:
    [C#]LDAP验证用户名和密码
    如何为 Go 设计一个通用的日志包
    使用 Go 的 struct tag 来解析版本号字符串
    Referrer Policy 介绍
    《计算机操作系统》MOOC笔记1-计算机系统概论
    C语言的Bit fields
    【转】gcc编译优化---likely()与unlikely()函数的意义
    三向切分的快速排序
    Codeforces Round #334 (Div. 2) C. Alternative Thinking
    基于相邻元素交换的排序算法的下界
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8477118.html
Copyright © 2020-2023  润新知