更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_syscall.h 文件参考

浏览源代码.

函数

STATIC INLINE BOOL LOS_IsNamedMapping (unsigned long flags)
 
STATIC INLINE BOOL LOS_IsAnonymousMapping (unsigned long flags)
 是否匿名映射 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0 更多...
 
VADDR_T LOS_MMap (VADDR_T vaddr, size_t len, unsigned prot, unsigned long flags, int fd, unsigned long pgoff)
 
STATUS_T LOS_UnMMap (VADDR_T addr, size_t size)
 解除映射关系 更多...
 
VOID * LOS_DoBrk (VOID *addr)
 
INT32 LOS_DoMprotect (VADDR_T vaddr, size_t len, unsigned long prot)
 修改内存段的访问权限 更多...
 
VADDR_T LOS_DoMremap (VADDR_T oldAddress, size_t oldSize, size_t newSize, int flags, VADDR_T newAddr)
 重新映射虚拟内存地址。 更多...
 
VOID LOS_DumpMemRegion (VADDR_T vaddr)
 输出内存线性区 更多...
 
UINT32 ShmInit (VOID)
 
UINT32 ShmDeinit (VOID)
 
INT32 ShmGet (key_t key, size_t size, INT32 shmflg)
 ShmGet
得到一个共享内存标识符或创建一个共享内存对象 更多...
 
VOID * ShmAt (INT32 shmid, const VOID *shmaddr, INT32 shmflg)
 ShmAt
用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。 更多...
 
INT32 ShmCtl (INT32 shmid, INT32 cmd, struct shmid_ds *buf)
 ShmCtl
此函数可以对shmid指定的共享存储进行多种操作(删除、取信息、加锁、解锁等) 更多...
 
INT32 ShmDt (const VOID *shmaddr)
 当对共享存储的操作已经结束时,则调用shmdt与该存储段分离 如果shmat成功执行,那么内核将使与该共享存储相关的shmid_ds结构中的shm_nattch计数器值减1 更多...
 

函数说明

◆ LOS_DoBrk()

VOID * LOS_DoBrk ( VOID *  addr)
 用户进程向内核申请空间,进一步说用于扩展用户堆栈空间,或者回收用户堆栈空间
 扩展当前进程的堆空间
 一个进程所有的线性区都在进程指定的线性地址范围内,
 线性区之间是不会有地址的重叠的,开始都是连续的,随着进程的运行出现了释放再分配的情况
 由此出现了断断续续的线性区,内核回收线性区时会检测是否和周边的线性区可合并成一个更大
 的线性区用于分配。
参数
addr
返回
VOID*

在文件 los_vm_syscall.c259 行定义.

260{
262 size_t size;
263 VOID *ret = NULL;
264 LosVmMapRegion *region = NULL;
265 VOID *alignAddr = NULL;
266 VOID *shrinkAddr = NULL;
267
268 if (addr == NULL) {//参数地址未传情况
269 return (void *)(UINTPTR)space->heapNow;//以现有指向地址为基础进行扩展
270 }
271
272 if ((UINTPTR)addr < (UINTPTR)space->heapBase) {//heapBase是堆区的开始地址,所以参数地址不能低于它
273 return (VOID *)-ENOMEM;
274 }
275
276 size = (UINTPTR)addr - (UINTPTR)space->heapBase;//算出大小
277 size = ROUNDUP(size, PAGE_SIZE); //圆整size
278 alignAddr = (CHAR *)(UINTPTR)(space->heapBase) + size;//得到新的线性区的结束地址
279 PRINT_INFO("brk addr %p , size 0x%x, alignAddr %p, align %d\n", addr, size, alignAddr, PAGE_SIZE);
280
281 (VOID)LOS_MuxAcquire(&space->regionMux);
282 if (addr < (VOID *)(UINTPTR)space->heapNow) {//如果地址小于堆区现地址
283 shrinkAddr = OsShrinkHeap(addr, space);//收缩堆区
284 (VOID)LOS_MuxRelease(&space->regionMux);
285 return shrinkAddr;
286 }
287
288 if ((UINTPTR)alignAddr >= space->mapBase) {//参数地址 大于映射区地址
289 VM_ERR("Process heap memory space is insufficient");//进程堆空间不足
290 ret = (VOID *)-ENOMEM;
291 goto REGION_ALLOC_FAILED;
292 }
293
294 if (space->heapBase == space->heapNow) {//往往是第一次调用本函数才会出现,因为初始化时 heapBase = heapNow
295 region = LOS_RegionAlloc(space, space->heapBase, size,//分配一个可读/可写/可使用的线性区,只需分配一次
296 VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |//线性区的大小由range.size决定
297 VM_MAP_REGION_FLAG_FIXED | VM_MAP_REGION_FLAG_PERM_USER, 0);
298 if (region == NULL) {
299 ret = (VOID *)-ENOMEM;
300 VM_ERR("LOS_RegionAlloc failed");
301 goto REGION_ALLOC_FAILED;
302 }
303 region->regionFlags |= VM_MAP_REGION_FLAG_HEAP;//贴上线性区类型为堆区的标签,注意一个线性区可以有多种标签
304 space->heap = region;//指定线性区为堆区
305 }
306
307 space->heapNow = (VADDR_T)(UINTPTR)alignAddr;//更新堆区顶部位置
308 space->heap->range.size = size; //更新堆区大小,经此操作线性区变大或缩小了
309 ret = (VOID *)(UINTPTR)space->heapNow;//返回堆顶
310
311REGION_ALLOC_FAILED:
312 (VOID)LOS_MuxRelease(&space->regionMux);
313 return ret;
314}
STATIC INLINE LosProcessCB * OsCurrProcessGet(VOID)
unsigned long VADDR_T
Definition: los_typedef.h:208
unsigned long UINTPTR
Definition: los_typedef.h:68
char CHAR
Definition: los_typedef.h:63
STATIC INLINE STATUS_T LOS_MuxAcquire(LosMux *m)
Definition: los_vm_lock.h:48
STATIC INLINE STATUS_T LOS_MuxRelease(LosMux *m)
Definition: los_vm_lock.h:53
LosVmMapRegion * LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff)
Definition: los_vm_map.c:581
VOID * OsShrinkHeap(VOID *addr, LosVmSpace *space)
收缩堆区
LosVmSpace * vmSpace
UINT32 size
Definition: los_vm_map.h:85
UINT32 regionFlags
Definition: los_vm_map.h:125
LosVmMapRange range
Definition: los_vm_map.h:123
虚拟空间,每个进程都有一个属于自己的虚拟内存地址空间
Definition: los_vm_map.h:146
LosVmMapRegion * heap
Definition: los_vm_map.h:154
VADDR_T mapBase
Definition: los_vm_map.h:155
VADDR_T heapBase
Definition: los_vm_map.h:152
LosMux regionMux
Definition: los_vm_map.h:149
VADDR_T heapNow
Definition: los_vm_map.h:153
函数调用图:
这是这个函数的调用关系图:

◆ LOS_DoMprotect()

INT32 LOS_DoMprotect ( VADDR_T  vaddr,
size_t  len,
unsigned long  prot 
)

修改内存段的访问权限

在文件 los_vm_syscall.c337 行定义.

338{
340 LosVmMapRegion *region = NULL;
341 UINT32 vmFlags;
342 UINT32 count;
343 int ret;
344
345 (VOID)LOS_MuxAcquire(&space->regionMux);
346 region = LOS_RegionFind(space, vaddr);//通过虚拟地址找到线性区
347 if (!IS_ALIGNED(vaddr, PAGE_SIZE) || (region == NULL) || (vaddr > vaddr + len)) {
348 ret = -EINVAL;
349 goto OUT_MPROTECT;
350 }
351
352 if ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))) {
353 ret = -EINVAL;
354 goto OUT_MPROTECT;
355 }
356 //如果是堆区或VDSO区,说明区内容是不能修改的
357 if ((region->regionFlags & VM_MAP_REGION_FLAG_VDSO) || (region->regionFlags & VM_MAP_REGION_FLAG_HEAP)) {
358 ret = -EPERM;
359 goto OUT_MPROTECT;
360 }
361 //如果是共享文件,说明内容也不能修改
362 if (LOS_IsRegionTypeFile(region) && (region->regionFlags & VM_MAP_REGION_FLAG_SHARED)) {
363 if (!OsProtMprotectPermCheck(prot, region)) {
364 ret = -EACCES;
365 goto OUT_MPROTECT;
366 }
367 }
368 len = LOS_Align(len, PAGE_SIZE);
369 /* can't operation cross region */
370 if ((region->range.base + region->range.size) < (vaddr + len)) {
371 ret = -EINVAL;
372 goto OUT_MPROTECT;
373 }
374
375 /* if only move some part of region, we need to split first */
376 if (region->range.size > len) {//如果只修改部分区域,我们需要先拆分区
377 OsVmRegionAdjust(space, vaddr, len);//调整下线性区范围
378 }
379
380 vmFlags = OsCvtProtFlagsToRegionFlags(prot, 0);//转换FLAGS
381 vmFlags |= (region->regionFlags & VM_MAP_REGION_FLAG_SHARED) ? VM_MAP_REGION_FLAG_SHARED : 0;
382 vmFlags |= OsInheritOldRegionName(region->regionFlags);
383 region = LOS_RegionFind(space, vaddr);
384 if (region == NULL) {
385 ret = -ENOMEM;
386 goto OUT_MPROTECT;
387 }
388 region->regionFlags = vmFlags;
389 count = len >> PAGE_SHIFT;
390 ret = LOS_ArchMmuChangeProt(&space->archMmu, vaddr, count, region->regionFlags);//修改访问权限实体函数
391 if (ret) {
392 ret = -ENOMEM;
393 goto OUT_MPROTECT;
394 }
395 ret = LOS_OK;
396
397OUT_MPROTECT:
398#ifdef LOSCFG_VM_OVERLAP_CHECK
399 if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
400 (VOID)OsShellCmdDumpVm(0, NULL);
401 ret = -ENOMEM;
402 }
403#endif
404
405 (VOID)LOS_MuxRelease(&space->regionMux);
406 return ret;
407}
LITE_OS_SEC_TEXT UINTPTR LOS_Align(UINTPTR addr, UINT32 boundary)
Align the value (addr) by some bytes (boundary) you specify.
Definition: los_misc.c:35
STATUS_T LOS_ArchMmuChangeProt(LosArchMmu *archMmu, VADDR_T vaddr, size_t count, UINT32 flags)
LOS_ArchMmuChangeProt 修改进程空间虚拟地址区间的映射属性 改变内存段的访问权限,例如: 读/写/可执行/不可用 ==
Definition: los_arch_mmu.c:949
unsigned int UINT32
Definition: los_typedef.h:57
STATIC INLINE BOOL LOS_IsRegionTypeFile(LosVmMapRegion *region)
是否为文件映射区
Definition: los_vm_map.h:234
STATIC INLINE UINT32 OsCvtProtFlagsToRegionFlags(unsigned long prot, unsigned long flags)
从外部权限标签转化为线性区权限标签
Definition: los_vm_map.h:197
LosVmMapRegion * LOS_RegionFind(LosVmSpace *vmSpace, VADDR_T addr)
查找线性区 根据起始地址在进程空间内查找是否存在
Definition: los_vm_map.c:414
STATUS_T OsVmRegionAdjust(LosVmSpace *space, VADDR_T vaddr, size_t size)
STATIC UINT32 OsInheritOldRegionName(UINT32 oldRegionFlags)
继承老线性区的标签
STATIC INLINE BOOL OsProtMprotectPermCheck(unsigned long prot, LosVmMapRegion *region)
VADDR_T base
Definition: los_vm_map.h:84
LosArchMmu archMmu
Definition: los_vm_map.h:157
LITE_OS_SEC_TEXT_MINOR UINT32 OsShellCmdDumpVm(INT32 argc, const CHAR *argv[])
查看进程的虚拟内存使用情况。vmm [-a / -h / –help], vmm [pid]
Definition: vm_shellcmd.c:108
函数调用图:
这是这个函数的调用关系图:

◆ LOS_DoMremap()

VADDR_T LOS_DoMremap ( VADDR_T  oldAddress,
size_t  oldSize,
size_t  newSize,
int  flags,
VADDR_T  newAddr 
)

重新映射虚拟内存地址。

在文件 los_vm_syscall.c460 行定义.

461{
462 LosVmMapRegion *regionOld = NULL;
463 LosVmMapRegion *regionNew = NULL;
464 STATUS_T status;
465 VADDR_T ret;
467
468 oldSize = LOS_Align(oldSize, PAGE_SIZE);
469 newSize = LOS_Align(newSize, PAGE_SIZE);
470
471 (VOID)LOS_MuxAcquire(&space->regionMux);
472
473 status = OsMremapCheck(oldAddress, oldSize, newAddr, newSize, (unsigned int)flags);
474 if (status) {
475 ret = status;
476 goto OUT_MREMAP;
477 }
478
479 /* if only move some part of region, we need to split first */
480 status = OsVmRegionAdjust(space, oldAddress, oldSize);
481 if (status) {
482 ret = -ENOMEM;
483 goto OUT_MREMAP;
484 }
485
486 regionOld = LOS_RegionFind(space, oldAddress);
487 if (regionOld == NULL) {
488 ret = -ENOMEM;
489 goto OUT_MREMAP;
490 }
491
492 if ((unsigned int)flags & MREMAP_FIXED) {
493 regionNew = OsVmRegionDup(space, regionOld, newAddr, newSize);
494 if (!regionNew) {
495 ret = -ENOMEM;
496 goto OUT_MREMAP;
497 }
498 status = LOS_ArchMmuMove(&space->archMmu, oldAddress, newAddr,
499 ((newSize < regionOld->range.size) ? newSize : regionOld->range.size) >> PAGE_SHIFT,
500 regionOld->regionFlags);
501 if (status) {
502 LOS_RegionFree(space, regionNew);
503 ret = -ENOMEM;
504 goto OUT_MREMAP;
505 }
506 LOS_RegionFree(space, regionOld);
507 ret = newAddr;
508 goto OUT_MREMAP;
509 }
510 // take it as shrink operation
511 if (oldSize > newSize) {
512 LOS_UnMMap(oldAddress + newSize, oldSize - newSize);
513 ret = oldAddress;
514 goto OUT_MREMAP;
515 }
516 status = OsIsRegionCanExpand(space, regionOld, newSize);
517 // we can expand directly.
518 if (!status) {
519 regionOld->range.size = newSize;
520 ret = oldAddress;
521 goto OUT_MREMAP;
522 }
523
524 if ((unsigned int)flags & MREMAP_MAYMOVE) {
525 regionNew = OsVmRegionDup(space, regionOld, 0, newSize);
526 if (regionNew == NULL) {
527 ret = -ENOMEM;
528 goto OUT_MREMAP;
529 }
530 status = LOS_ArchMmuMove(&space->archMmu, oldAddress, regionNew->range.base,
531 regionOld->range.size >> PAGE_SHIFT, regionOld->regionFlags);
532 if (status) {
533 LOS_RegionFree(space, regionNew);
534 ret = -ENOMEM;
535 goto OUT_MREMAP;
536 }
537 LOS_RegionFree(space, regionOld);
538 ret = regionNew->range.base;
539 goto OUT_MREMAP;
540 }
541
542 ret = -EINVAL;
543OUT_MREMAP:
544#ifdef LOSCFG_VM_OVERLAP_CHECK
545 if (VmmAspaceRegionsOverlapCheck(aspace) < 0) {
546 (VOID)OsShellCmdDumpVm(0, NULL);
547 ret = -ENOMEM;
548 }
549#endif
550
551 (VOID)LOS_MuxRelease(&space->regionMux);
552 return ret;
553}
STATUS_T LOS_ArchMmuMove(LosArchMmu *archMmu, VADDR_T oldVaddr, VADDR_T newVaddr, size_t count, UINT32 flags)
LOS_ArchMmuMove 将进程空间一个虚拟地址区间的映射关系转移至另一块未使用的虚拟地址区间重新做映射。
Definition: los_arch_mmu.c:996
int STATUS_T
Definition: los_typedef.h:215
LosVmMapRegion * OsVmRegionDup(LosVmSpace *space, LosVmMapRegion *oldRegion, VADDR_T vaddr, size_t size)
STATUS_T OsIsRegionCanExpand(LosVmSpace *space, LosVmMapRegion *region, size_t size)
STATUS_T LOS_RegionFree(LosVmSpace *space, LosVmMapRegion *region)
释放进程空间指定线性区
Definition: los_vm_map.c:694
STATUS_T OsMremapCheck(VADDR_T addr, size_t oldLen, VADDR_T newAddr, size_t newLen, unsigned int flags)
STATUS_T LOS_UnMMap(VADDR_T addr, size_t size)
解除映射关系
函数调用图:
这是这个函数的调用关系图:

◆ LOS_DumpMemRegion()

VOID LOS_DumpMemRegion ( VADDR_T  vaddr)

输出内存线性区

在文件 los_vm_syscall.c555 行定义.

