Linux驱动mmap内存映射详解及例子实现

26 篇文章 1 订阅
订阅专栏
17 篇文章 2 订阅
订阅专栏

mmap在linux哪里?

什么是mmap?

上图说了,mmap是操作这些设备的一种方法,所谓操作设备,比如IO端口(点亮一个LED)、LCD控制器、磁盘控制器,实际上就是往设备的物理地址读写数据。

但是,由于应用程序不能直接操作设备硬件地址,所以操作系统提供了这样的一种机制——内存映射,把设备地址映射到进程虚拟地址,mmap就是实现内存映射的接口。

操作设备还有很多方法,如ioctl、ioremap

mmap的好处是,mmap把设备内存映射到虚拟内存,则用户操作虚拟内存相当于直接操作设备了,省去了用户空间到内核空间的复制过程,相对IO操作来说,增加了数据的吞吐量。

 

什么是内存映射?

既然mmap是实现内存映射的接口,那么内存映射是什么呢?看下图

每个进程都有独立的进程地址空间,通过页表和MMU,可将虚拟地址转换为物理地址,每个进程都有独立的页表数据,这可解释为什么两个不同进程相同的虚拟地址,却对应不同的物理地址。

 

什么是虚拟地址空间?

每个进程都有4G的虚拟地址空间,其中3G用户空间,1G内核空间(linux),每个进程共享内核空间,独立的用户空间,下图形象地表达了这点

驱动程序运行在内核空间,所以驱动程序是面向所有进程的。

用户空间切换到内核空间有两种方法:

(1)系统调用,即软中断

(2)硬件中断

 

虚拟地址空间里面是什么?

了解了什么是虚拟地址空间,那么虚拟地址空间里面装的是什么?看下图

虚拟空间装的大概是上面那些数据了,内存映射大概就是把设备地址映射到上图的红色段了,暂且称其为“内存映射段”,至于映射到哪个地址,是由操作系统分配的,操作系统会把进程空间划分为三个部分:

(1)未分配的,即进程还未使用的地址

(2)缓存的,缓存在ram中的页

(3)未缓存的,没有缓存在ram中

操作系统会在未分配的地址空间分配一段虚拟地址,用来和设备地址建立映射,至于怎么建立映射,后面再揭晓。

现在大概明白了“内存映射”是什么了,那么内核是怎么管理这些地址空间的呢?任何复杂的理论最终也是通过各种数据结构体现出来的,而这里这个数据结构就是进程描述符。从内核看,进程是分配系统资源(CPU、内存)的载体,为了管理进程,内核必须对每个进程所做的事情进行清楚的描述,这就是进程描述符,内核用task_struct结构体来表示进程,并且维护一个该结构体链表来管理所有进程。该结构体包含一些进程状态、调度信息等上千个成员,我们这里主要关注进程描述符里面的内存描述符(struct mm_struct mm)

 

内存描述符

具体的结构,请参考下图

现在已经知道了内存映射是把设备地址映射到进程空间地址(注意:并不是所有内存映射都是映射到进程地址空间的,ioremap是映射到内核虚拟空间的,mmap是映射到进程虚拟地址的),实质上是分配了一个vm_area_struct结构体加入到进程的地址空间,也就是说,把设备地址映射到这个结构体,映射过程就是驱动程序要做的事了。

 

内存映射的实现

以字符设备驱动为例,一般对字符设备的操作都如下框图

而内存映射的主要任务就是实现内核空间中的mmap()函数,先来了解一下字符设备驱动程序的框架

以下是mmap_driver.c的源代码

