操作系统实验6:地址映射与共享

这篇具有很好参考价值的文章主要介绍了操作系统实验6:地址映射与共享。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本次实践项目有两个基本内容:
(1)用Bochs调试工具跟踪Linux-0.11的地址转换过程;
(2)实现基于共享物理页框的进程间内存共享。

知识点补充

GDT和GDTR

和一个段有关的信息需要 8 个字节来描述,所以称为段描述符(Segment Descriptor),每个段都需要一个描述符。为了存放这些描述符,需要在内存中开辟出一段空间。在这段空间里,所有的描述符都是挨在一起,集中存放的,这就构成一个描述符表。最主要的描述符表是全局描述符表(Global Descriptor Table, GDT)。为了跟踪全局描述符表,处理器内部有一个 48 位的寄存器,称为全局描述符表寄存器(GDTR),该寄存器分为两部分,分别是 32 位的线性地址和 16 位的边界。
操作系统实验6:地址映射与共享

存储器的段描述符格式

操作系统实验6:地址映射与共享

段选择子的组成

操作系统实验6:地址映射与共享
在保护模式下访问一个段时,传送到段选择器的是段选择子。它由三部分组成,第一部分是描述符的索引号,用来在描述符表中选择一个段描述符。 TI 是描述符表指示器(Table Indicator), TI=0 时,表示描述符在 GDT 中; TI=1 时,描述符在 LDT 中。 RPL 是请求特权级,表示给出当前选择子的那个程序的特权级别,正是该程序要求访问这个内存段。

地址转换过程跟踪

要跟踪地址转换过程,首先需要以汇编级调试的方式启动Bochs,即在编译好Linux-0.11后,通过运行命令./dbg-asm来启动调试器,此时Bochs模拟器会处于黑屏状态,执行命令的宿主主机窗口中的显示如下图所示:
操作系统实验6:地址映射与共享
"Next at t=0"表示下面执行的指令是Bochs启动后要执行的第一条指令,单步跟踪进去就能看到BIOS代码。现在直接输入命令“c”,即继续运行程序,Bochs和以前一样启动Linux-0.11。
现在需要在Linux-0.11上编写一个测试程序test.c,要跟踪的地址就是这个程序中的地址。test.c代码如下:

#include <stdio.h>

int i = 0x12345678;

int main(void)
{
    printf("The logical address of i is 0x%08x",&i);
    fflush(stdout);
    while(i);
    return 0;
}

将test.c拷贝到Linux-0.11上编译、运行,运行输出如下:

The logical address of i is 0x00003004

由于打印的是逻辑地址,即离开程序段首的偏移地址,所以只要程序test.c不发生变化,0x00003004这个值也是不会变化的,即在同一机器上多次运行test.c,这个逻辑地址也是一样的。
由于test.c中有一个死循环,所以这个程序不会主动退出,正是这样,其各种资源,如逻辑地址、LDT表、GDT表、页表等信息才能在调试器中用调试命令查看。
现在在Bochs命令行窗口按下Ctrl + c键,Bochs会暂停运行,进入调试状态。此时的Bochs会有很大的可能是在test.c中运行,因为此时Linux-0.11中进程很少。宿主机调试器窗口会显示类似如下信息:
操作系统实验6:地址映射与共享
若其中的"000f"显示为"0008",则说明按下Ctrl+c中断发生在内核中,这时需要输入c继续执行,然后再按下Ctrl+c直到变为"000f"为止。如果显示的指令不是cmp,就用"n"命令单步运行几步,直到停在cmp指令上,实际上就是停在while(i)语句处。然后用“u/8命令”,显示从当前位置开始的8条指令的反汇编代码,如下图所示:
操作系统实验6:地址映射与共享
这正是从while(i)开始到return语句的汇编代码。不难分析出,变量i就保存在地址DS:0x3004处。cmp指令要将DS:3004处存放的内容和0就行比较,只有等于0才跳出循环,即执行"jz .+0x00000004"。
现在要开始寻找逻辑地址DS:0x3004对应的物理地址,即开始跟踪地址转换过程。由于是段页式内存结构,所以要先用段表找到虚拟地址。DS:0x3004是逻辑地址,DS表明这个地址属于DS段,只有找到进程对应的段表以后,才能通过DS寄存器的值在段表中找到DS段的具体信息,得到虚拟地址。这个段表就是进程的LDT表,接下来就要找到这个LDT表,LDTR就是起点。LDTR寄存器中存放的是当前进程LDT表地址在GDT表中的偏移值。
用"sreg"命令可以看到各个寄存器的信息:
操作系统实验6:地址映射与共享
可以看到ldtr的值是0x0068 = 0000000001101000,根据段选择子的结构,当前进程的LDT存放在GDT表中的13(1101)号位置。