556{
557 LosVmSpace *space = NULL;
558
559 space = OsCurrProcessGet()->vmSpace;
560 if (space == NULL) {
561 return;
562 }
563
564 if (LOS_IsRangeInSpace(space, ROUNDDOWN(vaddr, MB), MB) == FALSE) {//是否在空间范围内
565 return;
566 }
567
568 OsDumpPte(vaddr);//dump L1 L2
569 OsDumpAspace(space);//dump 空间
570}
VOID OsDumpAspace(LosVmSpace *space)
dump 指定虚拟空间的信息
Definition: los_vm_dump.c:396
VOID OsDumpPte(VADDR_T vaddr)
dump 页表项
Definition: los_vm_dump.c:456
BOOL LOS_IsRangeInSpace(const LosVmSpace *space, VADDR_T vaddr, size_t size)
函数调用图:
这是这个函数的调用关系图:

◆ LOS_IsAnonymousMapping()

STATIC INLINE BOOL LOS_IsAnonymousMapping ( unsigned long  flags)

是否匿名映射 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0

在文件 los_vm_syscall.h54 行定义.

55{
56 return ((flags & MAP_ANONYMOUS) == MAP_ANONYMOUS);
57}

◆ LOS_IsNamedMapping()

STATIC INLINE BOOL LOS_IsNamedMapping ( unsigned long  flags)

在文件 los_vm_syscall.h49 行定义.

50{
51 return ((flags & MAP_ANONYMOUS) == 0);
52}
这是这个函数的调用关系图:

◆ LOS_MMap()

VADDR_T LOS_MMap ( VADDR_T  vaddr,
size_t  len,
unsigned  prot,
unsigned long  flags,
int  fd,
unsigned long  pgoff 
)
 mmap基础概念:
 一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.
 实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,
 即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,
 从而可以实现不同进程间的文件共享。

 https://www.cnblogs.com/huxiao-tee/p/4660352.html
 http://abcdxyzk.github.io/blog/2015/09/11/kernel-mm-mmap/

 参数     描述      
 addr   指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
 length 代表将文件中多大的部分映射到内存。
 prot   用于设置内存段的访问权限,有如下权限:
         PROT_EXEC 映射区域可被执行
         PROT_READ 映射区域可被读取
         PROT_WRITE 映射区域可被写入
         PROT_NONE 映射区域不能存取
         
 flags  控制程序对内存段的改变所造成的影响,有如下属性:
         MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
         MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
         MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
         MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
         MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
         MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

 fd:        要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。
         有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。

 offset 文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是PAGE_SIZE的整数倍。
 成功返回:虚拟内存地址,这地址是页对齐。
 失败返回:(void *)-1。
参数
vaddr
len
prot
flags
fd
pgoff
返回
VADDR_T

在文件 los_vm_syscall.c149 行定义.

150{
151 STATUS_T status;
152 VADDR_T resultVaddr;
153 UINT32 regionFlags;
154 LosVmMapRegion *newRegion = NULL;//应用的内存分配对应到内核就是分配一个线性区
155 struct file *filep = NULL;// inode : file = 1:N ,一对多关系,一个inode可以被多个进程打开,返回不同的file但都指向同一个inode
156 LosVmSpace *vmSpace = OsCurrProcessGet()->vmSpace;
157
158 len = ROUNDUP(len, PAGE_SIZE);
159 STATUS_T checkRst = OsCheckMMapParams(&vaddr, flags, len, pgoff);
160 if (checkRst != LOS_OK) {
161 return checkRst;
162 }
163
164 if (LOS_IsNamedMapping(flags)) {//是否文件映射
165 status = fs_getfilep(fd, &filep);//获取文件描述符和状态
166 if (status < 0) {
167 return -EBADF;
168 }
169
170 status = OsNamedMmapingPermCheck(filep, flags, prot);
171 if (status < 0) {
172 return status;
173 }
174 }
175
176 (VOID)LOS_MuxAcquire(&vmSpace->regionMux);
177 /* user mode calls mmap to release heap physical memory without releasing heap virtual space */
178 status = OsUserHeapFree(vmSpace, vaddr, len);//用户模式释放堆物理内存而不释放堆虚拟空间
179 if (status == LOS_OK) {//OsUserHeapFree 干两件事 1.解除映射关系 2.释放物理页
180 resultVaddr = vaddr;
181 goto MMAP_DONE;
182 }
183 //地址不在堆区
184 regionFlags = OsCvtProtFlagsToRegionFlags(prot, flags);//将参数flag转换Region的flag
185 newRegion = LOS_RegionAlloc(vmSpace, vaddr, len, regionFlags, pgoff);//分配一个线性区
186 if (newRegion == NULL) {
187 resultVaddr = (VADDR_T)-ENOMEM;//ENOMEM:内存溢出
188 goto MMAP_DONE;
189 }
190 newRegion->regionFlags |= VM_MAP_REGION_FLAG_MMAP;
191 resultVaddr = newRegion->range.base;//线性区基地址为分配的地址
192
193 if (LOS_IsNamedMapping(flags)) {
194 status = OsNamedMMap(filep, newRegion);//文件映射
195 } else {
196 status = OsAnonMMap(newRegion);//匿名映射
197 }
198
199 if (status != LOS_OK) {
200 LOS_RbDelNode(&vmSpace->regionRbTree, &newRegion->rbNode);//从红黑树和双循环链表中删除
201 LOS_RegionFree(vmSpace, newRegion);//释放
202 resultVaddr = (VADDR_T)-ENOMEM;
203 goto MMAP_DONE;
204 }
205
206MMAP_DONE:
207 (VOID)LOS_MuxRelease(&vmSpace->regionMux);
208 return resultVaddr;
209}
VOID LOS_RbDelNode(LosRbTree *pstTree, LosRbNode *pstNode)
Definition: los_rbtree.c:700
STATUS_T OsNamedMMap(struct file *filep, LosVmMapRegion *region)
INT32 OsUserHeapFree(LosVmSpace *vmSpace, VADDR_T addr, size_t len)
STATUS_T OsAnonMMap(LosVmMapRegion *region)
匿名映射
STATUS_T OsNamedMmapingPermCheck(struct file *filep, unsigned long flags, unsigned prot)
STATUS_T OsCheckMMapParams(VADDR_T *vaddr, unsigned long flags, size_t len, unsigned long pgoff)
STATIC INLINE BOOL LOS_IsNamedMapping(unsigned long flags)
LosRbNode rbNode
Definition: los_vm_map.h:120
LosRbTree regionRbTree
Definition: los_vm_map.h:148
函数调用图:
这是这个函数的调用关系图:

◆ LOS_UnMMap()

STATUS_T LOS_UnMMap ( VADDR_T  addr,
size_t  size 
)

解除映射关系

在文件 los_vm_syscall.c211 行定义.

212{
213 if ((addr <= 0) || (size <= 0)) {
214 return -EINVAL;
215 }
216
217 return OsUnMMap(OsCurrProcessGet()->vmSpace, addr, size);
218}
STATUS_T OsUnMMap(LosVmSpace *space, VADDR_T addr, size_t size)
函数调用图:
这是这个函数的调用关系图:

◆ ShmAt()

VOID * ShmAt ( INT32  shmid,
const VOID *  shmaddr,
INT32  shmflg 
)

ShmAt
用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。

参数
shm_flg是一组标志位,通常为0。
shmaddr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
shmid是shmget()函数返回的共享内存标识符
返回
如果shmat成功执行,那么内核将使与该共享存储相关的shmid_ds结构中的shm_nattch计数器值加1 shmid 就是个索引,就跟进程和线程的ID一样 g_shmSegs[shmid] shmid > 192个
参见

在文件 shm.c634 行定义.

635{
636 INT32 ret;
637 UINT32 prot = PROT_READ;
638 mode_t acc_mode = SHM_S_IRUGO;
639 struct shmIDSource *seg = NULL;
640 LosVmMapRegion *r = NULL;
641
642 ret = ShmatParamCheck(shmaddr, shmflg);//参数检查
643 if (ret != 0) {
644 set_errno(ret);
645 return (VOID *)-1;
646 }
647
648 if ((UINT32)shmflg & SHM_EXEC) {//flag 转换
649 prot |= PROT_EXEC;
650 acc_mode |= SHM_S_IXUGO;
651 } else if (((UINT32)shmflg & SHM_RDONLY) == 0) {
652 prot |= PROT_WRITE;
653 acc_mode |= SHM_S_IWUGO;
654 }
655
656 SYSV_SHM_LOCK();
657 seg = ShmFindSeg(shmid);//找到段
658 if (seg == NULL) {
659 SYSV_SHM_UNLOCK();
660 return (VOID *)-1;
661 }
662
663 ret = ShmPermCheck(seg, acc_mode);
664 if (ret != 0) {
665 goto ERROR;
666 }
667
668 seg->ds.shm_nattch++;//ds上记录有一个进程绑定上来
669 r = ShmatVmmAlloc(seg, shmaddr, shmflg, prot);//在当前进程空间分配一个线性区并映射到共享内存
670 if (r == NULL) {
671 seg->ds.shm_nattch--;
672 SYSV_SHM_UNLOCK();
673 return (VOID *)-1;
674 }
675
676 r->shmid = shmid;//把ID给线性区的shmid
677 r->regionFlags |= VM_MAP_REGION_FLAG_SHM;//这是一个共享线性区
678 seg->ds.shm_atime = time(NULL);//访问时间
679 seg->ds.shm_lpid = LOS_GetCurrProcessID();//进程ID
680 SYSV_SHM_UNLOCK();
681
682 return (VOID *)(UINTPTR)r->range.base;
683ERROR:
684 set_errno(ret);
685 SYSV_SHM_UNLOCK();
686 PRINT_DEBUG("%s %d, ret = %d\n", __FUNCTION__, __LINE__, ret);
687 return (VOID *)-1;
688}
LITE_OS_SEC_TEXT UINT32 LOS_GetCurrProcessID(VOID)
获取当前进程的进程ID
Definition: los_process.c:2161
signed int INT32
Definition: los_typedef.h:60
LosVmMapRegion * ShmatVmmAlloc(struct shmIDSource *seg, const VOID *shmaddr, INT32 shmflg, UINT32 prot)
分配一个共享线性区并映射好
Definition: shm.c:576
INT32 ShmatParamCheck(const VOID *shmaddr, INT32 shmflg)
Definition: shm.c:562
STATIC struct shmIDSource * ShmFindSeg(int shmid)
通过ID找到共享内存资源
Definition: shm.c:357
STATIC INT32 ShmPermCheck(struct shmIDSource *seg, mode_t mode)
对共享内存段权限检查
Definition: shm.c:455
UINT32 shmid
Definition: los_vm_map.h:126
struct shmid_ds ds
Definition: shm.c:146
pid_t shm_lpid
最后操作共享内存的进程ID
Definition: shm.c:132
unsigned long shm_nattch
当前使用该共享内存段的进程数量
Definition: shm.c:133
time_t shm_atime
最后一个进程访问共享内存的时间
Definition: shm.c:128
ARG_NUM_3 ARG_NUM_1 ARG_NUM_2 ARG_NUM_2 ARG_NUM_3 ARG_NUM_1 ARG_NUM_4 ARG_NUM_2 ARG_NUM_2 ARG_NUM_5 ARG_NUM_2 ARG_NUM_0 ARG_NUM_2 ARG_NUM_1 ARG_NUM_2 ARG_NUM_3 ARG_NUM_7 ARG_NUM_2 ARG_NUM_3 ARG_NUM_2 ARG_NUM_4 ARG_NUM_5 ARG_NUM_6 ARG_NUM_3 ARG_NUM_5 ARG_NUM_7 ARG_NUM_1 ARG_NUM_4 ARG_NUM_5 ARG_NUM_4 ARG_NUM_7 ARG_NUM_2 ARG_NUM_3 ARG_NUM_7 ARG_NUM_7 ARG_NUM_3 ARG_NUM_3 ARG_NUM_3 ARG_NUM_7 ARG_NUM_3 ARG_NUM_2 char ARG_NUM_2 ARG_NUM_1 ARG_NUM_0 ARG_NUM_0 ARG_NUM_3 void ARG_NUM_1 ARG_NUM_0 unsigned ARG_NUM_0 ARG_NUM_2 ARG_NUM_3 ARG_NUM_2 ARG_NUM_5 ARG_NUM_3 ARG_NUM_3 ARG_NUM_4 ARG_NUM_1 ARG_NUM_1 ARG_NUM_3 ARG_NUM_2 mode_t
time_t time(time_t *t)
Definition: time.c:1224
函数调用图:
这是这个函数的调用关系图:

◆ ShmCtl()

INT32 ShmCtl ( INT32  shmid,
INT32  cmd,
struct shmid_ds buf 
)

ShmCtl
此函数可以对shmid指定的共享存储进行多种操作(删除、取信息、加锁、解锁等)

参数
buf是一个结构指针,它指向共享内存模式和访问权限的结构。
cmdcommand是要采取的操作,它可以取下面的三个值 : IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。 IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除共享内存段
shmid是shmget()函数返回的共享内存标识符
返回
参见

在文件 shm.c703 行定义.

704{
705 struct shmIDSource *seg = NULL;
706 INT32 ret = 0;
707 struct shm_info shmInfo;
708 struct ipc_perm shm_perm;
709
710 cmd = ((UINT32)cmd & ~IPC_64);
711
712 SYSV_SHM_LOCK();
713
714 if ((cmd != IPC_INFO) && (cmd != SHM_INFO)) {
715 seg = ShmFindSeg(shmid);//通过索引ID找到seg
716 if (seg == NULL) {
717 SYSV_SHM_UNLOCK();
718 return -1;
719 }
720 }
721
722 if ((buf == NULL) && (cmd != IPC_RMID)) {
723 ret = EINVAL;
724 goto ERROR;
725 }
726
727 switch (cmd) {
728 case IPC_STAT:
729 case SHM_STAT://取段结构
730 ret = ShmPermCheck(seg, SHM_S_IRUGO);
731 if (ret != 0) {
732 goto ERROR;
733 }
734
735 ret = LOS_ArchCopyToUser(buf, &seg->ds, sizeof(struct shmid_ds));//把内核空间的共享页数据拷贝到用户空间
736 if (ret != 0) {
737 ret = EFAULT;
738 goto ERROR;
739 }
740 if (cmd == SHM_STAT) {
741 ret = (unsigned int)((unsigned int)seg->ds.shm_perm.seq << 16) | (unsigned int)((unsigned int)shmid & 0xffff); /* 16: use the seq as the upper 16 bits */
742 }
743 break;
744 case IPC_SET://重置共享段
745 ret = ShmPermCheck(seg, SHM_M);
746 if (ret != 0) {
747 ret = EPERM;
748 goto ERROR;
749 }
750 //从用户空间拷贝数据到内核空间
751 ret = LOS_ArchCopyFromUser(&shm_perm, &buf->shm_perm, sizeof(struct ipc_perm));
752 if (ret != 0) {
753 ret = EFAULT;
754 goto ERROR;
755 }
756 seg->ds.shm_perm.uid = shm_perm.uid;
757 seg->ds.shm_perm.gid = shm_perm.gid;
758 seg->ds.shm_perm.mode = (seg->ds.shm_perm.mode & ~ACCESSPERMS) |
759 (shm_perm.mode & ACCESSPERMS);//可访问
760 seg->ds.shm_ctime = time(NULL);
761#ifdef LOSCFG_SHELL
762 (VOID)memcpy_s(seg->ownerName, OS_PCB_NAME_LEN, OS_PCB_FROM_PID(shm_perm.uid)->processName,
763 OS_PCB_NAME_LEN);
764#endif
765 break;
766 case IPC_RMID://删除共享段
767 ret = ShmPermCheck(seg, SHM_M);
768 if (ret != 0) {
769 ret = EPERM;
770 goto ERROR;
771 }
772
773 seg->status |= SHM_SEG_REMOVE;
774 if (seg->ds.shm_nattch <= 0) {//没有任何进程在使用了
775 ShmFreeSeg(seg);//释放 归还内存
776 }
777 break;
778 case IPC_INFO://把内核空间的共享页数据拷贝到用户空间
779 ret = LOS_ArchCopyToUser(buf, &g_shmInfo, sizeof(struct shminfo));
780 if (ret != 0) {
781 ret = EFAULT;
782 goto ERROR;
783 }
784 ret = g_shmInfo.shmmni;
785 break;
786 case SHM_INFO:
787 shmInfo.shm_rss = 0;
788 shmInfo.shm_swp = 0;
789 shmInfo.shm_tot = 0;
790 shmInfo.swap_attempts = 0;
791 shmInfo.swap_successes = 0;
792 shmInfo.used_ids = ShmSegUsedCount();//在使用的seg数
793 ret = LOS_ArchCopyToUser(buf, &shmInfo, sizeof(struct shm_info));//把内核空间的共享页数据拷贝到用户空间
794 if (ret != 0) {
795 ret = EFAULT;
796 goto ERROR;
797 }
798 ret = g_shmInfo.shmmni;
799 break;
800 default:
801 VM_ERR("the cmd(%d) is not supported!", cmd);
802 ret = EINVAL;
803 goto ERROR;
804 }
805
806 SYSV_SHM_UNLOCK();
807 return ret;
808
809ERROR:
810 set_errno(ret);
811 SYSV_SHM_UNLOCK();
812 PRINT_DEBUG("%s %d, ret = %d\n", __FUNCTION__, __LINE__, ret);
813 return -1;
814}
STATIC INLINE VOID ShmFreeSeg(struct shmIDSource *seg)
释放seg->node 所占物理页框,seg本身重置
Definition: shm.c:310
STATIC struct shminfo g_shmInfo
Definition: shm.c:155
STATIC INT32 ShmSegUsedCount(VOID)
获取共享内存池中已被使用的段数量
Definition: shm.c:440
Definition: shm.c:113
mode_t mode
Definition: shm.c:119
gid_t gid
Definition: shm.c:116
uid_t uid
Definition: shm.c:115
CHAR ownerName[OS_PCB_NAME_LEN]
Definition: shm.c:150
UINT32 status
Definition: shm.c:147
Definition: shm.c:125
time_t shm_ctime
创建时间
Definition: shm.c:130
struct ipc_perm shm_perm
操作许可,里面包含共享内存的用户ID、组ID等信息
Definition: shm.c:126
Definition: shm.c:138
unsigned long shmmni
Definition: shm.c:139
ARG_NUM_3 int
size_t LOS_ArchCopyToUser(void *dst, const void *src, size_t len)
从内核空间拷贝到用户空间
Definition: user_copy.c:79
size_t LOS_ArchCopyFromUser(void *dst, const void *src, size_t len)
Definition: user_copy.c:58
函数调用图:
这是这个函数的调用关系图:

◆ ShmDeinit()

UINT32 ShmDeinit ( VOID  )

在文件 shm.c200 行定义.

201{
202 UINT32 ret;
203
204 (VOID)LOS_MemFree((VOID *)OS_SYS_MEM_ADDR, g_shmSegs);//归还内存池
205 g_shmSegs = NULL;
206
207 ret = LOS_MuxDestroy(&g_sysvShmMux);//销毁互斥量
208 if (ret != LOS_OK) {
209 return -1;
210 }
211
212 return 0;
213}
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
释放从指定动态内存中申请的内存
Definition: los_memory.c:1369
LITE_OS_SEC_TEXT UINT32 LOS_MuxDestroy(LosMux *mutex)
销毁互斥锁
Definition: los_mux.c:289
STATIC struct shmIDSource * g_shmSegs
Definition: shm.c:163
STATIC LosMux g_sysvShmMux
Definition: shm.c:76
函数调用图:

◆ ShmDt()

INT32 ShmDt ( const VOID *  shmaddr)

当对共享存储的操作已经结束时,则调用shmdt与该存储段分离 如果shmat成功执行,那么内核将使与该共享存储相关的shmid_ds结构中的shm_nattch计数器值减1

注意
注意:这并不从系统中删除共享存储的标识符以及其相关的数据结构。共享存储的仍然存在, 直至某个进程带IPC_RMID命令的调用shmctl特地删除共享存储为止
参数
shmaddr
返回
INT32

在文件 shm.c824 行定义.

825{
826 LosVmSpace *space = OsCurrProcessGet()->vmSpace;//获取进程空间
827 struct shmIDSource *seg = NULL;
828 LosVmMapRegion *region = NULL;
829 INT32 shmid;
830 INT32 ret;
831
832 if (IS_PAGE_ALIGNED(shmaddr) == 0) {//地址是否对齐
833 ret = EINVAL;
834 goto ERROR;
835 }
836
837 (VOID)LOS_MuxAcquire(&space->regionMux);
838 region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)shmaddr);//找到线性区
839 if (region == NULL) {
840 ret = EINVAL;
841 goto ERROR_WITH_LOCK;
842 }
843 shmid = region->shmid;//线性区共享ID
844
845 if (region->range.base != (VADDR_T)(UINTPTR)shmaddr) {//这是用户空间和内核空间的一次解绑
846 ret = EINVAL; //shmaddr 必须要等于region->range.base
847 goto ERROR_WITH_LOCK;
848 }
849
850 /* remove it from aspace */
851 LOS_RbDelNode(&space->regionRbTree, &region->rbNode);//从红黑树和链表中摘除节点
852 LOS_ArchMmuUnmap(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT);//解除线性区的映射
853 (VOID)LOS_MuxRelease(&space->regionMux);
854 /* free it */
855 free(region);//释放线性区所占内存池中的内存
856
857 SYSV_SHM_LOCK();
858 seg = ShmFindSeg(shmid);//找到seg,线性区和共享段的关系是 1:N 的关系,其他空间的线性区也会绑在共享段上
859 if (seg == NULL) {
860 ret = EINVAL;
861 SYSV_SHM_UNLOCK();
862 goto ERROR;
863 }
864
865 ShmPagesRefDec(seg);//页面引用数 --
866 seg->ds.shm_nattch--;//使用共享内存的进程数少了一个
867 if ((seg->ds.shm_nattch <= 0) && //无任何进程使用共享内存
868 (seg->status & SHM_SEG_REMOVE)) {//状态为删除时需要释放物理页内存了,否则其他进程还要继续使用共享内存
869 ShmFreeSeg(seg);//释放seg 页框链表中的页框内存,再重置seg状态
870 } else {
871
872 seg->ds.shm_dtime = time(NULL);//记录分离的时间
873 seg->ds.shm_lpid = LOS_GetCurrProcessID();//记录操作进程ID
874 }
875 SYSV_SHM_UNLOCK();
876
877 return 0;
878
879ERROR_WITH_LOCK:
880 (VOID)LOS_MuxRelease(&space->regionMux);
881ERROR:
882 set_errno(ret);
883 PRINT_DEBUG("%s %d, ret = %d\n", __FUNCTION__, __LINE__, ret);
884 return -1;
885}
STATUS_T LOS_ArchMmuUnmap(LosArchMmu *archMmu, VADDR_T vaddr, size_t count)
LOS_ArchMmuUnmap 解除进程空间虚拟地址区间与物理地址区间的映射关系
Definition: los_arch_mmu.c:619
void free(void *ptr)
释放ptr所指向的内存空间
Definition: malloc.c:66
STATIC VOID ShmPagesRefDec(struct shmIDSource *seg)
seg下所有共享页引用减少
Definition: shm.c:233
time_t shm_dtime
最后一个进程离开共享内存的时间
Definition: shm.c:129
函数调用图:
这是这个函数的调用关系图:

◆ ShmGet()

INT32 ShmGet ( key_t  key,
size_t  size,
INT32  shmflg 
)

ShmGet
得到一个共享内存标识符或创建一个共享内存对象

参数
key建立新共享内存对象 标识符是IPC对象的内部名。为使多个合作进程能够在同一IPC对象上汇聚,需要提供一个外部命名方案。 为此,每个IPC对象都与一个键(key)相关联,这个键作为该对象的外部名,无论何时创建IPC结构(通过msgget、semget、shmget创建), 都应给IPC指定一个键, key_t由ftok创建,ftok当然在本工程里找不到,所以要写这么多.
shmflgIPC_CREAT IPC_EXCL IPC_CREAT: 在创建新的IPC时,如果key参数是IPC_PRIVATE或者和当前某种类型的IPC结构无关,则需要指明flag参数的IPC_CREAT标志位, 则用来创建一个新的IPC结构。(如果IPC结构已存在,并且指定了IPC_CREAT,则IPC_CREAT什么都不做,函数也不出错) IPC_EXCL: 此参数一般与IPC_CREAT配合使用来创建一个新的IPC结构。如果创建的IPC结构已存在函数就出错返回, 返回EEXIST(这与open函数指定O_CREAT和O_EXCL标志原理相同)
size新建的共享内存大小,以字节为单位
返回
参见

在文件 shm.c515 行定义.

516{
517 INT32 ret;
518 INT32 shmid;
519
520 SYSV_SHM_LOCK();
521
522 if (key == IPC_PRIVATE) {
523 ret = ShmAllocSeg(key, size, shmflg);
524 } else {
525 ret = ShmFindSegByKey(key);//通过key查找资源ID
526 if (ret < 0) {
527 if (((UINT32)shmflg & IPC_CREAT) == 0) {//
528 ret = -ENOENT;
529 goto ERROR;
530 } else {
531 ret = ShmAllocSeg(key, size, shmflg);//分配一个共享内存
532 }
533 } else {
534 shmid = ret;
535 if (((UINT32)shmflg & IPC_CREAT) &&
536 ((UINT32)shmflg & IPC_EXCL)) {
537 ret = -EEXIST;
538 goto ERROR;
539 }
540 ret = ShmPermCheck(ShmFindSeg(shmid), (UINT32)shmflg & ACCESSPERMS);//对共享内存权限检查
541 if (ret != 0) {
542 ret = -ret;
543 goto ERROR;
544 }
545 ret = ShmSegValidCheck(shmid, size, shmflg);
546 }
547 }
548 if (ret < 0) {
549 goto ERROR;
550 }
551
552 SYSV_SHM_UNLOCK();
553
554 return ret;
555ERROR:
556 set_errno(-ret);
557 SYSV_SHM_UNLOCK();
558 PRINT_DEBUG("%s %d, ret = %d\n", __FUNCTION__, __LINE__, ret);
559 return -1;
560}
STATIC INT32 ShmFindSegByKey(key_t key)
通过key查找 shmId
Definition: shm.c:325
STATIC INT32 ShmAllocSeg(key_t key, size_t size, INT32 shmflg)
为共享段分配物理内存 例如:参数size = 4097, LOS_Align(size, PAGE_SIZE) = 8192 分配页数 size >> PAGE_SHIFT = 2页
Definition: shm.c:251
STATIC INT32 ShmSegValidCheck(INT32 segNum, size_t size, INT32 shmFlg)
共享内存段有效性检查
Definition: shm.c:341
函数调用图:
这是这个函数的调用关系图:

◆ ShmInit()

UINT32 ShmInit ( VOID  )

在文件 shm.c166 行定义.

167{
168 UINT32 ret;
169 UINT32 i;
170
171 ret = LOS_MuxInit(&g_sysvShmMux, NULL);//初始化互斥锁
172 if (ret != LOS_OK) {
173 goto ERROR;
174 }
175
176 g_shmSegs = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, sizeof(struct shmIDSource) * g_shmInfo.shmmni);//分配shm段数组
177 if (g_shmSegs == NULL) {
179 goto ERROR;
180 }
181 (VOID)memset_s(g_shmSegs, (sizeof(struct shmIDSource) * g_shmInfo.shmmni),
182 0, (sizeof(struct shmIDSource) * g_shmInfo.shmmni));//数组清零
183
184 for (i = 0; i < g_shmInfo.shmmni; i++) {
185 g_shmSegs[i].status = SHM_SEG_FREE;//节点初始状态为空闲
186 g_shmSegs[i].ds.shm_perm.seq = i + 1;//struct ipc_perm shm_perm;系统为每一个IPC对象保存一个ipc_perm结构体,结构说明了IPC对象的权限和所有者
187 LOS_ListInit(&g_shmSegs[i].node);//初始化节点
188 }
190
191 return LOS_OK;
192
193ERROR:
194 VM_ERR("ShmInit fail\n");
195 return LOS_NOK;
196}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
VOID * LOS_MemAlloc(VOID *pool, UINT32 size)
从指定内存池中申请size长度的内存,注意这可不是从内核堆空间中申请内存
Definition: los_memory.c:1123
LITE_OS_SEC_TEXT UINT32 LOS_MuxInit(LosMux *mutex, const LosMuxAttr *attr)
初始化互斥锁
Definition: los_mux.c:262
STATIC UINT32 g_shmUsedPageCount
Definition: shm.c:164
函数调用图: