Linux 操作二:文件映射与文件状态

devtools/2025/1/19 21:38:39/

Linux 操作二:文件映射与文件状态查询

文件映射

mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写数据到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。

在这里插入图片描述

mmap函数:

  • 头文件

    #include <sys/mman.h> 
    
  • 函数原型

    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    //解除映射
    int munmap(void *addr, size_t length);
    
  • 参数说明

    • addr:映射的地址空间首地址,NULL 表示让系统决定;

    • length:地址空间大小

    • prot:映射的地址空间访问方式,必须和文件打开方式匹配

    • flags: 映射的地址空间的访问标记常见的标志位:

      1. MAP_SHARED
        这个选项表示映射的内存区域与文件共享,即对映射内存区域的修改会直接反映到原始文件中。通常用于多个进程共享同一文件的情况。
        • 例如:进程 A 和进程 B 都映射了同一个文件,进程 A 修改了映射区域中的内容,进程 B 可以立即看到这些修改。
      2. MAP_PRIVATE
        这个选项表示映射的内存区域与文件私有,修改映射区域的内容不会写回到原文件。此类修改仅在当前进程的内存中有效,其他进程不可见。
        • 例如:进程 A 映射了一个文件并修改了映射区域的内容,但文件本身保持不变。
      3. MAP_ANONYMOUS
        这个选项表示创建一个匿名内存映射,即该映射不与任何文件相关联。fd 参数通常为 -1。它用于分配一块内存,而不是映射一个文件。对于不需要读取或写入文件的场景,匿名映射特别有用。
        • 例如:创建一个内存区域供程序使用(如内存池、数据结构等),而不需要任何文件作为后端。
      4. MAP_FIXED
        这个选项表示指定的 addr 地址是映射区域的起始地址。它要求映射区域严格地在该地址上开始。如果该地址已有其他映射或冲突,则会直接替换现有的映射区域。
        • 注意:如果指定的地址无法映射(例如,无法与当前地址空间兼容),mmap 将返回 MAP_FAILED
      5. MAP_FILE
        这个选项指定映射的是文件,通常默认情况下,mmap 会将文件映射到内存。该选项几乎与 MAP_SHARED 等价,但出于兼容性考虑,MAP_FILE 仍然可以使用。
      6. MAP_HUGETLB
        这个选项表示使用大页面(huge pages)。它要求映射使用大页内存,而不是系统默认的标准页面。这个选项通常需要超级用户权限,并且在支持大页的操作系统中才有效。
        • 例如:在内存密集型应用(如数据库、虚拟机管理程序)中,使用大页面可以提高内存管理的效率。
      7. MAP_LOCKED
        这个选项表示映射的内存区域在物理内存中将被锁定,操作系统不会将其交换到交换空间(swap)中。这对于实时应用或对内存访问有严格要求的应用非常有用。
      8. MAP_NORESERVE
        这个选项表示在创建映射时不保留交换空间(swap),即使映射的内存区域并没有实际分配内存。这通常用于创建一个大区域的虚拟内存映射,期望它不会被使用。
        • 该选项一般在大数据结构的内存分配中使用,以避免过多的交换空间消耗。
      9. MAP_POPULATE
        这个选项会在映射时立即预取(prefetch)所有的页面,即使这些页面还没有被访问过。它可以减少后续对内存的访问延迟,适用于希望提前加载数据的应用。
      10. MAP_STACK
        这个选项用来为程序的线程栈创建映射,表示该映射是为了线程的栈而创建的。通常由系统自动处理。
    • fd: 需映射的文件描述符

    • offset: 文件存储空间的偏移量

  • 返回值

    • 成功:munmap返回 0 ,mmap 返回映射后的地址;
    • 出错:返回 MMAP_FAILED,并将错误码存入 errno 中
  • 案例

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <unistd.h>int main() {const char *filename = "testfile.txt";int fd = open(filename, O_RDWR);  // 打开文件,读取/写入权限if (fd == -1) {perror("open");return 1;}// 获取文件的大小off_t file_size = lseek(fd, 0, SEEK_END);// 使用 mmap 映射文件到内存char *mapped = (char *)mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);return 1;}// 修改文件内容mapped[0] = 'H';  // 将文件的第一个字符改为 'H'// 同步映射区的内容到文件if (msync(mapped, file_size, MS_SYNC) == -1) {perror("msync");}// 解除映射if (munmap(mapped, file_size) == -1) {perror("munmap");}close(fd);return 0;
    }
    
  • 文件映射与系统i/o的区别

    • 抽象层次

      • 文件映射(Memory-Mapped Files):文件映射是一种将文件内容映射到进程的虚拟内存中的技术。通过文件映射,程序可以像访问内存一样访问文件的内容,而不需要显式地进行文件读取或写入操作。文件映射通过操作系统的虚拟内存管理机制来管理文件的加载和访问。

      • 系统I/O(System I/O):系统I/O是通过操作系统提供的系统调用(如 read, write, open, close 等)直接进行文件操作。程序需要显式地读取或写入数据到文件中,通常依赖于内存缓冲区。

    • 性能和效率

      • 文件映射:文件映射能够提供更高效的文件操作,尤其是在处理大文件或频繁访问文件的场景下。操作系统会自动将文件的数据加载到内存中,并根据需要进行页面交换。文件映射的优势在于它通过内存管理机制减少了多次I/O操作,提高了性能。内存映射文件可以直接在内存中访问,从而避免了频繁的磁盘I/O。

      • 系统I/O:系统I/O操作通常需要显式地读取或写入文件,每次操作都可能涉及磁盘I/O,尤其是在没有有效缓存时。系统I/O的性能受到磁盘I/O和缓冲策略的影响,通常比文件映射较慢,特别是在频繁读取大文件时。

    • 访问方式

      • 文件映射:文件映射将文件的一部分或整个文件映射到进程的虚拟地址空间,使程序可以通过指针直接访问文件内容。这种访问方式类似于访问普通内存,程序员可以像操作内存一样对文件进行读写。

      • 系统I/O:系统I/O需要使用操作系统提供的接口(如 read, write 等)来显式地读取或写入文件内容,数据需要先从文件中读取到内存中的缓冲区,然后进行处理。文件的访问方式是通过系统调用来完成的。

    • 数据同步

      • 文件映射:文件映射的更大优点之一是,操作系统负责管理数据的同步。数据可以在内存中直接操作,操作系统会定期将内存中更改的数据写回磁盘。为了保证数据一致性,通常会使用 msyncmunmap 等函数来手动同步数据。

      • 系统I/O:系统I/O通常依赖于缓冲区来处理数据,程序员需要显式地调用 flush 等函数来确保缓冲区的内容被写回磁盘,或者使用文件关闭操作来触发数据的同步。

    • 内存使用

      • 文件映射:文件映射会直接将文件的一部分或整个文件映射到进程的虚拟内存空间。操作系统会为映射区域分配虚拟内存,并在实际访问时将数据从磁盘加载到内存中。对于大型文件,文件映射只会在需要时加载文件的部分数据,而不是将整个文件加载到内存中。

      • 系统I/O:系统I/O操作通常需要将文件的数据读入到应用程序提供的缓冲区中。缓冲区的大小由程序员控制,且每次I/O操作都可能导致较高的内存使用,尤其是在读取大型文件时。

