Linux C应用中常用的内存映射技术 (linux c内存映射)

内存映射是Linux C应用程序开发中的重要技术之一。它可以在用户空间和内核空间间建立一种映射关系,并使得程序可以直接访问内核中的某些数据结构和文件系统。本文将介绍在,并阐述它们的实现原理和使用方法。

1.匿名内存映射

匿名内存映射是一种最简单的内存映射技术,它可以在程序内部映射一块未命名的内存区域。使用该技术需要调用mmap函数,并指明所需的映射地址、映射大小、映射方式等参数。

匿名内存映射在Linux C应用开发中常用于创建共享内存区域,使得多个进程可以共享同一块内存,并进行数据交换。在匿名内存映射技术中,当一个进程写入共享内存时,其它进程都可以立即看到这个变化。

2.文件内存映射

文件内存映射是Linux C应用中另一个常用的内存映射技术,它可以将文件映射到进程的地址空间,并可直接在内存中读写文件。使用该技术需要调用mmap函数,指定所需映射的文件和映射方式等参数。

文件内存映射在Linux C应用开发中广泛应用于文件操作和数据结构处理等场景。在文件内存映射技术中,使用者可以直接访问内存中的数据,而无需进行文件读写操作,极大地方便了应用程序的开发和实现。

3.共享内存映射

共享内存映射是Linux C应用中非常常用的内存映射技术之一,它可以将内存区域映射到多个进程中,并实现数据共享。共享内存映射技术需要调用shm_open和mmap这两个函数。

共享内存映射技术在Linux C应用程序开发中广泛应用于多进程协作和数据共享等场景。通过共享内存映射技术,多个进程可以共享一个变量或数据结构,从而实现进程间数据交换和通信。

4.循环缓冲区内存映射

循环缓冲区内存映射是一种特殊的内存映射技术,它是通过内存映射方式实现循环队列的操作。使用该技术需要调用mmap函数,并指明所需的映射地址、大小、映射方式等参数。

循环缓冲区内存映射技术在Linux C应用中广泛应用于数据传输和队列处理等场景。该技术能够实现高效的数据传输和存储,从而提高程序的效率和性能。

结语

内存映射技术是Linux C应用程序开发中非常重要的技术之一。本文介绍了在Linux C应用中常用的几种内存映射技术,包括匿名内存映射、文件内存映射、共享内存映射和循环缓冲区内存映射,并阐述了它们的实现原理和使用方法。通过内存映射技术,我们可以更加高效地处理数据和实现多进程协作,从而提高程序的效率和性能。

相关问题拓展阅读:

怎么理解linux下内存的三级映射?请大侠指教

逻辑地址-线性地址-物理睁竖激地址

分段纤谈,分页

再详细点需要阅读书籍吧,这个也不光是linux,而是386硬悉袜件决定的,建议看下汇编方面的。

内存映射的相关信息

4.1示例代码

通过前面的理论分析,我们通过编写一个简单的程序,来分析内核是如何把线性地址映射到物理地址的。

#cattest.c

#include

voidtest(void)

{

printf(hello,world.\n);

}

intmain(void)

{

test();

}

这段代码很简单,我们故意要main调用test函数,就是想看下test函数的虚拟地址是如何映射成物理地址的。

4.2段式映射分析

我们先编译,在反汇编下test文件

#gcc-otesttest.c

#objdump-dtest

:

:55push%ebp

:89e5mov%esp,%ebp

804836b:83ec08sub$0x8,%esp

804836e:83ec0csub$0xc,%esp

:push$0x

:e835ffffffcall80482b0

804837b:83c410add$0x10,%esp

804837e:c9leave

804837f:c3ret

:

:55push%ebp

:89e5mov%esp,%ebp

:83ec08sub$0x8,%esp

:83e4f0and$0xfffffff0,%esp

:bmov$0x0,%eax

804838e:83c00fadd$0xf,%eax

:83c00fadd$0xf,%eax

:c1e804shr$0x4,%eax

:c1e004shl$0x4,%eax

804839a:29c4sub%eax,%esp

804839c:e8c7ffffffcall

80483a1:c9leave

80483a2:c3ret

80483a3:90nop

从上述结果可以看到,ld给test()函数分配的地址为0x.在elf格式的可执行文件代码中,ld的实际位置总是从0x开始安排程序

的代码段,对每个程序都是这样。至于程序在执行时在物理内存中的实际位置就要由内核在为其建立内存映射时临时做出安排,具体地址则

取决段亏于当时所分配到的物理内存页面。假设该程序已经运行,整个映射机制都已经建立好,尺燃派并且CPU正在执行main()中的call这条指

令,要转移到虚拟地址0x去运行.下面将详细介绍这个虚拟地址转换为物理地址的映射过程.

首先是段式映射阶段。由于0x是一个程序的入口,更重要的是在执行的过程中是由CPU中的指令计数器EIP所指向的,所以在代码段中

。因此,i386CPU使用代码段寄存器CS的当前值作为段式映射的选择子,也就是用它作为在段描述表的下标.那么CS的值是多少呢?

用GDB调试下test:

(gdb)inforeg

eax0x1016

ecx0x11

edx0x9d915c

ebx0x9d6ff

