进程通信system V IPC----共享内存

内核空间和用户空间:
Linux的虚拟地址空间范围为0~4G,Linux内核将这4G字节的空间分为两部分,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF)供各个进程使用,称为“用户空间。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。
Linux使用两级保护机制:0级供内核使用,3级供用户程序使用,每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的,最高的1GB字节虚拟内核空间则为所有进程以及内核所共享。
内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。 虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0x00000000),另外,使用虚拟地址可以很好的保护内核空间被用户空间破坏,虚拟地址到物理地址转换过程有操作系统和CPU共同完成(操作系统为CPU设置好页表,CPU通过MMU单元进行地址转换)。

注:多任务操作系统中的每一个进程都运行在一个属于它自己的内存沙盒中,这个沙盒就是虚拟地址空间(virtual address space),在32位模式下,它总是一个4GB的内存地址块。这些虚拟地址通过页表(page table)映射到物理内存,页表由操作系统维护并被处理器引用。每个进程都拥有一套属于它自己的页表。

0-3G 用户空间 0x00000000 ~ 0xbfffffff

3-4G 内核空间 0xc0000000 ~ 0xffffffff

每个用户进程都有独立的用户空间(虚拟地址0-3),而内核空间是唯一的(相当于共享)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
内核空间内存动态申请

主要包括三个函数:kmalloc(), __get_free_pages, vmalloc。

kmalloc(),
__get_free_pages申请的内存位于物理地址映射区,而且在物理上也是连续的,返回的虚拟地址与真实的物理地址(物理地址是连续的,虚拟地址也是连续的)只有一个固定的偏移,因此存在较简单的转换关系。

而vmalloc申请的内存位于vmalloc虚拟内存分配区(这些区都是以线性地址为度量),它在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,而vmalloc申请的虚拟内存和物理内存之间也没有简单的换算关系。因为vmalloc申请的在虚拟内存空间连续的内存区在物理内存中并不一定连续,可以想象为了完成vmalloc,新的页表需要被建立,因此,调用vmalloc来分配少量内存是不妥的。一般来讲,kmalloc用来分配小于128K的内存,而更大的内存块需要用vmalloc来实现。

虚拟地址与物理地址关系

对于内核物理内存映射区的虚拟内存(用kmalloc(),
__get_free_pages申请的),使用virt_to_phys()和phys_to_virt()来实现物理地址和内核虚拟地址之间的互相转换。它实际上,仅仅做了3G的地址移位。上述方法适用于常规内存(内核物理内存映射区),高端内存的虚拟地址与物理地址之间不存在如此简单的换算关系。因为它涉及到了分离物理页的页表控制机制。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

void error_out(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

int main (int argc, char *argv[])
{
        key_t mykey = 0x45678;
        int smid = shmget(mykey, 1024, IPC_CREAT|0666);
        if(smid == -1){
            error_out("shmget");
        }
        void *ptr;
        ptr = shmat(smid, NULL, 0);
        if (ptr == (void *) -1){
            error_out("shmat");
        }
        printf("Attach addr is %p \n", ptr);
        pid_t pid = fork();
        if (pid == 0){
            u_long *d = (u_long *)ptr;
            *d = 0xdfaa3455;
            exit(0);
        }else{
            int status;
            waitpid(pid, &status, 0);
            printf("child wrote %#lx\n", *(u_long *)ptr);
        }
        sleep(10);
        int r = shmdt(ptr);
        if (r == -1){
            error_out("shmdt");
        }
        r = shmctl(smid, IPC_RMID, NULL);
        if (r == -1){
            error_out("shmctl");
        }
        return 0;
}

可以看到key的信息, 0x00045678,就是默认定义的0x45678.分配的size为:1024
在这里插入图片描述
在Linux version 5.3.0-40-generic 的系统上,输出的结果为:

扫描二维码关注公众号,回复: 11101191 查看本文章
Attach addr is 0x7f2285c9e000 
child wrote 0xdfaa3455
发布了95 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ding283595861/article/details/104733609
今日推荐