总结:
  • 文件映射:通过将文件映射到内存中,提供了一种高效的方式来处理文件,可以像操作内存一样访问文件内容,适用于需要频繁或大规模访问文件的场景。
  • 系统I/O:是传统的文件操作方式,需要显式进行文件读取或写入操作,适用于一般的文件访问,但性能和灵活性相对较低。

文件状态查询

  • 头文件

    #include <fcntl.h>            
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
  • 函数原型:

    int stat(const char *path, struct stat* buf)
    int fstat(int fd, struct stat *buf);
    
  • 参数说明

    • path:要操作的文件名或路径;

    • buf:指向stat 结构体的指针,用来获取文件状态信息:

      struct stat 
      {dev_t          st_dev;            //文件设备编号ino_t          st_ino;            //文件inode节点号mode_t      st_mode;              //文件类型,访问权限等nlink_t       st_nlink;           //文件的连接数uid_t          st_uid;            //文件所有者的用户IDgid_t          st_gid;            //文件所有者对应的组IDdev_t         st_rdev;            //若文件为设备文件,则表示设备编号off_t          st_size;           //文件大小,对应的文件字节数blksize_t    st_blksize;          //文件系统的 I/O 缓冲区大小blkcnt_t     st_blocks;           //占用文件区块数量,每一区块512 字节time_t        st_atime;           //文件最后一次被访问的时间time_t        st_mtime;           //文件内容最后一次被修改的时间time_t        st_ctime;           //最后一次改变时间(属性改变)
      };
      
    • fd:文件描述符

    • st_mode

      st_mode 常见取值: (八进制,文件类型主要取决第3字节)

      S_IFSOCK0140000socket 套接字文件
      S_IFLNK0120000链接文件
      S_IFREG0100000一般文件
      S_IFBLK0060000块设备文件
      S_IFDIR0040000目录
      S_IFCHR0020000字符设备文件
      S_IFIFO0010000管道文件

      上述的文件类型在POSIX中定义了检查这些类型的宏定义:

      S_ISLNK (st_mode)判断是否为链接文件
      S_ISREG (st_mode)是否为一般文件
      S_ISDIR (st_mode)是否为目录
      S_ISCHR (st_mode)是否为字符设备文件
      S_ISBLK (st_mode)是否为块设备文件
      S_ISSOCK (st_mode)是否为socket套接字文件
      S_ISFIFO (st_mode)是否为管道文件

      st_mode 其他常见取值:

      S_IRWXU00700自己拥有所有权限
      S_IRUSR00400自己拥有读权限
      S_IWUSR00200自己拥有写权限
      S_IXUSR00100自己拥有执行权限
      S_IRWXG00070自己组拥有所有权限
      S_IRGRP00040自己组拥有写权限
      S_IWGRP00020自己组拥有执行权限
      S_IXGRP00010自己组拥有执行权限
      S_IRWXO00007其他组用户拥有所有权限
      S_IROTH00004其他组用户拥有读权限
      S_IWOTH00002其他组用户拥有写权限
      S_IXOTH00001其他组用户拥有执行权限

