.net 零拷贝_模拟总线型以太网数据帧发送过程

.net 零拷贝_模拟总线型以太网数据帧发送过程mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

mmap说明

mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

mmap/munmap函数声明如下:

#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

addr用于指定映射到进程空间的起始地址,为了应用程序的可移植性,一般设置为NULL,让内核来选择一个合适的地址。

mmap函数就是为了寻找一个可用的地址空间,将文件描述符fd对应的文件从偏移量offset开始,copy文件的length个字符进入用户地址空间。将起始地址返回。为什么第一个参数还要传用户地址空间的指针呢?这不是骑驴找驴吗?

这个addr参数可以NULL,这种情况我们比较容易理解。当addr不为NULL的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。

  • 如果flags带上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,直接把addr返回。
  • 如果没带上MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
  • addr为NULL,让内核自己选择。

length、fd、offset

将fd对应的文件,从offset位置开始,长为len的内容映射到内存地址空间。

prot

prot参数通常表示映射页面的的读写权限,可以有如下参数组合:

  • PROT_EXEC:表示映射的页面是可以执行的。
  • PROT_READ:表示映射的页面是可以读取的。
  • PROT_WRITE:表示映射的页面是可以写入的。
  • PROT_NONE:表示映射的页面是不可访问的。

flags

flags参数也是一个重要的参数,有如下常见的参数:

  • MAP_SHARED:创建一个共享映射的区域。多个进程可以通过共享映射方式来映射一个文件,这样其他进程也可以看到映射内容的改变,修改后的内容会同步到磁盘文件中。
  • MAP_PRIVATE:创建一个私有的写时复制的映射。多个进程可以通过私有映射的方式来映射一个文件,这样其他进程不会看到映射内容的改变,修改后的内容也不会同步到磁盘文件中。
  • MAP_ANONYMOUS:创建一个匿名映射,即没有关联到文件的映射。
  • MAP_FIXED:使用参数addr创建映射,如果内核无法映射指定地址addr,那么mmap会返回失败,参数addr要求按页对齐。如果addr和length指定的进程地址空间和已有的VMA区域重叠,那么内核会调用do_munmap()函数把这段重叠区域销毁,然后重新映射新的内容。
  • MAP_POPULATE:对于文件映射来说,会提前预读文件内容到映射区域,该特性只支持私用映射。

mmap映射类型

参数fd可以看出mmap映射是否和文件相关联,因此Linux内核中映射可以分为匿名映射和文件映射。

  • 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0。
  • 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。

最后根据文件关联性和映射区域是否共享等属性,又可以分为如下4种:

私有映射 共享映射
匿名映射 私有匿名映射-通常用于内存分配 共享匿名映射-通常用于进程间共享内存
文件映射 私有文件映射-通常用于加载动态库 共享文件映射-通常用于内存映射IO,进程间通信

私有匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_PRIVATE时,创建的mmap映射是私有匿名映射。私有匿名映射最常见的用途是在glibc分配大块内存中,当需要的分配的内存大于MMAP_THREASHOLD(128KB)时,glibc会默认使用mmap代替brk来分配内存。

共享匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_SHARED。在这种情况下,创建共享匿名映射。共享匿名映射让相关进程共享一块内存区域,通常用于父子进程的之间通信。

创建共享匿名映射有如下两种方式:

  1. fd=-1且flags= MAP_ANONYMOUS|MAP_SHARED。在这种情况下,do_mmap_pgoff()->mmap()函数最终调用shmem_zero_setup()来打开一个”/dev/zero”特殊的设备文件。
  2. 另外一个是直接打开”/dev/zero”设备文件,然后使用这个文件句柄来创建mmap。

私有文件映射

私有文件映射时flags的标志位被设置为MAP_PRIVATE,那么就会创建私有文件映射。

私有文件映射的最常用的场景是加载动态共享库。

共享文件映射

创建文件映射时flags的标志位被设置为MAP_SHARED,那么就会创建共享文件映射。如果prot参数指定了PROT_WRITE,那么打开文件需要制定O_RDWR标志位。共享文件映射通常有如下场景:

  • 读写文件:把文件内容映射到进程地址空间,同时对映射的内容做了修改,内核的回写机制(writeback)最终会把修改的内容同步到磁盘中。
  • 进程间通信:进程之间的进程地址空间相互隔离,一个进程不能访问到另外一个进程的地址空间。如果多个进程都同时映射到一个相同的文件,就实现了多进程间的共享内存的通信。如果一个进程对映射内容做了修改,那么另外的进程是可以看到的。

mmap的使用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>

int main() { 
   

        int fd = open("/root/demo/aa", O_CREAT | O_RDWR, 777);

        size_t length = 1024;
        ftruncate(fd, length); // 一定要这句,否则会报Bus error
        char *addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
                       MAP_SHARED, fd, 0);

        char* data = "hello";
        memcpy(addr, data, 5);

        char result[5];
        memcpy(result, addr, 5);

        printf("%s\n", result);

        sleep(100);

        munmap(addr, length);
        close(fd);

        return 0;
}

对一个空的新文件进行mmap前,需要往里面写入一点内容,否则会报错Bus error。发生错误的原因是因为mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。

解决方案有2个:

  • 一个就是上面的链接里的方案: 只需要在新创建的空文件中先写入一些数据即可;
  • 另外一个是通过ftruncate对新建立的文件进行扩展后再映射修改。

