
munmap函数 kcore算法
商是两位数的除法-洗澡杨绛
2023年3月3日发(作者:我上学了)mmap函数使⽤与实例详解
外⽂名释义参数作⽤系
统
⽬录
mmap
mmap将⼀个⽂件或者其它对象映射进内存。⽂件被映射到多个页上,如果⽂件的⼤⼩不是所有页的⼤⼩之和,最后⼀个页不被使⽤的空间将会清零。
mmap在⽤户空间映射调⽤系统中作⽤很⼤。
头⽂件
函数原型
void*mmap(void*start,size_tlength,intprot,intflags,intfd,off_toffset);
intmunmap(void*start,size_tlength);
mmap()必须以PAGE_SIZE为单位进⾏映射,⽽内存也只能以页为单位进⾏,若要映射⾮PAGE_SIZE整数倍的地址范围,要先进⾏内存对齐,强⾏
以PAGE_SIZE的倍数⼤⼩进⾏映射。
start:映射区的开始地址,设置为0时表⽰由系统决定映射区的起始地址。
length:映射区的长度。//长度单位是以字节为单位,不⾜⼀内存页按⼀内存页处理
prot:期望的内存保护标志,不能与⽂件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在⼀起
PROT_EXEC//页内容可以被执⾏
PROT_READ//页内容可以被读取
PROT_WRITE//页可以被写⼊
PROT_NONE//页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是⼀个或者多个以下位的组合体
MAP_FIXED//使⽤指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可⽤,
操作将会失败。并且起始地址必须落在页的边界上。
MAP_SHARED//与其它所有映射这个对象的进程共享映射空间。对共享区的写⼊,相当于输出到⽂件。直到msync()或者()被调⽤,⽂件实际上不会被更
新。
MAP_PRIVATE//建⽴⼀个写⼊时拷贝的私有映射。内存区域的写⼊不会影响到原⽂件。这个标志和以上标志是互斥的,只能使⽤其中⼀个。
MAP_DENYWRITE//这个标志被忽略。
MAP_EXECUTABLE//同上
MAP_NORESERVE//不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时,对映射区的修改
会引起段违例信号。
mmap
⼀个⽂件或者其它对象映射进内存
start、length
LinuxorUnix
1
2
3
4
5
6
▪
▪
▪
▪
7
▪
▪
1
条件
[1]
参数说明
MAP_LOCKED//锁定映射区的页⾯,从⽽防⽌页⾯被交换出内存。
MAP_GROWSDOWN//⽤于堆栈,告诉VM系统,映射区可以向下扩展。
MAP_ANONYMOUS//匿名映射,映射区不与任何⽂件关联。
MAP_ANON//MAP_ANONYMOUS的别称,不再被使⽤。
MAP_FILE//兼容标志,被忽略。
MAP_32BIT//将映射区放在进程的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到⽀持。
MAP_POPULATE//为⽂件映射通过预读的⽅式准备好页表。随后对映射区的访问不会被页违例阻塞。
MAP_NONBLOCK//仅和MAP_POPULATE⼀起使⽤时才有意义。不执⾏预读,只为已存在于内存中的页⾯建⽴⼊⼝。
fd:有效的。⼀般是由()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进⾏的是匿名映射。
off_toffset:被映射对象内容的起点。
成功执⾏时,mmap()返回被映射区的,()返回0。失败时,mmap()返回MAP_FAILED[其值为(void*)-1],munmap返回-1。errno被设为以下的某个值
EACCES:访问出错
EAGAIN:⽂件已被锁定,或者太多的内存已被锁定
EBADF:fd不是有效的
EINVAL:⼀个或者多个参数⽆效
ENFILE:已达到系统对打开⽂件的限制
ENODEV:指定⽂件所在的⽂件系统不⽀持内存映射
ENOMEM:,或者进程已超出最⼤内存映射数量
EPERM:权能不⾜,操作不允许
ETXTBSY:已写的⽅式打开⽂件,同时指定MAP_DENYWRITE标志
SIGSEGV:试着向只读区写⼊
SIGBUS:试着访问不属于进程的内存区
mmap操作提供了⼀种机制,让直接访问设备内存,这种机制,相⽐较在和互相拷贝数据,效率更⾼。在要求⾼性能的应⽤中⽐较常⽤。mmap映射内存
必须是页⾯⼤⼩的整数倍,⾯向流的设备不能进⾏mmap,mmap的实现和硬件有关。
mmap()系统调⽤使得进程之间通过映射同⼀个普通⽂件实现共享内存。普通⽂件被映射到进程地址空间后,进程可以像访问普通内存⼀样对⽂件进⾏访
问,不必再调⽤read(),write()等操作。
注:实际上,mmap()系统调⽤并不是完全为了⽤于共享内存⽽设计的。它本⾝提供了不同于⼀般对普通⽂件的访问⽅式,进程可以像读写内存⼀样对普通
⽂件的操作。⽽Posix或SystemV的共享内存IPC则纯粹⽤于共享⽬的,当然mmap()实现共享内存也是其主要应⽤之⼀。
1、mmap()系统调⽤形式如下:
void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset)
参数fd为即将映射到进程空间的⽂件描述字,⼀般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进⾏的是匿名映射
(不涉及具体的⽂件名,避免了⽂件的创建及打开,很显然只能⽤于具有亲缘关系的进程间通信)。len是映射到调⽤进程地址空间的字节数,它从被映射⽂件
开头offset个字节开始算起。prot参数指定共享内存的访问权限。可取如下⼏个值的或:PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可
执⾏),PROT_NONE(不可访问)。flags由以下⼏个常值指定:MAP_SHARED,MAP_PRIVATE,MAP_FIXED,其中,MAP_SHARED,MAP_PRIVATE必
选其⼀,⽽MAP_FIXED则不推荐使⽤。offset参数⼀般设为0,表⽰从⽂件头开始映射。参数addr指定⽂件应被映射到进程空间的起始地址,⼀般被指定⼀个空
指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后⽂件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。这⾥不再详细
介绍mmap()的参数,读者可参考mmap()⼿册页获得进⼀步的信息。
返回说明
设备操作
系统调⽤
2、系统调⽤mmap()⽤于共享内存的两种⽅式:
(1)使⽤普通⽂件提供的内存映射:适⽤于任何进程之间;此时,需要打开或创建⼀个⽂件,然后再调⽤mmap();典型调⽤代码如下:
fd=open(name,flag,mode);
if(fd<0)
...
ptr=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);通过mmap()实现共享内存的通信⽅式有许多特点和要注意的地⽅,我们将
在范例中进⾏具体说明。
(2)使⽤特殊⽂件提供匿名内存映射:适⽤于具有亲缘关系的进程之间;由于⽗⼦进程特殊的亲缘关系,在⽗进程中先调⽤mmap(),然后调⽤fork()。那
么在调⽤fork()之后,⼦进程继承⽗进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,⽗⼦进程就可以通过映射区域进⾏通信了。注意,这⾥
不是⼀般的继承关系。⼀般来说,⼦进程单独维护从⽗进程继承下来的⼀些变量。⽽mmap()返回的地址,却由⽗⼦进程共同维护。
对于具有亲缘关系的进程实现共享内存最好的⽅式应该是采⽤匿名内存映射的⽅式。此时,不必指定具体的⽂件,只要设置相应的标志即可,参见范例2。
3、系统调⽤munmap()
intmunmap(void*addr,size_tlen)
该调⽤在进程地址空间中解除⼀个映射关系,addr是调⽤mmap()时返回的地址,len是映射区的⼤⼩。当映射关系解除后,对原来映射地址的访问将导致段
错误发⽣。
4、系统调⽤msync()
intmsync(void*addr,size_tlen,intflags)
⼀般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘⽂件中,往往在调⽤munmap()后才执⾏该操作。可以通过调⽤msync()实现磁盘上
⽂件内容与共享内存区的内容⼀致。
下⾯将给出使⽤mmap()的⼀个范例:范例1给出两个进程通过映射普通⽂件实现共享内存通信;系统调⽤mmap()有许多有趣的地⽅,下⾯是通过
mmap()映射普通⽂件实现进程间的通信的范例,我们通过该范例来说明mmap()实现共享内存的特点及注意事项。
范例1:两个进程通过映射普通⽂件实现共享内存通信
范例1包含两个⼦程序:map_normalfile1.c及map_normalfile2.c。编译两个程序,可执⾏⽂件分别为map_normalfile1及map_normalfile2。两个程序通过
命令⾏参数指定同⼀个⽂件来实现共享内存⽅式的进程间通信。map_normalfile2试图打开命令⾏参数指定的⼀个普通⽂件,把该⽂件映射到进程的地址空间,
并对映射后的地址空间进⾏写操作。map_normalfile1把命令⾏参数指定的⽂件映射到进程地址空间,然后对映射后的地址空间执⾏读操作。这样,两个进程通
过命令⾏参数指定同⼀个⽂件来实现共享内存⽅式的进程间通信。
代码⽰例
下⾯是两个程序代码:
范例
1
2
3
4
5
6
7
8
9
10
11
12
13
/*-------------map_normalfile1.c-----------*/
#include
#include
#include
#include
#include
#include
typedef
struct
{
char
name[4];
int
age;
}people;
void
main(
int
argc,
char
**argv)
//mapanormalfileassharedmem:
{
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
int
fd,i;
people*p_map;
char
temp;
fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,
sizeof
(people)*5-1,SEEK_SET);
write(fd,
\"\"
,1);
p_map=(people*)mmap(NULL,
sizeof
(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
temp=
\'a\'
;
for
(i=0;i<10;i++)
{
temp+=1;
memcpy
((*(p_map+i)).name,&temp,2);
(*(p_map+i)).age=20+i;
}
printf
(
\"initializeovern\"
);
sleep(10);
munmap(p_map,
sizeof
(people)*10);
printf
(
\"umapokn\"
);
}
/*-------------map_normalfile2.c-----------*/
#include
#include
#include
#include
#include
代码解释
map_normalfile1.c⾸先定义了⼀个people数据结构,(在这⾥采⽤数据结构的⽅式是因为,共享内存区的数据往往是有固定格式的,这由通信的各个进程
决定,采⽤结构的⽅式有普遍代表性)。map_normfile1⾸先打开或创建⼀个⽂件,并把⽂件的长度设置为5个people结构⼤⼩。然后从mmap()的返回地址开
始,设置了10个people结构。然后,进程睡眠10秒钟,等待其他进程映射同⼀个⽂件,最后解除映射。
map_normfile2.c只是简单的映射⼀个⽂件,并以people数据结构的格式从mmap()返回的地址处读取10个people结构,并输出读取的值,然后解除映射。
分别把两个程序编译成可执⾏⽂件map_normalfile1和map_normalfile2后,在⼀个终端上先运⾏./map_normalfile1/tmp/test_shm,程序输出结果如下:
initializeover
umapok
在map_normalfile1输出initializeover之后,输出umapok之前,在另⼀个终端上运⾏map_normalfile2/tmp/test_shm,将会产⽣如下输出(为了节省空
间,输出结果为稍作整理后的结果):
name:bage20;name:cage21;name:dage22;name:eage23;name:fage24;
name:gage25;name:hage26;name:Iage27;name:jage28;name:kage29;
在map_normalfile1输出umapok后,运⾏map_normalfile2则输出如下结果:
name:bage20;name:cage21;name:dage22;name:eage23;name:fage24;
name:age0;name:age0;name:age0;name:age0;name:age0;
结论
46
47
48
49
50
51
52
53
54
55
56
#include
typedef
struct
{
char
name[4];
int
age;
}people;
void
main(
int
argc,
char
**argv)
//mapanormalfileassharedmem:
{
int
fd,i;
people*p_map;
fd=open(argv[1],O_CREAT|O_RDWR,00777);
p_map=(people*)mmap(NULL,
sizeof
(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
for
(i=0;i<10;i++)
{
printf
(
\"name:%sage%d;n\"
,(*(p_map+i)).name,(*(p_map+i)).age);
}
munmap(p_map,
sizeof
(people)*10);
}
从程序的运⾏结果中可以得出的结论
1、最终被映射⽂件的内容的长度不会超过⽂件本⾝的初始⼤⼩,即映射不能改变⽂件的⼤⼩;
2、可以⽤于进程通信的有效地址空间⼤⼩⼤体上受限于被映射⽂件的⼤⼩,但不完全受限于⽂件⼤⼩。打开⽂件被截短为5个people结构⼤⼩,⽽在
map_normalfile1中初始化了10个people数据结构,在恰当时候(map_normalfile1输出initializeover之后,输出umapok之前)调⽤map_normalfile2会发现
map_normalfile2将输出全部10个people结构的值,后⾯将给出详细讨论。
注:在linux中,内存的保护是以页为基本单位的,即使被映射⽂件只有⼀个字节⼤⼩,内核也会为映射分配⼀个页⾯⼤⼩的内存。当被映射⽂件⼩于⼀个
页⾯⼤⼩时,进程可以对从mmap()返回地址开始的⼀个页⾯⼤⼩进⾏访问,⽽不会出错;但是,如果对⼀个页⾯以外的地址空间进⾏访问,则导致错误发⽣,
后⾯将进⼀步描述。因此,可⽤于进程间通信的有效地址空间⼤⼩不会超过⽂件⼤⼩及⼀个页⾯⼤⼩的和。
3、⽂件⼀旦被映射后,调⽤mmap()的进程对返回地址的访问是对某⼀内存区域的访问,暂时脱离了磁盘上⽂件的影响。所有对mmap()返回地址空间的操
作只在内存中有意义,只有在调⽤了munmap()后或者msync()时,才把内存中的相应内容写回磁盘⽂件,所写内容仍然不能超过⽂件的⼤⼩。
其他
执⾏相反的操作,删除特定地址区域的对象映射,基于⽂件的映射,在mmap和munmap执⾏过程的任何时刻,被映射⽂件的st_atime可能被更新。如果
st_atime字段在前述的情况下没有得到更新,⾸次对映射区的第⼀个页索引时会更新该字段的值。⽤PROT_WRITE和MAP_SHARED标志建⽴起来的⽂件映射
其st_ctime和st_mtime,在对映射区写⼊之后但在msync()通过MS_SYNC和MS_ASYNC两个标志调⽤之前会被更新
集成环境
mmap是mac+mysql+apache+php的集成环境的简称,主要⽤于在mac本地快速创建php环境,对于phper是相当不错的软件。
扩展名
.mmap是思维导图MindManager软件的存储格式.
其他含义