[cpp]  view plain  copy

  1. //所有的模块代码都包含下面两个头文件  
    #include <linux/module.h>  
    #include <linux/init.h>  
      
    #include <linux/types.h> //定义dev_t类型  
    #include <linux/cdev.h> //定义struct cdev结构体及相关操作  
    #include <linux/slab.h> //定义kmalloc接口  
    #include <asm/io.h>//定义virt_to_phys接口  
    #include <linux/mm.h>//remap_pfn_range  
    #include <linux/fs.h>  
      
    #define MAJOR_NUM 990  
    #define MM_SIZE 4096  
      
    static char driver_name[] = "mmap_driver1";//驱动模块名字  
    static int dev_major = MAJOR_NUM;  
    static int dev_minor = 0;  
    char *buf = NULL;  
    struct cdev *cdev = NULL;  
      
    static int device_open(struct inode *inode, struct file *file)  
    {  
        printk(KERN_ALERT"device open\n");  
        buf = (char *)kmalloc(MM_SIZE, GFP_KERNEL);//内核申请内存只能按页申请,申请该内存以便后面把它当作虚拟设备  
        return 0;  
    }  
      
    static int device_close(struct inode *indoe, struct file *file)  
    {  
        printk("device close\n");  
        if(buf)  
        {  
            kfree(buf);  
        }  
        return 0;  
    }  
      
    static int device_mmap(struct file *file, struct vm_area_struct *vma)  
    {  
        vma->vm_flags |= VM_IO;//表示对设备IO空间的映射  
        vma->vm_flags |= VM_RESERVED;//标志该内存区不能被换出,在设备驱动中虚拟页和物理页的关系应该是长期的,应该保留起来,不能随便被别的虚拟页换出  
        if(remap_pfn_range(vma,//虚拟内存区域,即设备地址将要映射到这里  
                           vma->vm_start,//虚拟空间的起始地址  
                           virt_to_phys(buf)>>PAGE_SHIFT,//与物理内存对应的页帧号,物理地址右移12位  
                           vma->vm_end - vma->vm_start,//映射区域大小,一般是页大小的整数倍  
                           vma->vm_page_prot))//保护属性,  
        {  
            return -EAGAIN;  
        }  
        return 0;  
    }  
      
    static struct file_operations device_fops =  
    {  
        .owner = THIS_MODULE,  
        .open  = device_open,  
        .release = device_close,  
        .mmap = device_mmap,  
    };  
      
    static int __init char_device_init( void )  
    {  
        int result;  
        dev_t dev;//高12位表示主设备号,低20位表示次设备号  
        printk(KERN_ALERT"module init2323\n");  
        printk("dev=%d", dev);  
        dev = MKDEV(dev_major, dev_minor);  
        cdev = cdev_alloc();//为字符设备cdev分配空间  
        printk(KERN_ALERT"module init\n");  
        if(dev_major)  
        {  
            result = register_chrdev_region(dev, 1, driver_name);//静态分配设备号  
            printk("result = %d\n", result);  
        }  
        else  
        {  
            result = alloc_chrdev_region(&dev, 0, 1, driver_name);//动态分配设备号  
            dev_major = MAJOR(dev);  
        }  
          
        if(result < 0)  
        {  
            printk(KERN_WARNING"Cant't get major %d\n", dev_major);  
            return result;  
        }  
          
          
        cdev_init(cdev, &device_fops);//初始化字符设备cdev  
        cdev->ops = &device_fops;  
        cdev->owner = THIS_MODULE;  
          
        result = cdev_add(cdev, dev, 1);//向内核注册字符设备  
        printk("dffd = %d\n", result);  
        return 0;  
    }  
      
    static void __exit char_device_exit( void )  
    {  
        printk(KERN_ALERT"module exit\n");  
        cdev_del(cdev);  
        unregister_chrdev_region(MKDEV(dev_major, dev_minor), 1);  
    }  
      
    module_init(char_device_init);//模块加载  
    module_exit(char_device_exit);//模块退出  
      
    MODULE_LICENSE("GPL");  
    MODULE_AUTHOR("ChenShengfa");  

     

 

下面是测试代码test_mmap.c

[cpp]  view plain  copy

  1.  
#include <stdio.h>  
#include <fcntl.h>  
#include <sys/mman.h>  
#include <stdlib.h>  
#include <string.h>  
  