GDT表的位置由GDTR寄存器明确给出,即在物理地址0x00005cb8位置处,GDT表中每一项占8个字节,所以我们要查找的LDT表的物理地址是0x00005cb8 + 13 * 8。用命令“xp /2w 0x00005cb8+13*8”可以查看这个位置的内容:
操作系统实验6:地址映射与共享
这两步在不同的机器上执行时得到的数值可能不一样,这是正常的。如果向确认是否正确,就看执行sreg命令后的输出信息中,ldtr所在行里的dl和dh的值,它们是Bachs自动计算出来的,从GDT表中找到的LDT地址应该和Bochs计算出来的一致。
将得到的数字“0xa2d00068 0x000082f9”进行组合,组合方式如下,得到LDT表的物理地址0x00f9a2d0,这就是LDT表的物理地址,组合方式是由段描述符的格式决定的。

执行命令“xp /8w 0x00f9a2d0”可以得到:
操作系统实验6:地址映射与共享
这就是当前进程LDT表的前四项内容了。
现在可以根据DS寄存器来查找LDT表了,由上面"sreg"命令获得的寄存器信息“ds:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=3”,可以直到,DS寄存器的值是0x0017,按照段选择子的格式,0x0017 = 0x0000000000010111,去掉最低三位,剩余的位组合起来得到的数值是2,即偏移为,因此是第三项,即“0x00003fff 0x10c0f300”,这个就是DS段的信息。用同样的组合方式组合“0x00003fff 0x10c0f300”得到DS段的基址为0x10000000,这就是当前进程DS段在虚拟内存空间中的起始地址。因此DS:0x3004对应的虚拟地址为:

0x10000000+3004 = 0x10003004

现在已经得到了虚拟地址,接下来就要将其转换为物理地址,核心就是查找页表。首先要计算出虚拟地址中的页目录号、页表号和页内偏移,它们分别对应了虚拟地址的前10位、中间10位和末尾的12位。不难计算出,虚拟地址0x10003004对应的页目录号是64,页号是3,页内偏移是4。页目录表的位置由CR3寄存器给出,用“creg”命令可以看到:
操作系统实验6:地址映射与共享
说明页目录表的基址为0。页目录表和表中内容都很简单,就是1024个32位二进制树,这32位中的前20位表示物理页框号,后面是一些属性信息(其中最重要的是最后一位P,表示是否有效)。第65页目录项就是要找的内容,用命令“xp /w 0+644”查看:
操作系统实验6:地址映射与共享
其中的027是属性,显然P=1,因此这个页目录项是有效的。因此页表所在物理页框号位0x00fa9,即该页目录对应的1024个页的所有页表项信息存放在物理地址0x00fa9000处,从该位置开始查找第3个页表项,即“xp /w 0x00fa9000+3
4”:
操作系统实验6:地址映射与共享
其中的067是属性,显然P=1,说明页表项也是有效的。
现在已知虚拟地址0x10003004对应的物理页框号为0x00fa7000,将它和页内偏移0x0004连接到一起,得到物理地址为0x00fa7004,这个就是变量i的物理地址,用命令“xp /w 0x00fa7004”查看:
操作系统实验6:地址映射与共享
得到的数值就是变量i的值,说明这个过程是正确的。
现在直接修改内存来改变i的值,使用命令“setpmem 0x00fa7004 4 0”实现,表示从0x00fa7004地址开始的4个字节都设置为0,然后使用“c”命令继续Bochs的运行,可以看到test进程退出了,说明i变量修改成功了。

基于共享物理页框的进程间内存共享的实现

在Linux下,可以通过shmget()和shmat()两个系统调用来使用共享内存。因此本部分的具体实现内容就是在Linux-0.11下添加shmget()和shmat()两个系统调用(Linux-0.11上没有这两个系统调用)。添加系统调用的具体过程可以参照操作系统实验2:系统调用。

shmget()系统调用的函数原型为:

int shmget(key_t key, size_t size, int shmflg);

该系统调用会新建/打开一页物理内存作为共享内存,并返回该页共享内存的shmid,即该页共享内存在操作系统中的标识。如果多个进程使用相同的key调用shmget,则这些进程就会获得相同的shmid,即得到同一块内存的标识。在shmget实现时,如果key所对应的共享内存已经建立,则直接返回shmid,否则新建。如果size超过一页内存的大小,返回-1,并置errno为EINVAL。如果系统无空=空闲内存,返回-1,并置errno为ENOMEM。对于本实验,shmflg参数忽略。

shmat()系统调用的函数原型为:

void* shmat(int shmid,const void *shmaddr, int shmflg);

该系统调用会将shmid指定的共享页面映射到当前进程的虚拟地址空间中,并返回一个逻辑地址p,调用进程可以通过读写逻辑地址p来读写这一页共享内存。如果shmid非法,返回-1,并置errno为EINVAL。对于本实验,参数shmaddr和shmflg都忽略。

因此,两个进程都调用shmat可以关联到同一页内存上,此时两个进程读写p指针就是在读写同一页内存,从而实现了基于共享内存的进程间通信。

下面参照操作系统实验2:系统调用添加系统调用。

  1. 添加系统调用的编号
    系统调用编号在 include/unistd.h中定义,打开该文中找到系统调用编号的定义:
    操作系统实验6:地址映射与共享

  2. 添加IDT(中断描述符表)
    打开include/linux/sys.h文件,在文件中的sys_call_table[]的数组中添加sys_shmget和sys_shmat,注意这里的前后顺序要和之前的系统调用编号的前后关系对应起来。同时,将sys_shmget()和sys_shmat()声明全局函数。
    操作系统实验6:地址映射与共享

  3. 修改系统调用数
    修改kernel/system_call.s中的系统调用数,由原来的72改为74.操作系统实验6:地址映射与共享

  4. 实现sys_shmget()和sys_shmat()

    在kernel/目录下新建一个shm.c文件,用于保存这个两个函数。
    在include/目录下新建一个shm.h文件,用于相关数据类型声明和函数声明。

    sys_shmget()函数的主要作用是获得一个空闲的物理页面,可以通过调用已有的get_free_page()函数来实现。

操作系统实验6:地址映射与共享
sys_shmat()的主要作用是将这个页面和进程的虚拟地址以及逻辑地址关联起来,让进程对某个逻辑地址的读写就是在读写该内存页。该函数首先要完成虚拟地址和物理页面的映射,核心就是填写页表,在Linux-0.11中的函数:

unsigned long put_page(unsigned long page,unsigned long address)

该函数的作用就是完成这样的映射,直接调用即可。函数中的page就是内存页的物理地址,即shm_list[shmid].page,address是虚拟地址。Linux-0.11给每个进程分配了64M的虚拟内存,其分布如下图所示。
操作系统实验6:地址映射与共享
可以看出,brk和start_stack之间的虚拟内存并没有使用,因此可以在这里分割一个虚拟内存页和那个物理内存页建立映射。brk和start_stack都是存储在进程的PCB中,可以用current->brk找到当前进程的brk,当前进程开始的虚拟地址存放在current->ldt[1]中,可以用get_base(current->ldt[1])获得,因此该虚拟内存页的虚拟地址为get_base(current->ldt[1]) + current->brk.
这样调用put_page函数就可以建立映射关系了。最后需要更新brk指针的指向,并返回虚拟内存页的逻辑地址,即原来的brk。

shm.h文件的内容如下:

#include <stddef.h>     


typedef unsigned int key_t;

struct struct_shmem
{
    unsigned int size;
    unsigned int key;
    unsigned long page;
};

int shmget(key_t key, size_t size);
void* shmat(int shmid);

#define SHM_NUM  16 

shm.c文件内容如下:

#include <shm.h>
#include <linux/mm.h>        
#include <unistd.h>     
#include <errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>

struct struct_shmem shm_list[SHM_NUM] = {{0,0,0}};

int sys_shmget(key_t key, size_t size)
{
    int i;
    unsigned long page;
    
    if(size > PAGE_SIZE){
        errno = EINVAL;
        printk("shmget:The size connot be greater than the PAGE_SIZE!\r\n");
        return -1;
    }
    if(key == 0){
        printk("shmget:key connot be 0!\r\n");
        return -1;
    }
    //判斷是否已经创建
    for(i = 0; i < SHM_NUM; i++){
        if(shm_list[i].key == key)
            return i;
    }
    page = get_free_page();  //申请内存页
    if(!page){
        errno = ENOMEM;
        printk("shmget:connot get free page!\r\n");
        return -1;
    }
    for(i = 0; i < SHM_NUM; i++){
        if(shm_list[i].key == 0){
            shm_list[i].size = size;
            shm_list[i].key = key;
            shm_list[i].page = page;
            break;
        }
    }
    return i;
}


void* sys_shmat(int shmid)
{
    unsigned long tmp;  //虚拟地址
    unsigned long logicalAddr;
    if(shmid < 0 || shmid >= SHM_NUM || shm_list[shmid].page == 0 || shm_list[shmid].key <= 0){
        errno = EINVAL;
        printk("shmat:The shmid id invalid!\r\n");
        return NULL;
    }
    tmp = get_base(current->ldt[1]) + current->brk;  //计算虚拟地址
    put_page(shm_list[shmid].page,tmp);
    logicalAddr = current->brk;  //记录逻辑地址
    current->brk += PAGE_SIZE;  //更新brk指针
    return (void *)logicalAddr;
}
  1. 修改Makefile文件
    文件位置:kernel/Makefile
    操作系统实验6:地址映射与共享
    操作系统实验6:地址映射与共享
    修改完成之后重新编译整个工程。

  2. 编写测试代码
    这里编写两个测试进程,一个进程向共享内存页中写入数据,另一进程从中读取数据,比对写入和读出的数据就能验证实验过程是否正确。
    test1.c每间隔5秒向共享内存页中写入递增的数据,代码如下:

#define   __LIBRARY__
#include <shm.h>
#include <unistd.h>


static inline _syscall1(void*,shmat,int,shmid);
static inline _syscall2(int,shmget,key_t,key,size_t,size);

int main()
{
    key_t key = 666;
    size_t size = sizeof(int);
    int shmid = shmget(key,size);
    int* p = (int*)shmat(shmid);
    *p = 0;
    while(1){
        (*p)++;
        printf("process1:write  %d\r\n",*p);
        sleep(5);
    }
    return 0;
}

test2.c每间隔5妙从共享内存页中读取数据,代码如下:

#define   __LIBRARY__
#include <shm.h>
#include <unistd.h>


static inline _syscall1(void*,shmat,int,shmid);
static inline _syscall2(int,shmget,key_t,key,size_t,size);

int main()
{
    key_t key = 666;
    size_t size = sizeof(int);
    int shmid = shmget(key,size);
    int* p = (int*)shmat(shmid);
    while(1){
        printf("process2:read   %d\r\n",*p);
        sleep(5);
    }
    return 0;
}

  1. 文件拷贝
    先在lab6目录下,执行下列命令就行挂载:
sudo ./mount-hdc

然后将test1.c和test2.c拷贝到linux-0.11中,命令如下:

cp test1.c test2.c ./hdc/usr/root/

另外,在test1.c和test2.c中用到shm.h和修改过的unistd.h,因此也需要将这两个文件拷贝到Linux-0.11中,命令如下(在lab6目录下):

 cp ./linux-0.11/include/unistd.h ./hdc/usr/include/
 cp ./linux-0.11/include/shm.h ./hdc/usr/include/
  1. 编译并运行测试代码
    运行Linux-0.11,然后编译test1.c和test2.c,命令如下:
gcc -o test1 test1.c 
gcc -o test2 test2.c
sync 

注意,Linux-0.11只有一个终端,而现在需要在一个终端上同时运行两个程序,方法是在命令末尾输入&,命令就会进入后台运行。命令如下:

./test1 &
./test2 &

然后就可看到打印出来的信息了,如下图所示:
操作系统实验6:地址映射与共享
可以看出,写入和读出的数据是一样的。
注意,在实验过程发现,如果一个进程结束会将共享的物理内存页释放,此时如果另一个进程再去读写该物理内存页就会导致错误!

至此,整个实验结束!文章来源地址https://www.toymoban.com/news/detail-414579.html