利用用户ID获取用户信息,

  • 头文件

    #include <sys/types.h>   
    #include <pwd.h>
    
  • 函数原型

    struct passwd* getpwuid(uid_t uid);
    
  • 参数说明

    uid:用户id;

利用用户组ID获取用户组信息

  • 头文件

    #include <sys/types.h>  
    #include <grp.h>
    
  • 函数原型

    struct group *getgrgid(gid_t gid)
    
  • 参数说明

    gid:用户组id;

  • 返回值

    成功后返回下列结构体:

struct passwd {char*   pw_name;                  /* user name */char*   pw_passwd;                /* user password */uid_t    pw_uid;                  /* user ID */gid_t    pw_gid;                  /* group ID */char*  pw_gecos;                  /* user information */char*  pw_dir;                    /* home directory */char*  pw_shell;                  /* shell program */};struct group {char*   gr_name;                  /* group name */char*   gr_passwd;                /* group password */gid_t     gr_gid;                 /* group ID */char** gr_mem;                    /* group members */};
  • 案例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <grp.h>
    #include <sys/types.h>int main() {gid_t gid = 1000;  // 替换为一个有效的组 IDstruct group *grp = getgrgid(gid);if (grp != NULL) {printf("组名: %s\n", grp->gr_name);printf("组 ID: %d\n", grp->gr_gid);printf("组成员: ");for (char **member = grp->gr_mem; *member != NULL; member++) {printf("%s ", *member);}printf("\n");} else {perror("getgrgid");}struct passwd *pw = getpwuid(uid);if (pw != NULL) {printf("用户名: %s\n", pw->pw_name);printf("用户 ID: %d\n", pw->pw_uid);printf("主组 ID: %d\n", pw->pw_gid);printf("主目录: %s\n", pw->pw_dir);printf("登录 Shell: %s\n", pw->pw_shell);} else {perror("getpwnam");}return 0;
    }
    

http://www.ppmy.cn/devtools/151918.html

相关文章

山石防火墙命令行配置示例

现网1台山石SG6000防火墙&#xff0c;配置都可以通过GUI实现。 但有一些配置在命令行下配置效率更高&#xff0c;比如在1个已有策略中添加1个host或端口。 下面的双引号可以不加 1 创建服务 1.1 单个端口 service "tcp-901"tcp dst-port 901 1.2 端口范围 servi…

电脑有两张网卡,如何实现同时访问外网和内网?

要是想让一台电脑用两张网卡&#xff0c;既能访问外网又能访问内网&#xff0c;那可以通过设置网络路由还有网卡的 IP 地址来达成。 检查一下网卡的连接 得保证电脑的两张网卡分别连到外网和内网的网络设备上&#xff0c;像路由器或者交换机啥的。 给网卡配上不一样的 IP 地…

如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!

最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近&#xff0c;我在进行前端开发时&#xff0c;遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需&#xff0c;我一直在冥思苦想。终于有了一个解决方法…

回顾 2024— 浔川社团:在数字浪潮中的成长与奋进

回顾 2024— 浔川社团&#xff1a;在数字浪潮中的成长与奋进 在过去的 2024 年&#xff0c;浔川社团以其独特的魅力和不懈的努力&#xff0c;在网络的舞台上绽放出别样的光彩。从各项数据指标中&#xff0c;我们能清晰地看到社团这一年来的发展轨迹&#xff0c;见证其在内容创作…

解决Spring Boot中Druid连接池“discard long time none received connection“警告

在使用Spring Boot结合Druid连接池时&#xff0c;开发者可能会遇到"discard long time none received connection"的警告信息。虽然这通常不会影响应用程序的正常运行&#xff0c;但这些警告信息可能会让人感到困扰。本文将探讨这个问题的原因&#xff0c;并提供几种…

5-1 创建和打包AXI Interface IP

创建和打包AXI Interface IP的前流程和后流程 step 1 &#xff1a; 选择类型 1&#xff1a; 将当前的工程打包成IP 2&#xff1a; 将当前的BD工程打包成IP 3&#xff1a; 将指定的源码打包成IP 4&#xff1a; 创建一个新的AXI 接口IP 其中3和4是比较常用的&#xff0c;本次…

C++通透讲解设计模式:依赖倒转(1)

依赖倒转 这是我认为的SOLID里面最重要的一个原则&#xff0c;当你掌握这种设计方式之后&#xff0c;会让别人在调用你的代码时爽很多。 在C20设计模式这本书中&#xff0c;依赖倒转写的很抽象。我这里将他的概念列出&#xff1a; 高层模块不应该依赖底层模块&#xff0c;它…

HNU人工智能课程总结与反思

HNU人工智能这门课教材用的是101计划的《人工智能引论》&#xff0c;浙江大学吴飞教授编的。个人认为&#xff0c;教材写的挺好&#xff0c;但是把人工智能这一领域的东西放在一本书里&#xff0c;用32个课时讲完还是太过于仓促。不过&#xff0c;对于我这个上课之前对人工智能…