BUAA OS 2025 LAB4 EXAM & EXTRA

Lab 4比Lab 3简单的许多。

EXAM

本题目有两个部分,第一部分是要求加入一个新的系统调用sys_get_ppid,来得到当前进程的父进程。我们会注意到对于进程控制块Env中有一项是env_parent_id,所以我们的系统调用代码非常简单。

int sys_get_ppid(){
    //TODO
    return curenv->env_parent_id;
}

第二部分要求更改duppage,当其第2位为1的时候什么都不做,所以只要加入简单的判断即可.

if((perm&PTE_PROTECT)==0){
    /* Exercise 4.10: Your code here. (2/2) */
        if ((perm & PTE_D) == 0 || (perm & PTE_LIBRARY) || (perm & PTE_COW)) {
            if ((r = syscall_mem_map(0, (void *)addr, envid, (void *)addr, perm)) < 0) {
                user_panic("user panic mem map error: %d", r);
            }
        } else {
            if ((r = syscall_mem_map(0, (void *)addr, envid, (void *)addr,
                        (perm & ~PTE_D) | PTE_COW)) < 0) {
                user_panic("user panic mem map error: %d", r);
            }
            if ((r = syscall_mem_map(0, (void *)addr, 0, (void *)addr,
                        (perm & ~PTE_D) | PTE_COW)) < 0) {
                user_panic("user panic mem map error: %d", r);
            }
        }
    }

其实也就改了第一行多了个if

EXTRA

题目背景

在 Lab4 课下实验中,我们已经了解了系统调用机制,并且实现了一个简易的进程间通信(IPC)。但是这样的机制基于单页,并且限于双方进程,我们现在想要实现一个简易的共享内存(Shared Memory,简记为 SHM),能够实现多进程间的不定页数通信。

为了实现这一点,我们需要在内核态维护一个数据结构 struct Shm,用来维护“一块共享内存”,其各个成员如下:

struct Shm {
    u_int npage;
    struct Page *pages[N_SHM_PAGE];
    u_int open;
};

其中:

npage 表示共享内存总共的页数,在我们的测试中被限制为 [1, N_SHM_PAGE] = [1, 8]
pages 为用于共享的物理页面,通过将不同进程的虚拟地址映射到相同的物理页面,可以实现内存共享
open 用于维护这一块共享内存的状态,open != 0 表示其已经被分配
在我们的测试中,限制共享内存的数量为 N_SHM = 8 个,使用内核数组维护。数组索引即为它们各自的编号。

系统调用约定

你需要实现下列系统调用,它们的定义与功能如下:

int sys_shm_new(u_int npage)
申请总大小为 npage 个页面的共享内存。在申请时,首先找到一个编号最小的,未被分配的(open = 0)的共享内存,并使用 page_alloc 函数申请 npage 个页面,进行必要的操作后,记录在 pages 数组中。

如果找不到这样可用的共享内存,返回 -E_SHM_INVALID 而无需申请页面。如果在申请页面时,空闲页面不足,返回 -E_NO_MEM。在这个过程中,请避免造成内存泄漏(也即,当发现分配失败时,你需要释放已经分配的页面)。

正确执行后,返回共享内存的编号(数组下标)。

int sys_shm_bind(int key, u_int va, u_int perm)
将虚拟地址 va(保证按页对齐) 作为共享内存的起始地址,映射到编号为 key 的共享内存中。你需要将虚拟地址范围 [va, va + npage * PAGE_SIZE) 依次映射到共享内存的 npage 个物理页面上。

如果对应的共享内存未被分配(open = 0),则返回 -E_SHM_NOT_OPEN。否则,返回 0 即可。

int sys_shm_unbind(int key, u_int va)
将虚拟地址范围 [va, va + npage * PAGE_SIZE) 解除映射(va 保证按页对齐)。简单起见,这里的实现不需要关心这些虚拟地址是否真的被映射到共享内存之中,使用 page_remove 函数移除映射即可。

如果 key 对应的共享内存未被分配(open = 0),返回 -E_SHM_NOT_OPEN。否则,返回 0 即可。

int sys_shm_free(int key)
将 key 对应的共享内存注销(将 open 设为 0),并对页面进行必要的操作,将它们释放。

如果该共享内存原本未被分配,返回 -E_SHM_NOT_OPEN。否则,返回 0 即可。

测试数据保证在调用 sys_shm_free 前,全部的映射都已经被 unbind 了。

注意事项

  • 测试数据保证,如果一个进程存在一个绑定了共享内存的页面时,则不会进行 fork,因为此时的行为没有在题面中给出定义。你不需要考虑这种情形。
  • 你的实现要能够支持同一个进程不同地址间的共享,同一个进程的不同地址也可以被映射到同一个物理页面上,但测试数据保证,同一个进程的某一虚拟地址不会被多次绑定。你不需要维护虚拟地址与共享内存的关系,仅需要实现系统调用的功能即可。
  • 对于某个共享内存的页面,可能被绑定多个进程,然后被全部解除绑定,此时,你应该保证这些物理页面仍可以进行新的绑定操作。提示:你可以通过在创建共享内存时,增加页面的 pp_ref 记录,并在销毁共享内存时使用 page_decref 函数释放页面。
  • 测试数据保证,共享页面中的内存仅作为数组进行访问,而不会存放代码段、栈数据等特殊内容。
    测试程序会使用 IPC 机制进行进程间的同步。

题目要求

user/lib/shm.cinclude/shm.h 的内容已在初始化分支时向仓库中添加,你可以查看它们的具体内容。

// include/shm.h
#ifndef _SHM_H_
#define _SHM_H_

#include <mmu.h>
#include <types.h>

#define N_SHM_PAGE 8
#define N_SHM 8

struct Shm {
    u_int npage;
    struct Page *pages[N_SHM_PAGE];
    u_int open;
};

#endif
// user/lib/shm.c
#include <lib.h>
#include <mmu.h>

int shm_new(u_int npage) {
    return syscall_shm_new(npage);
}

int shm_bind(u_int key, void *va) {
    return syscall_shm_bind(key, (u_int)va, PTE_D);
}

int shm_unbind(u_int key, void *va) {
    return syscall_shm_unbind(key, (u_int)va);
}

int shm_free(u_int key) {
    return syscall_shm_free(key);
}

此外,你可以按照下面的步骤进行:

include/error.h 中,添加下列宏:

#define E_SHM_INVALID 14
#define E_SHM_NOT_OPEN 15

user/include/lib.h 中,添加下列系统调用的用户态封装,以及库函数声明:

// syscalls
int syscall_shm_new(u_int npage);
int syscall_shm_bind(int key, u_int va, u_int perm);
int syscall_shm_unbind(int key, u_int va);
int syscall_shm_free(int key);

// shm.c
int shm_new(u_int npage);
int shm_bind(u_int key, void *va);
int shm_unbind(u_int key, void *va);
int shm_free(u_int key);

include/syscall.h 中,向 enum 中添加以下系统调用号。请注意新增系统调用号的位置,应当位于 MAX_SYSNO 之前;

SYS_shm_new
SYS_shm_bind
SYS_shm_unbind
SYS_shm_free

user/lib/syscall_lib.c 中添加系统调用封装的具体实现,使用 msyscall 函数陷入内核:

int syscall_shm_new(u_int npage) {
    // Lab4-Extra: Your code here. (1/8)
}

int syscall_shm_bind(int key, u_int va, u_int perm) {
    // Lab4-Extra: Your code here. (2/8)
}

int syscall_shm_unbind(int key, u_int va) {
    // Lab4-Extra: Your code here. (3/8)
}

int syscall_shm_free(int key) {
    // Lab4-Extra: Your code here. (4/8)
}

kern/syscall_all.c 中引入相关的头文件,定义表示共享内存的内核数组,并添加系统调用在内核中的实现函数。请保证函数的定义位于系统调用函数表 void *syscall_table[MAX_SYSNO] 之前

#include <shm.h>

// ... ...

struct Shm shm_pool[N_SHM];

int sys_shm_new(u_int npage) {
    if (npage == 0 || npage > N_SHM_PAGE) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (5/8)

    return ??;
}

int sys_shm_bind(int key, u_int va, u_int perm) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (6/8)

    return ??;
}

int sys_shm_unbind(int key, u_int va) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (7/8)

    return ??;
}

int sys_shm_free(int key) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (8/8)

    return ??;
}

kern/syscall_all.c 中的 void *syscall_table[MAX_SYSNO] 系统调用函数表中,为步骤 3 中添加的系统调用号添加对应的内核函数指针。

[SYS_shm_new] = sys_shm_new
[SYS_shm_bind] = sys_shm_bind
[SYS_shm_unbind] = sys_shm_unbind
[SYS_shm_free] = sys_shm_free

分析

要求我们实现一种共享内存,并且给出了较为消息的实现步骤,主要是实现四个系统调用

int syscall_shm_new(u_int npage) {
    // Lab4-Extra: Your code here. (1/8)
    return msyscall(SYS_shm_new,npage);
}

int syscall_shm_bind(int key, u_int va, u_int perm) {
    // Lab4-Extra: Your code here. (2/8)
    return msyscall(SYS_shm_bind,key,va,perm);
}

int syscall_shm_unbind(int key, u_int va) {
    // Lab4-Extra: Your code here. (3/8)
    return msyscall(SYS_shm_unbind,key,va);
}

int syscall_shm_free(int key) {
    // Lab4-Extra: Your code here. (4/8)
    return msyscall(SYS_shm_free,key);
}

提示非常详细,唯一没说的就是在实现虚拟地址到物理地址的映射的时候需要使用page_insert,并且要注意pprefpage_alloc后及时++即可,所以最后的这部分的代码为

int sys_shm_new(u_int npage) {
    //printk("\nTry shm new\n");
    if (npage == 0 || npage > N_SHM_PAGE) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (5/8)
    for(int i=0;i<N_SHM;++i){
        if(shm_pool[i].open==0){
            // use this
        //  printk("id is %d\n",i);
            shm_pool[i].npage=npage;//这里的page指针其实还是虚拟地址,不过是kseg0的
            int k=0;
            while(k<npage){
                int r=page_alloc(&shm_pool[i].pages[k]);
                if(r==-E_NO_MEM){
                    // 清除之前的映射

                    for(int j=0;j<k;++j){
                        page_decref(shm_pool[i].pages[j]);
                    }
                    return -E_NO_MEM;
                }
                shm_pool[i].pages[k]->pp_ref++;
                k++;
            }
        //  printk("Success new shm\n");
            shm_pool[i].open=1;
            return i;
        }
    }
    return -E_SHM_INVALID;
}

int sys_shm_bind(int key, u_int va, u_int perm) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (6/8)
    if(shm_pool[key].open==0){
        return -E_SHM_NOT_OPEN;
    }
    int cnt=0;
    for(u_int i=va;i<va+shm_pool[key].npage*PAGE_SIZE;i+=PAGE_SIZE){
        page_insert(cur_pgdir,curenv->env_asid,shm_pool[key].pages[cnt],i,perm);
        cnt++;
    }
    return 0;
}

int sys_shm_unbind(int key, u_int va) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }

    // Lab4-Extra: Your code here. (7/8)
    if(shm_pool[key].open==0){
        return -E_SHM_NOT_OPEN;
    }
    int cnt=0;
    for(u_int i=va;i<va+shm_pool[key].npage*PAGE_SIZE;i+=PAGE_SIZE){
        page_remove(cur_pgdir,curenv->env_asid,i);
        cnt++;
    }
    return 0;
}

int sys_shm_free(int key) {
    if (key < 0 || key >= N_SHM) {
        return -E_SHM_INVALID;
    }
    // Lab4-Extra: Your code here. (8/8)
    if(shm_pool[key].open==0){
        return -E_SHM_NOT_OPEN;
    }
    for(int i=0;i<shm_pool[key].npage;++i){
        page_decref(shm_pool[key].pages[i]);
    }
    shm_pool[key].open=0;
    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