到了这里,关于操作系统实验6:地址映射与共享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 银河麒麟桌面操作系统【Samba共享文件夹】配置手册

    银河麒麟桌面操作系统【Samba共享文件夹】配置手册 简介: Samba 是基于SMB/CIFS网络协议的重新实现的一个免费软件,包括服务端和客户端。SMB是Server Messages Block的缩写,是一种在局域网上共享文件|打印机的一种通信协议,为局域网内的不同计算机之间提供文件和打印机等资源

    2024年04月13日
    浏览(47)
  • 【操作系统】Centos7搭建nfs文件共享服务器实战

    1.NFS介绍 2.环境准备 3.在A服务端机器安装nfs-utils和rpcbind包 4.启动rpcbind检查是否启动了rpcbind服务并监听111端口 5.配置NFS服务的配置文件,参数配置:vi /etc/exports 数据同步与数据异步的区别 数据同步:当系统从内存中向磁盘中持久化数据时,同步发送表示只有当内存中的数据

    2024年02月07日
    浏览(55)
  • 【操作系统-内存】地址翻译流程图

    简略版: 完整版:(注意,TLB和Cache是组相联的) 字节编址 一页为 4KB 逻辑地址空间为 32 位(4GB) 物理地址空间为 28 位(256MB) 页表项长度为 4B TLB为八路组相联,一共16行(组数=16/8=2) Cache为四路组相联,一共16行(组数=16/4=4),Cache行大小为64B 字节编址 一页为 4KB 逻辑

    2024年02月12日
    浏览(44)
  • 凝思操作系统配置IP地址方法

    方法1: /etc/network/interfaces文件中配置: 如上,以此配置所有的eth*,, 测试: 1)未加allow-hotplug eth0     ip不生效; 2)这句可以不加:gateway 193.168.100.1   另外,正如,windows系统一块网卡可以设置多个ip地址, linux系统一块网卡,也可以设置多个ip 注意:第二个ip,没有添加g

    2024年02月12日
    浏览(43)
  • 1.2 - 操作系统 - DOS命令&Windows防火墙&Windows共享文件

    「作者简介」: CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「订阅专栏」: 此文章已录入专栏《网络安全入门到精通》

    2024年02月05日
    浏览(44)
  • 操作系统-区分文件共享的两种方式:软链接和硬链接

    软链接与硬链接是用来干什么的呢? 为解决文件的共享使用,Linux 系统引入了两种链接:硬链接 (hard link) 与软链接(又称符号链接,即 soft link 或 symbolic link)。链接为 Linux 系统解决了文件的共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。 由于linux下的

    2023年04月12日
    浏览(43)
  • 计算机操作系统实验:进程调度实验

    前言 二、实验目的 三、实验要求 四、实验原理 五、实验过程 六、代码详解 总结 计算机操作系统是管理计算机硬件和软件资源的核心软件,它负责为用户提供一个友好、高效、安全的使用环境。进程调度是操作系统的一个重要功能,它决定了进程在处理器上的执行顺序和时

    2024年02月07日
    浏览(52)
  • 操作系统实验(进程调度)

      1.1理解有关进程控制块、进程队列的概念。   1.2掌握进程优先权调度算法和时间片轮转调度算法的处理逻辑。   2.1设计进程控制块PCB的结构,分别适用于优先权调度算法和时间片轮转调度算法。   2.2建立进程就绪队列。   2.3编制两种进程调度算法:优先权调度

    2024年02月06日
    浏览(46)
  • 408操作系统-区分文件共享的两种方式:软链接和硬链接

    软链接与硬链接是用来干什么的呢? 为解决文件的共享使用,Linux 系统引入了两种链接:硬链接 (hard link) 与软链接(又称符号链接,即 soft link 或 symbolic link)。链接为 Linux 系统解决了文件的共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。 由于linux下的

    2023年04月17日
    浏览(59)
  • 操作系统期末实验:多用户二级文件系统

    期末实验不是python写的,所以很可能是当时在github上找了一个,然后改了改hhh 如果后续找到了链接就放过来 设计一个多用户的二级文件系统,能够实现简单的文件操作。具体包括如下几条命令: (1)Dir 列文件目录; (2)Create 创建文件 (3)Delete 删除文件 (4)Deldir 删除

    2024年01月18日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包