int main( void )  
{  
    int fd;  
    char *buffer;  
    char *mapBuf;  
    fd = open("/dev/mmap_driver", O_RDWR);//打开设备文件,内核就能获取设备文件的索引节点,填充inode结构  
    if(fd<0)  
    {  
        printf("open device is error,fd = %d\n",fd);  
        return -1;  
    }  
    /*测试一:查看内存映射段*/  
    printf("before mmap\n");  
    sleep(15);//睡眠15秒,查看映射前的内存图cat /proc/pid/maps  
    buffer = (char *)malloc(1024);  
    memset(buffer, 0, 1024);  
    mapBuf = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//内存映射,会调用驱动的mmap函数  
    printf("after mmap\n");  
    sleep(15);//睡眠15秒,在命令行查看映射后的内存图,如果多出了映射段,说明映射成功  
      
    /*测试二:往映射段读写数据,看是否成功*/  
    strcpy(mapBuf, "Driver Test");//向映射段写数据  
    memset(buffer, 0, 1024);  
    strcpy(buffer, mapBuf);//从映射段读取数据  
    printf("buf = %s\n", buffer);//如果读取出来的数据和写入的数据一致,说明映射段的确成功了  
      
      
    munmap(mapBuf, 1024);//去除映射  
    free(buffer);  
    close(fd);//关闭文件,最终调用驱动的close  
    return 0;  
}  

 

下面是makefile文件

[plain]  view plain  copy

  1. ifneq ($(KERNELRELEASE),)  
      
    obj-m := mmap_driver.o  
      
    else  
    KDIR := /lib/modules/3.2.0-52-generic/build  
      
    all:  
        make -C $(KDIR) M=$(PWD) modules  
    clean:  
        rm -f *.ko *.o *.mod.o *.mod.c *~ *.symvers *.order  
      
    endif  

     

下面命令演示一下驱动程序的编译、安装、测试过程(注:其他用户在mknod之后还需要chmod改变权限)

# make    //编译驱动

# insmod mmap_driver.ko    //安装驱动

# mknod /dev/mmap_driver c 999 0    //创建设备文件

# gcc test_mmap.c -o test.o    //编译应用程序

# ./test.o    //运行应用程序来测试驱动程序


拓展:

关于这个过程,涉及一些术语

(1)设备文件:linux中对硬件虚拟成设备文件,对普通文件的各种操作均适用于设备文件

(2)索引节点:linux使用索引节点来记录文件信息(如文件长度、创建修改时间),它存储在磁盘中,读入内存后就是一个inode结构体,文件系统维护了一个索引节点的数组,每个元素都和文件或者目录一一对应。

(3)主设备号:如上面的999,表示设备的类型,比如该设备是lcd还是usb等

(4)次设备号:如上面的0,表示该类设备上的不同设备

(5)文件(普通文件或设备文件)的三个结构

        ①文件操作:struct file_operations

        ②文件对象:struct file

        ③文件索引节点:struct inode

 

关于驱动程序中内存映射的实现,先了解一下open和close的流程

(1)设备驱动open流程

①应用程序调用open("/dev/mmap_driver", O_RDWR);

②Open就会通过VFS找到该设备的索引节点(inode),mknod的时候会根据设备号把驱动程序的file_operations结构填充到索引节点中(关于mknod /dev/mmap_driver c 999 0,这条指令创建了设备文件,在安装驱动(insmod)的时候,会运行驱动程序的初始化程序(module_init),在初始化程序中,会注册它的主设备号到系统中(cdev_add),如果mknod时的主设备号999在系统中不存在,即和注册的主设备号不同,则上面的指令会执行失败,就创建不了设备文件)

③然后根据设备文件的索引节点中的file_operations中的open指针,就调用驱动的open方法了。

④生成一个文件对象files_struct结构,系统维护一个files_struct的链表,表示系统中所有打开的文件

⑤返回文件描述符fd,把fd加入到进程的文件描述符表中

 

(2)设备驱动close流程

应用程序调用close(fd),最终可调用驱动的close,为什么根据一个简单的int型fd就可以找到驱动的close函数?这就和上面说的三个结构(struct file_operations、struct file、struct inode)息息相关了,假如fd = 3

 

(3)设备驱动mmap流程

由open和close得知,同理,应用程序调用mmap最终也会调用到驱动程序中mmap方法

①应用程序test.mmap.c中mmap函数

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

addr:映射后虚拟地址的起始地址,通常为NULL,内核自动分配

length:映射区的大小

prot:页面访问权限(PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE)

flags:参考网络资料

fd:文件描述符

offset:文件映射开始偏移量

 

②驱动程序的mmap_driver.c中mmap函数

上面说了,mmap的主要工作是把设备地址映射到进程虚拟地址,也即是一个vm_area_struct的结构体,这里说的映射,是一个很悬的东西,那它在程序中的表现是什么呢?——页表,没错,就是页表,映射就是要建立页表。进程地址空间就可以通过页表(软件)和MMU(硬件)映射到设备地址上了

virt_to_phys(buf),buf是在open时申请的地址,这里使用virt_to_phys把buf转换成物理地址,是模拟了一个硬件设备,即把虚拟设备映射到虚拟地址,在实际中可以直接使用物理地址。

 

总结

①从以上看到,内核各个模块错综复杂、相互交叉

②单纯一个小小驱动模块,就涉及了进程管理(进程地址空间)、内存管理(页表与页帧映射)、虚拟文件系统(structfile、structinode)

③并不是所有设备驱动都可以使用mmap来映射,比如像串口和其他面向流的设备,并且必须按照页大小进行映射。

linux mmap文件内存映射机制
10-16
mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而 Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。   mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
Linux下得mmap内存映射机制分析
10-20
mmap内存映射机制分析
Python3 mmap内存映射文件示例解析
01-20
1. mmap内存映射文件 建立一个文件的内存映射将使用操作系统虚拟内存来直接访问文件系统上的数据,而不是使用常规的I/O函数访问数据内存映射通常可以提供I/O性能,因为使用内存映射是,不需要对每个访问都建立一个单独的系统调用,也不需要在缓冲区之间复制数据;实际上,内核和用户应用都能直接访问内存。 内存映射文件可以看作是可修改的字符串或类似文件的对象,这取决于具体的需要。映射文件支持一般的文件API方法,如close()、flush()、read()、readline()、seek()、tell()和write()。它还支持字符串API,提供分片等特性以及类似find()的方法。 下面的所有
linux下的内存映射函数mmap详解及示例代码.pdf
12-04
linux下的内存映射函数mmap详解及示例代码.pdf
mmap实验例子
weixin_43300893的博客
11-09 1288
mmap内存映射实现 实验内容:在内核下面申请4K大小的内存(kmalloc申请的物理连续内存),在应用程序中通过mmap把内核态下申请的内存块映射到用户空间,然后在应用程序中通过read,write操作读写内核空间数据,最后读取用户态映射的地址空间,看内容是否和内核数据空间一致。 我的编译工具: /opt/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gun/bin/arrch64-linux-gun-gcc 内核实现: #include <linux/
Linux内存管理
Likes的博客
06-25 143
http://www.kerneltravel.net/journal/v/mem.htm 目录 一、前言 进程与内存 1、进程如何使用内存? 2、进程如何组织这些区域? 三、进程内存空间 1、进程内存管理 2、进程内存的分配与回收 3、如何由虚变实! 四、系统物理内存管理 1、物理内存管理(页管理) 2、内核内存使用 a、Slab b、Kmalloc c、内核非连...
MMZ内存管理
09-22 7590
看完秒懂:Linux DMA mapping机制分析
weixin_71478434的博客
08-27 4301
Linux内核中提供了两种dma mapping的接口:Consistent mapping和Stream mapping。通常在使用consistent dma mapping时,首先需要通过。
Linux Kernel Map分享
vertor11的博客
03-21 1545
一. 原网址 https://makelinux.github.io/kernel/map/ 二. 大图
海思mpp学习:MMZ内存
qq_52754751的博客
06-05 1444
海思芯片的物理内存被划分为两个部分,一块供OS使用,另一块就是MMZ(Media Memory Zone,多媒体内存区域)。操作MMZ内存需要HI_MPI提供的接口。不会申请到MMZ里的内存。MMZ内存的使用情况可以通过命令查看从上图我们可以看出,系统只分配了一块MMZ,名字是’anonymous’,这块MMZ下面目前申请了五块内存。系统内存使用情况可以通过free命令查看从上面能看出来,系统占有57M的内存,MMZ占有64M的内存。
Linux内存管理之mmap详解 (2
wocjj的专栏
04-04 940
原文地址:Linux内存管理之mmap详解 作者:heavent2010 Linux内存管理之mmap详解 一. mmap系统调用 1. mmap系统调用         mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特
应用层mmap驱动mmap之间的联系
10-21 592
应用层mmap驱动mmap之间的联系
怎么通过media foundation将图像数据写入虚拟摄像头_海思MPP:海思的图像内存管理
weixin_39842237的博客
11-15 320
写在前面《海思MPP》系列文章发表之后,通过知乎平台认识了不少也在海思芯片上开发的同学,大家互相交流了很多技术细节,笔者发现在了解“怎么用海思MPP做xxx”之前,有一个必须知道的知识点:海思MPP的图像内存管理。 本文将分享以下两块内容:笔者对于海思MPP的MMZ和VB的认识怎么在MMZ和VB上读写图像I. MPP和VB的介绍关于MPP和VB的概念,笔者在《海思MPP》系列开篇中就介绍过,这里再...
理清海思系统“内存”相关名词
JCYAO_的博客
05-20 679
易失性存储 分成 DRAM 和 SRAM 不刷新就会丢失数据,关机就没 DRAM:即动态随机存取存储器,最为常见的系统内存。利用场效应管的栅极对其衬底间的分布电容来保存信息,以存储电荷的多少,即电容端电压的高低来表示“1”和“0。采用 DRAM的计算机必须配置动态刷新电路,防止信息丢失。DRAM一般用作计算机中的主存储器。 SRAM:特点是工作速度快,只要电源不撤除,写入SRAM的信息就不会消失,不需要刷新电路,同时在读出时不破坏原来存放的信息,一经写入可多次读出。SRAM一般用来作为计算机中的高.
linux驱动mmap地址映射
qq_40684669的博客
11-17 1306
mmap实现,页表介绍
Linux驱动开发——物理地址映射(①)
跑不了的你的博客
01-02 2690
文章目录Linux驱动开发之物理地址映射ioremap函数使用方式方式一方式二不使用GPIO库函数方式来修改之前操作LED灯代码示例总结 Linux驱动开发之物理地址映射 如果不采用GPIO库函数,那么我们如何能在底层驱动中访问外设对应的硬件寄存器呢?是像类似单片机编程一样直接对硬件寄存器访问么? 在Linux系统中,不管是在用户空间还是内核空间一律不允许直接访问硬件外设的基地址(包括寄存器...
Linux文件编程5-文件的内存映射(mmap)
最新发布
Torch_HXM的博客
09-19 121
频繁地读取和写入文件十分耗时,如果能将文件加载到内存中,则读取和写入的主体就变成了程序和内存而不是程序和文件,大大减小时间开销。另外,将文件映射到内存中并由多个进程访问,能够实现进程之间共享内存。,表示进程独享该内存映射,并且不将内存映射中的修改写入原文件(时间上此处由于prot参数的问题也无法修改)。中的prot和flag参数,就可以让我们具有权限去实现修改内存映射,并将内存映射保存到原文件中。,表示这块内存映射可以读取;先向文件中追加了一个字符’\0’,来满足。函数中,我们设置prot参数为。
【转载】利用签名驱动漏洞加载未签名驱动
时空之中的灵魂
06-14 695
设置电脑为测试模式加载驱动,某些游戏在该模式下不运行。自己写的驱动没有签名,系统不加载。自己写的驱动没有签名,系统不加载。
Linux ------- 内存映射(1)
LFQ_516的博客
10-27 567
一、内存映射的原理     内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,同样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间&lt;----&gt;用户空间两者之间需要大量数据传输等操作的话效率是非常高的。 内存映射分为2种: 1.文件映射:将一个普通文件的全部或者一部分内容映射到进程的虚拟内存中...
linux内存映射函数mmap
07-28
mmap函数是Linux系统中用于内存映射的函数之一。它的原型如下: ```c void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` mmap函数的作用是将一个文件或者设备映射到进程的虚拟地址空间。它可以将文件的内容或者设备的I/O空间直接映射到进程的内存中,使得进程可以通过内存访问这些数据,而无需进行繁琐的读写操作。 参数说明: - addr:映射的起始地址,一般设置为NULL,由系统自动分配。 - length:映射的长度,以字节为单位。 - prot:映射区域的保护方式,可以是以下几个值的组合: - PROT_EXEC:可执行。 - PROT_READ:可读。 - PROT_WRITE:可写。 - PROT_NONE:无权限。 - flags:映射区域的标志位,可以是以下几个值的组合: - MAP_SHARED:与其他进程共享该映射区域。 - MAP_PRIVATE:独占该映射区域,对其进行修改不会影响其他进程。 - MAP_ANONYMOUS:创建一个匿名映射区域,不与文件关联。 - fd:要映射的文件描述符。 - offset:映射文件的偏移量。 mmap函数返回映射区域的起始地址,如果映射失败,则返回MAP_FAILED。 使用mmap函数可以方便地进行文件的读写操作,也可以用于实现进程间的共享内存。需要注意的是,在使用完映射区域后,需要使用munmap函数进行解除映射,以释放资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 避免跳出谷歌人机验证 reCAPTCHA界面的解决方法 203203
  • 零拷贝原理详解 59979
  • 错误ValueError: Object arrays cannot be loaded when allow_pickle=False的解决 57191
  • teraterm--一款超好用堪比MobaXterm的远程终端连接软件,能解决shell端tab键等自动补全命令功能的缺失问题 55516
  • 把编译好的ko文件加载模块时出错:Error: could not insert module hello_world.ko: Invalid module format 55313

分类专栏

  • Linux C开发 26篇
  • 计算机常识 11篇
  • VScode使用技巧 3篇
  • Linux内核驱动 17篇
  • SDN的P4学习 5篇
  • 计算机网络 1篇
  • dpdk 3篇
  • cavium芯片包处理 9篇
  • 读书笔记 3篇
  • Android开发 1篇
  • Autojs 1篇
  • MATLAB安装 3篇
  • UIO用户空间驱动 1篇
  • 算法难题
  • 机器学习 1篇
  • 损失函数 1篇

最新评论

  • Linux驱动mmap内存映射详解及例子实现

    修诡道的马前卒: 请问spi要怎么改写呢,参数要怎么设置呢

  • 关于Linux库(动态/静态库)、库文件、库函数,头文件、链接方式以及相关操作的知识汇总

    unlimited blade: 科普的很好,很系统化,给楼主点赞

  • VScode通过remote ssh连接虚拟机 & 报错 过程试图写入的管道不存在(已解决)

    qq_45915612: 我调整了.ssh的用户组权限http://t.csdnimg.cn/wUnwy,修改ssh默认的环境变量http://t.csdnimg.cn/NxwxX,调整了config文件的用户名等信息http://t.csdnimg.cn/1z7X7,删除了know_hosts文件里的对应服务器信息http://t.csdnimg.cn/6Z7t2,连上了。

  • 360安全浏览器总是锁屏解锁后自启动打开网页,烦~

    m0_65931746: 360壁纸那个选项,你关闭一段时间后它自己会偷偷给你打开

  • Ubuntu 18启动失败 Started Hold until boot procss finishes up

    Eruptin: 作者您好,我按照第一种方式清理了之后还是无法启动,该怎么办呢?

最新文章

  • 柔性数组详解
  • 360安全浏览器总是锁屏解锁后自启动打开网页,烦~
  • 运用paramiko远程执行命令报错:TypeError: from_buffer() cannot return the address of the raw string withi....
2021年2篇
2020年19篇
2019年116篇

目录

目录

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家益阳玻璃钢外壳价格黑河玻璃钢树池坐凳制作河南不锈钢雕塑加工宣城不锈钢雕塑厂家直销阳江玻璃钢加工北海商场美陈制作信阳玻璃钢制品公司汕尾玻璃钢前台批发昌都玻璃钢机械外壳哪家好江门玻璃钢动物雕塑价格新余玻璃钢摆件生产厂家东营玻璃钢垃圾桶定制吕梁玻璃钢垃圾桶加工湖南玻璃钢花坛制作盘锦玻璃钢花池制作湖北玻璃钢家具公司黑龙江玻璃钢造型加工宜昌玻璃钢机械外壳价格吉安玻璃钢垃圾桶厂家四平玻璃钢茶几制造南宁玻璃钢花钵生产厂家贵港玻璃钢树池厂家眉山玻璃钢花盆生产厂家梧州玻璃钢花盆价格吉林玻璃钢花钵生产厂家中山不锈钢花盆制造荆门玻璃钢公仔雕塑厂家绵阳玻璃钢坐凳厂家临沂玻璃钢人物雕塑公司福建玻璃钢雕塑厂家直销香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化