查询进程所有的mmap映射:

# cat /proc/1078/maps
5636aadb5000-5636aadb6000 r--p 00000000 08:10 41881                      /root/demo/a.out
5636aadb6000-5636aadb7000 r-xp 00001000 08:10 41881                      /root/demo/a.out
5636aadb7000-5636aadb8000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb8000-5636aadb9000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb9000-5636aadba000 rw-p 00003000 08:10 41881                      /root/demo/a.out
5636abae0000-5636abb01000 rw-p 00000000 00:00 0                          [heap]
7f9f4e83f000-7f9f4e861000 r--p 00000000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e861000-7f9f4e9d9000 r-xp 00022000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e9d9000-7f9f4ea27000 r--p 0019a000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea27000-7f9f4ea2b000 r--p 001e7000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2b000-7f9f4ea2d000 rw-p 001eb000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2d000-7f9f4ea33000 rw-p 00000000 00:00 0
7f9f4ea3b000-7f9f4ea3c000 r--p 00000000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea3c000-7f9f4ea5f000 r-xp 00001000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea5f000-7f9f4ea67000 r--p 00024000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea67000-7f9f4ea68000 rw-s 00000000 08:10 768                        /root/demo/aa
7f9f4ea68000-7f9f4ea69000 r--p 0002c000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea69000-7f9f4ea6a000 rw-p 0002d000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea6a000-7f9f4ea6b000 rw-p 00000000 00:00 0
7ffe68bf2000-7ffe68c13000 rw-p 00000000 00:00 0                          [stack]
7ffe68dd5000-7ffe68dd8000 r--p 00000000 00:00 0                          [vvar]
7ffe68dd8000-7ffe68dd9000 r-xp 00000000 00:00 0                          [vdso]

查看内存映射的区域地址范围:

# ll /proc/1078/map_files/
total 0
dr-x------ 2 root root  0 Jul 12 11:02 ./
dr-xr-xr-x 9 root root  0 Jul 12 11:02 ../
lr-------- 1 root root 64 Jul 12 11:02 5636aadb5000-5636aadb6000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb6000-5636aadb7000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb7000-5636aadb8000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb8000-5636aadb9000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb9000-5636aadba000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e83f000-7f9f4e861000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e861000-7f9f4e9d9000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e9d9000-7f9f4ea27000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea27000-7f9f4ea2b000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea2b000-7f9f4ea2d000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3b000-7f9f4ea3c000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3c000-7f9f4ea5f000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea5f000-7f9f4ea67000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lrw------- 1 root root 64 Jul 12 11:02 7f9f4ea67000-7f9f4ea68000 -> /root/demo/aa*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea68000-7f9f4ea69000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea69000-7f9f4ea6a000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*

查看内存映射地址范围的内容:

# cat /proc/1078/map_files/7f9f4ea67000-7f9f4ea68000
hello

我们看到/root/demo/aa文件已经映射到内存中取了,占用空间0x7f9f4ea67000-0x7f9f4ea68000=4K,虽然申请的是1K,但是映射的分配是以页面为单位分配的,即最小分配4K。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/192741.html原文链接:https://javaforall.net

(0)
上一篇 2025年12月9日 上午9:15
下一篇 2025年12月9日 上午9:43


相关推荐

  • vue人脸识别_vue不是内部或外部命令

    vue人脸识别_vue不是内部或外部命令1.vue报错:无法将“vue”项识别为 cmdlet、函数、脚本文件或可运行程序的名称

    2022年8月18日
    10
  • c语言移位操作

    c语言移位操作

    2021年12月17日
    56
  • PHP实现微信申请退款流程实例源码

    PHP实现微信申请退款流程实例源码

    2021年10月30日
    39
  • 利用PLINK进行GWAS分析

    利用PLINK进行GWAS分析PLINK 软件输入文件的常见格式类型 1 一般格式 PED MAP2 转置格式 TPED TFAM3 二进制格式 BED BIM FAM 几种格式之间可以相互转换 推荐使用 BED BIM FAM 这种格式 读取速度快 BED 文件包含 SNP 数据 是二进制格式 不能由 Notepad 等文本编辑器打开 BIM 文件包括 SNP 位置信息 FAM 文件包括家系表型信息 这两种文件都是文本格式 PE

    2026年3月19日
    3
  • Linux load average负载量分析与解决思路

    Linux load average负载量分析与解决思路top命令中loadaverage显示的是最近1分钟、5分钟和15分钟的系统平均负载。系统平均负载表示  系统平均负载被定义为在特定时间间隔内运行队列中(在CPU上运行或者等待运行多少进程)的平均进程树。如果一个进程满足以下条件则其就会位于运行队列中:  -它没有在等待I/O操作的结果  -它没有主动进入等待状态(也就是没有调用’wait’)  -没有被停止

    2022年7月17日
    17
  • pythoncharm注释快捷键_jsp注释快捷键

    pythoncharm注释快捷键_jsp注释快捷键常用的快捷键1.设置(ctrl+alt+s)2.快速创建文件(alt+insert)3.自动格式化(ctrl+alt+l)4.快速注释代码(ctrl+/)5.快速取消注释代码(ctrl+/)6.复制一行代码(ctrl+d)7.撤销操作(ctrl+z)常用的注释方式1.单行注释使用#2.多行注释使用三引号“””“””…

    2022年8月28日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号