esp0xbfedb4800xbfedb480

ebp0xbfedb4880xbfedb488

esi0xbfedb

edi0xbfedb4c

eip0x804836e0x804836e

eflags0x282642

cs0x73115

ss0x7b123

ds0x7b123

es0x7b123

fs0x00

gs0x3351

可以看到CS的值为0x73,我们把它分解成二进制:

更低2位为3,说明RPL的值为3,应为我们这个程序本省就是在用户空间,RPL的值自然为3.

第3位为0表示这个下标在GDT中。

高13位为14,所以段描述符在GDT表的第14个表项中,我陵贺们可以到内核代码中去验证下:

在i386/a/segment.h中:

#defineGDT_ENTRY_DEFAULT_USER_CS14

#define__USER_CS(GDT_ENTRY_DEFAULT_USER_CS*8+3)

可以看到段描述符的确就是GDT表的第14个表项中。

我们去GDT表看看具体的表项值是什么,GDT的内容在arch/i386/kernel/head.S中定义:

ENTRY(cpu_gdt_table)

.quad0x00/*NULLdescriptor*/

.quad0x00/*0x0breserved*/

.quad0x00/*0x13reserved*/

.quad0x00/*0x1breserved*/

.quad0x00/*0x20unused*/

.quad0x00/*0x28unused*/

.quad0x00/*0x33TLSentry1*/

.quad0x00/*0x3bTLSentry2*/

.quad0x00/*0x43TLSentry3*/

.quad0x00/*0x4breserved*/

.quad0x00/*0x53reserved*/

.quad0x00/*0x5breserved*/

.quad0x00cf9a000000ffff/*0x60kernel4GBcodeat0x*/

.quad0x00cfffff/*0x68kernel4GBdataat0x*/

.quad0x00cffa000000ffff/*0x73user4GBcodeat0x*/

.quad0x00cffffff/*0x7buser4GBdataat0x*/

.quad0x00/*0x80TSSdescriptor*/

.quad0x00/*0x88LDTdescriptor*/

/*SegmentsusedforcallingPnPBIOS*/

.quad0x00c09a/*0x9032-bitcode*/

.quad0x00809a/*0x9816-bitcode*/

.quad0x00/*0xa016-bitdata*/

.quad0x00/*0xa816-bitdata*/

.quad0x00/*0xb016-bitdata*/

/*

*TheAPMsegmentshavebytegranularityandtheirbases

*andlimitsaresetatruntime.

*/

.quad0x00409a/*0xb8APMCScode*/

.quad0x00009a/*0xc0APMCS16code(16bit)*/

.quad0x00/*0xc8APMDSdata*/

.quad0x00/*0xd0-unused*/

.quad0x00/*0xd8-unused*/

.quad0x00/*0xe0-unused*/

.quad0x00/*0xe8-unused*/

.quad0x00/*0xf0-unused*/

.quad0x00/*0xf8-GDTentry31:double-faultTSS*/

.quad0x00cffa000000ffff/*0x73user4GBcodeat0x*/

我们把这个值展开成二进制:

根据上述对段描述符表项值的描述,可以得出如下结论:

B0-B15,B16-B31是0,表示基地址全为0.

L0-L15,L16-L19是1,表示段的上限全是0xffff.

G位是1表示段长度单位均为4KB。

D位是1表示对段的访问都是32位指令

P位是1表示段在内存中。

DPL是3表示特权级是3级

S位是1表示为代码段或数据段

type为1010表示代码段,可读,可执行,尚未收到访问

这个描述符指示了段从0地址开始的整个4G虚存空间,逻辑地址直接转换为线性地址。

所以在经过段式映射后就把逻辑地址转换成了线性地址,这也是在linux中,为什么逻辑地址等同于线性地址的原因了。

4.3页式映射分析

现在进入页式映射的过程了,Linux系统中的每个进程都有其自身的页面目录PGD,指向这个目录的指针保存在每个进程的mm_struct数据结构

中。每当调度一个进程进入运行的时候,内核都要为即将运行的进程设置好控制寄存器cr3,而MMU的硬件则总是从cr3中取得指向当前页面目

录的指针。当我们在程序中要转移到地址0x去的时候,进程正在运行,cr3早以设置好,指向我们这个进程的页面目录了。先将线性

地址0x展开成二进制:

1000

对照线性地址的格式,可见更高10位为二进制的,也就是十进制的32,所以MMU就以32为下标在其页面目录中找到其目录项。这个

目录项的高20位指向一个页面表,CPU在这20位后添上12个0就得到页面表的指针。找到页面表以后,CPU再来看线性地址中的中间10位,

,即十进制的72.于是CPU就以此为下标在页表中找相应的表项。表项值的高20位指向一个物理内存页面,在后边添上12个0就得到物

理页面的开始地址。假设物理地址在0x620230的,线性地址的更低12位为0x368.那么test()函数的入口地址就为0x620230+0x368=0x620238

linux c内存映射的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux c内存映射,Linux C应用中常用的内存映射技术,怎么理解linux下内存的三级映射?请大侠指教,内存映射的相关信息的信息别忘了在本站进行查找喔。


数据运维技术 » Linux C应用中常用的内存映射技术 (linux c内存映射)