将数组作为共享内存,mmap函数要怎么写呢?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将数组作为共享内存,mmap函数要怎么写呢?相关的知识,希望对你有一定的参考价值。

新手求教啊
我定义了一个数组int a[20],然后想用mmap函数进行映射,然后就写了这句:
a=(int *)mmap(NULL,sizeof(int)*20,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
但是报错说a是int [20]类型的和int * 的类型不符合,改成int 或者改成 int[20]或者改成a[]都还是错,精疲力竭了……求问到底要怎么改啊…… 难道一定要用struct结构体才可以吗?可是我就只要用到一个数组就可以了…
再问一个,新建线程的时候想用pthread_create,但是我线程调用的函数的参数是int类型的,
void *create(void *arg)

int *num;
num=(int *)arg;
……
int main(void)

int test=4;int *arg=&test;
……
ret=pthread_create(&tidp,NULL,create,(void *)arg);
……
这种方法已经试过了。
invalid operands to binary * (have ‘unsigned int’ and ‘void *’)报错了……
我就想问我要怎么样才能将int类型的传递进去呢,我的函数里确实就是要传入一个整形变量的。

mmap函数的使用方法 UNIX网络编程第二卷进程间通信对mmap函数进行了说明。该函数主要用途有三个:1、将一个普通文件映射到内存中bf通常在需要对文件进行频繁读写时使用v这样用内存读写取代I/O读写,以获得较高的性能;2、将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;3、为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize); 参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。参数length:代表将文件中多大的部分映射到内存。参数prot:映射区域的保护方式。可以为以下几种方式的组合:PROT_EXEC 映射区域可被执行PROT_READ 映射区域可被读取PROT_WRITE 映射区域可被写入PROT_NONE 映射区域不能存取参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正s通常不鼓励用此旗标。MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。MAP_ANONYMOUS建立匿名映射。此时会忽略参数fddhl不涉及文件,而且映射区域无法和其他进程共享。MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。错误代码:EBADF 参数fd 不是有效的文件描述词EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。EINVAL 参数start、length 或offset有一个不合法。EAGAIN 文件被锁譼蚴怯刑嗄诖姹凰 #牛危希停牛汀∧诖娌蛔恪O低车饔茫恚恚幔穑ǎ┯糜诠蚕砟诖娴牧街址绞剑骸。ǎ保┦褂闷胀ㄎ募峁┑哪诖嬗成洌菏视糜谌魏谓讨洹4耸保枰蚩虼唇ㄒ桓鑫募缓笤俚饔茫恚恚幔穑ǎ┑湫偷饔么肴缦拢海妫洌剑铮穑澹睿ǎ睿幔恚澹。妫欤幔纾。恚铮洌澹弧。椋妫ǎ妫洌Γ欤簦唬埃。穑簦颍剑恚恚幔穑ǎ危眨蹋蹋。欤澹睢。。校遥希裕撸遥牛粒模校遥希裕撸祝遥桑裕牛。停粒校撸樱龋粒遥牛摹。。妫洹。。埃弧⊥ü恚恚幔穑ǎ┦担嘞氯模荆追问

非常感谢你回答我,但是你这个复制也复制的太乱了吧,而且关于mapp函数介绍什么的我都看过了。

参考技术A 不需要先定义int a[20],mmap函数自己就能申请到内存。
int * p = (int*)mmap(NULL, .....);
p[0] = 10;本回答被提问者采纳

mmap详谈

简述:

  mmap函数将文件系统内的文件或者是Posix共享内存对象映射到调用进程的地址空间

用途:

  1.对普通文件使用mmap提供内存映射I/O,以避免系统调用(read、write、lseek)带来的性能开销。同时减少了数据在内核缓冲区和进程地址空间的拷贝次数。

  2.使用特殊文件提供匿名内存映射。

  3.使用shm_open以提供无亲缘关系进程间的Posix共享内存区。

接口说明:

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

mmap返回成功后,fd文件描述符可以关闭且不会对内存映射产生影响。

对内存区的保护由prot参数指定:

PROT_EXEC:数据可执行

PROT_READ:数据可读

PROT_WRITE:数据可写

PROT_NONE:数据不可访问

对映射内存区的操作标志由flags指定:

MAP_SHARED:对内存区数据操作的变动是共享的。即所有映射到该内存地址的进程都能收到该数据的变动。

MAP_PRIVATE:对内存区数据的操作进行写时复制。(注:仅仅在内存中有多一份拷贝,文件并不受到影响)

mmap函数通过文件描述符定位到文件位置再到用户地址空间:

技术图片

int munmap(void *addr, size_t len);

从某个进程的地址空间删除一个内存映射关系。len是映射区的大小。

如果被映射区是使用MAP_PRIVATE标志映射的,那么调用进程对它的所有变动都不会写回文件,并且被丢弃。

细节:

1.考虑不同的进程调用mmap内存映射同一个文件(前提设置了MAP_SHARED),那么某个进程对这个内存映射的修改是否会影响其他进程的内存映射。

//orgin.c用于打印内存映射区内的值
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
    int fd = open("./testfile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
    int num = 0;
    write(fd, &num, sizeof(int));
    int *ptr = mmap(NULL, sizeof(int), PROT_WRITE, MAP_PRIVATE, fd, 0);
    sleep(10);                                                                                                                               
    printf("%d
", *ptr);
}

 

//mod.c用于修改内存映射区内的数据
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
    int fd = open("./testfile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
    int num = 0;
    write(fd, &num, sizeof(int));
    int *ptr = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    printf("%d
", ++(*ptr));                                                                                                                
}

结论:

当不同的进程映射同一个文件的时候,内存映射区其实变为内核为各个进程维护的共享内存区。各个进程对内存映射区的修改都会写回文件,并影响其他进程在该共享内存上的值。

 

2.内存分配策略以页为单位(假设一页为4096个字节)。考虑内存映射空间和文件大小都为5000个字节的时候,文件映射的读写会如何。

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

int main()
{
    int fileSize = 5000;
    int mapSize = 5000;
    long pageSize;

    int fd = open("./testfile", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IRUSR);
    lseek(fd, fileSize-1, SEEK_SET);
    write(fd, "", 1);
    char *ptr = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    
    pageSize = sysconf(_SC_PAGESIZE);

    int i;
    for (i = 0; i < 5000; i+=pageSize)
    {
        //打印每个页的首字节内容,并置为1
        printf("ptr[%d] = %d
", i, ptr[i]);
        ptr[i] = 1;
        //打印每个页的尾字节内容,并置为1
        printf("ptr[%d] = %d
", i + pageSize - 1, ptr[i+pageSize-1]);
        ptr[i+pageSize-1] = 1;
    }
    return 0;
}

输出结果:

ptr[0] = 0
ptr[4095] = 0
ptr[4096] = 0
ptr[8191] = 0

使用命令:od -b(以八进制形式查看内容) -A b(以十进制输出地址) testfile 查看地址对应的内容。

技术图片

结果:

内存映射其实分配了2个页的大小(第一个页0~4095字节,第二个页4096~8191字节),对于在这两个页范围内的读和写操作都不会导致“Segmentation Fault”,但对在5000~8191字节范围内的写操作不会写回文件中(因为文件大小为5000字节)。对于超过8191字节(即第三个页)的读写将导致段错误。

技术图片

 

结论:

在内存分配的页范围内的读写不会导致段错误,在文件大小范围内的写操作才会写回文件。

 

3.进程采用mmap操作文件内容一定比readwrite快吗?

讨论这个问题前先看进程采用系统条用read/write方式读写文件过程。

数据先从文件系统复制到内核缓冲区(最小单位为一个页大小)---->复制数据到用户的进程空间;此过程中如果进程的下一次读操作的数据依然在内核缓冲区的话内核不会再从文件系统中读取,而是将数据从内核缓冲区直接复制给进程。

后续对文件的操作还需要其他的系统调用如lseekwrite。进程将多次在内核态和用户态之间切换。而mmap则只需一次系统调用,其后操作都在用户态上。

由此可知:

对于小文件的读写操作,内核完全有可能直接从内核缓冲区读写数据并不见得会比mmap慢,但是对于大文件且频繁读写的操作,mmap会比readwrite要快。

 

完。

you knon i am not leaving you.right?

以上是关于将数组作为共享内存,mmap函数要怎么写呢?的主要内容,如果未能解决你的问题,请参考以下文章

Android跨进程通信-mmap函数

mmap详谈

mmap实现进程内写数据和读数据

mmap实现进程内写数据和读数据

mmap实现进程内写数据和读数据

使用 mmap() 为 2D 数组初始化共享内存,是不是还需要为后续指针映射内存?我应该改用 shm 吗?