• 4、linux下文件的通用操作方法


    文件描述符仅在同一个进程中有效,即不同进程的文件描述符,同一个值很可能不是描述的不是同一个设备或普通文件。在linux系统中有三个已经分配的文件描述符,他们分别是stdin、stdout、stderr,他们的文件描述符的值分别为 0、1、2

    打开、创建文件----open,creat函数(不是C语言函数,可以直接在shell中调用)

    open函数用于打开一个已经存在的文件或者创建一个新文件,create函数用于创建一个新文件。

    open函数原型:

    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
    
    • 1
    • 2

    在使用这些函数的时候需要包含头文件sys/types.h sys/stat.h fcntl.h

    open函数用于打开一个文件并返回一个文件描述符,如果打开出错就返回-1。

    flags用于用于设置文件打开后允许的操作方式,分别用O_RDONLY, O_WRONLY, O_RDWR。在打开的时候必须指定上述三种模式之一。除此之外还有一些可选的参数:

    O_APPEND 追加

    O_CREAT(不是CREATE) 如果文件不存在则创建

    O_EXCL 查看文件是否存在

    O_TRUNC 将文件长度截为0

    多个选项之间用 |连接

    mode是指定创建文件时文件的权限的,所以必须和O_CREAT一起使用。

    creat函数原型:

    int creat(const char *pathname, mode_t mode);
    
    • 1

    关闭文件close函数

    使用close函数需要包含的头文件:unistd.h

    close函数原型:

    int close(int fd);
    
    • 1

    关闭一个文件描述符,成功返回0, 错误返回-1。

    打开文件后必须关闭,如果进程中没有关闭文件,则进程退出时系统会自动关闭打开的文件,但是文件描述符的数量是有限的,如果频繁的打开而又忘记关闭,则可能导致因为文件描述符不够用而打开文件失败。

    读取文件 read函数

    用read函数从打开的文件中读取数据,用户可对读入的数据进行操作。

    头文件:unistd.h

    函数原型:

    ssize_t read(int fd, void *buf, size_t count);
    
    • 1

    从fd对应的文件中读取count个字符到buf中。

    执行成功,返回读取的字节数,失败返回-1,如果已经达到文件的末尾,返回 0。

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main(int argc, char *argv[]) {
        char filename[] = "test.txt";
        int fd = open(filename, O_RDONLY);
        if(fd == -1) {
            printf("打开失败\n");
        }
        else {
            //输出文件中所有的数据
            char buf[10];
            while(read(fd, buf, 10) != 0) {
                printf("%s",buf);
                memset(buf, 0, 10);
            }
            puts("");
            close(fd);
        }
    }
    
    • 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

    写文件write函数

    write函数向打开的文件中写入数据。

    头文件:unistd.h

    函数原型:

    ssize_t write(int fd, const void *buf, size_t count);
    
    • 1

    向fd打开的文件中写入从buf开始的长度为count个字节的信息。

    操作成功返回写入的字节数,操作失败返回-1。

    写操作并不能保证将数据成功写入磁盘,这在异步操作中经常出现,write函数经常将函数写入缓冲区,在合适的时机由系统写入实际的设备,可以调用fsync函数,显式将输入写入设备。
    unistd.h
    int fsync(int fd);
    
    • 1
    • 2
    • 3
    
    
    • 1

    文件偏移 lseek函数

    lseek函数可以设置文件偏移量的位置

    头文件sys/types.h unistd.h

    函数原型:

    off_t lseek(int fd, off_t offset, int whence);
    
    • 1

    这个函数对fd所代表的文件,按照操作模式whence和偏移量大小offset,重新设定文件的偏移量。操作成功返回新的偏移量的值,操作失败返回-1,由于文件偏移量可以为负值,所以判断lseek是否成功时不要用小于0判断,要用等于-1判断。

    whence表示操作的模式,offset表示偏移的值。offset可以为负值

    • 如果whence为SEEK_SET ,则offset为相对文件开始处的值,即该文件偏移量设为距文件开始处offset个字节。
    • 如果whence为SEEK_CUR,则offset为相对当前位置的值,即该文件的偏移量设置为当前偏移量位置加offset
    • 如果whence为SEEK_END, 则offset为相对文件末尾的值,即该文件的偏移量设置为末尾偏移量加offset

    可以用SEEK_CUR模式下偏移0获取当前的偏移量

    #include
    #include
    #include
    #include
    #include
    #include
    
    int main(int argc, char *argv[]) {
        int fd = open("test.txt", O_RDWR | O_TRUNC);
        if(fd == -1) {
            puts("打开文件失败");
        }
        else {
            write(fd, "我是罗新1", strlen("我是罗新1"));
            int offset = lseek(fd, 0, SEEK_SET);
            if(offset == -1) {
                puts("改变偏移失败");
            }
            else write(fd, "我是罗新2", strlen("我是罗新2"));
            offset = lseek(fd, 0, SEEK_END);
            if(offset == -1) {
                puts("改变偏移失败");
            }
            else write(fd, "我是罗新3", strlen("我是罗新3"));
    
            close(fd);
        }
    }
    
    • 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

    最终文件中的内容是我是罗新2我是罗新3,我是罗新1被覆盖掉了。

    使用lseek造成空洞

    当设置文件的偏移量超过文件的大小的时候,下一次的写入仍会在指定偏移量处写入,中间跳过的空间用\0填充。

    获取文件状态 fstat函数

    有时对文件操作的目的不是读写文件,而是要获得文件的状态,例如获得目标文件的大小、权限、时间等信息。

    stat、fstat、lstat函数可以获得文件的状态。

    头文件sys/types.h sys/stat.h unistd.h

    函数原型:

    int stat(const char *pathname, struct stat *statbuf);
    int fstat(int fd, struct stat *statbuf);
    int lstat(const char *pathname, struct stat *statbuf);
    lstat相比于stat:lstat如果文件是一个符号链接的话返回的是符号链接的状态而不是符号链接关联的文件的状态。
    
    • 1
    • 2
    • 3
    • 4

    函数的作用是将指定文件的描述信息写入stat型的结构体 statbuf中,成功执行时返回0 ,失败是 返回 -1。

    stat结构体如下:

    struct stat {
                   dev_t     st_dev;         /* ID of device containing file 文件所处设备的设备ID*/
                   ino_t     st_ino;         /* Inode number 文件的inode值*/
                   mode_t    st_mode;        /* File type and mode 文件类型以及打开模式*/
                   nlink_t   st_nlink;       /* Number of hard links 文件硬链接数*/
                   uid_t     st_uid;         /* User ID of owner 文件所有者的用户ID*/
                   gid_t     st_gid;         /* Group ID of owner 文件所有者的组ID*/
                   dev_t     st_rdev;        /* Device ID (if special file) */
                   off_t     st_size;        /* Total size, in bytes 以字节统计的大小*/
                   blksize_t st_blksize;     /* Block size for filesystem I/O 块大小*/
                   blkcnt_t  st_blocks;      /* Number of 512B blocks allocated 占用块的数量*/
    
                   /* Since Linux 2.6, the kernel supports nanosecond
                      precision for the following timestamp fields.
                      For the details before Linux 2.6, see NOTES. */
    
                   struct timespec st_atim;  /* Time of last access 最后访问时间*/
                   struct timespec st_mtim;  /* Time of last modification 最后修改时间*/
                   struct timespec st_ctim;  /* Time of last status change 状态最后改变的时间*/
    
               #define st_atime st_atim.tv_sec      /* Backward compatibility */
               #define st_mtime st_mtim.tv_sec
               #define st_ctime st_ctim.tv_sec
               };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    文件空间映射 mmap、munmap函数

    mmap函数用来将文件或者设备空间映射到内存中,可以通过对映射后的内存空间的存取来获得与存取文件一致的控制方式,不必再使用read、write函数。简单的说就是将文件映射到内存中,内存比磁盘快。

    头文件sys/mman.h

    函数原型:

    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    
    • 1

    返回映射后的地址。

    addr:要映射到的内存地址

    length:映射长度

    prot:映射区的保护方式,prot的值是一个组合值,可以选一个或多个。

    • PROT_EXRC表示映射区可执行
    • PROT_READ表示映射区可读取
    • PROT_WRITE表示可写
    • PROT_NONE表示映射区不能存取

    flags:用于设置映射对象的类型选项和是否可以对映射对象进行操作(读写等),这个参数和open函数中的含义类似,flags也是一个组合值。

    • MAP_FIXED
    • MAP_SHARED 共享的映射区域,映射区域允许其他进程访问,对区域写入的数据会写入原文件中
    • MAP_PRIVATE 对映射文件写入时会产生一个映射文件的复制,即写入复制(copy on write),而读操作不会影响此复制,对此映射区的修改不会写回原文件
    • MAP_ANONYMOUS:建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法与其他进程共享。
    • MAP_DENYWRITE:对文件的写入操作会被禁止。只能通过对映射区操作的方式操作,不允许直接对文件进行操作。
    • MAP_LOCKED : 对映射区锁定,此区域不会被虚拟内存重置。

    flags必须为shared或者private之一

    fd:要映射文件的描述符

    offset:文件的偏移

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    
    int main(int argc, char *argv[]) {
        int fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRWXU);
        if(fd == -1) {
            puts("打开文件失败");
        }
        else {
            char *addr = NULL;
            //把文件映射到内存中
            //下面的代码将文件的长度扩大到20
            lseek(fd, 19, SEEK_SET);
            write(fd,"a",1);//必须要写一个字符,不能全空
            addr = (char*)mmap(NULL, 20, PROT_WRITE, MAP_SHARED, fd, 0);
            //操作内存
            strcpy(addr,"罗新");
            //取消映射
            munmap(addr, 20);
        }
    }
    
    • 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

    munmap用于取消mmap函数的映射关系。

    int munmap(void *addr, size_t length);
    
    • 1

    文件属性 fcntl函数

    fcntl用于获得和改变已经打开文件的性质。

    头文件unistd.h fcntl.h

    函数原型:

    int fcntl(int fd, int cmd, ... /* arg */ );
    
    • 1

    文件输入输出控制 ioctl函数

    ioctl是input output control的缩写

    ioctl函数通过对文件描述符发送特定的命令来控制文件描述符代表的设备。

    函数原型:

    #include 
    
    int ioctl(int fd, unsigned long request, ...);
    
    • 1
    • 2
    • 3

    通常情况下函数出错返回-1,成功则返回0或者大于1的值

    使用ioctl像其他的系统调用一样:打开文件、发送命令、查询结果。ioctl函数像一个杂货铺,对设备的控制通常都通过这个函数来实行。

  • 相关阅读:
    Java的IO流-序列化流
    SAS学习1(总体介绍以及一些程序例子)
    【逆向】修改LightCycler 96文件Summary日期
    怎样选择适合自己的ITSM软件?
    实现微信小程序无限发消息,实现类似长期订阅的功能。判断用户是否关注公众号。(保姆级教学、建议阅读每一个字~)
    MongoDB聚合运算符:$bsonSize
    ref的使用
    通过配置文件方式配置包含thymeleaf的SpringMVC开发环境
    Linux目录结构和VI/VIM 编辑器
    图片引用功能导致的XSS漏洞
  • 原文地址:https://blog.csdn.net/m0_45972156/article/details/126432090