LAB 4 多任务调度

本实验的主要内容:

  • 多处理器支持,循环调度。
  • 实现一个类 Unix 的 fork()
  • 支持对进程间通信 (IPC) 的支持。

1、多处理器支持

此部分是对多处理器的初始化。

i386_init
// 多处理器初始化函数
--> mp_init() // 在启动 APs 之前,BSP 首先收集有关多处理器系统的信息,例如 CPU 的总数、其 APIC ID                
// 和 LAPIC 单元的 MMIO 地址。根据找到的配置信息,设置 MultiProcessor Specification
    --> mpconfig() // 寻找 MP 配置表,检查签名、校验和和和版本是否正确。
        --> mpsearch() // 从 BIOS 的 0xE0000-0xFFFFF 处找到一个 MP 结构。
            --> mpsearch1(a, len) // 在物理地址 a 的 len 字节中查找 MP 结构。
    
// LAPIC 单元负责在整个系统中提供中断。LAPIC 还为其连接的 CPU 提供唯一标识符。
--> lapic_init()
    --> mmio_map_region()
        --> boot_map_region()
    --> lapicw()
    
// 多任务初始化函数
// Initialize the 8259A interrupt controllers.
--> pic_init()
    --> irq_setmask_8259A(irq_mask_8259A)
    
// BSP 获取大内核锁   
--> lock_kernel()
    --> spin_lock() //请求锁,一直循环直到获得锁
    
// 由 BSP 启动应用处理器    KADDR(MPENTRY_PADDR) is : f0007000
--> boot_aps()
    // 一次引导一个 AP
    // Tell mpentry.S what stack to use
    // Start the CPU at mpentry_start
    --> lapic_startap() // Start additional processor running entry code at addr. 
    // Wait for the CPU to finish some basic setup in mp_main().
    // Send startup IPI(处理器之间中断) (twice!) to enter code.
        // entry point for APs
        --> mpentry.S // 设置寄存器、开启分页、初始化堆栈、调用 mp_main()
            --> mp_main() // 初始化 lapic、env、trap,启动完成。获取内核锁,运行程序。
    
// 所有 CPU 启动完成,开始运行进程。首先创建进程,然后运行 sched_yield() 进行进程调度。
--> sched_yield()
    // 寻找所有可执行的进程执行。如果没有可以运行的进程,使用 sched_halt(),使 CPU halted。在最后一个      
    // CPU 时,进入 monitor。
    --> sched_halt()
        --> monitor()

2、实现系统调用以支持用户进程创建进程

kern/dumbfork.c 为例。

 kern/dumbfork.c
 // 从用户态下 umain() 开始、
 --> umain(argc, argv) // 执行程序代码
     --> who = dumbfork() // fork a child process, return envid
        // 系统调用,进入内核态
        --> envid = sys_exofork() // Allocate a new child environment
        // 调用完成,返回用户态
        // Eagerly copy our entire address space into the child.
        --> duppage() // Also copy the stack we are currently running on.
        // Start the child environment.
        --> sys_env_set_status(envid, ENV_RUNNABLE)                 
     // running
     --> sys_yield() // 父子进程循环调度执行

3、Copy-on-Write Fork

本实验最重要的内容。

实现用户级别的 lib/fork()

fork() 的基本控制流程:

  1. 父级使用上面实现的 set_pgfault_handler() 函数将 pgfault() 安装为 C 级页面错误处理程序。

  2. 父级调用 sys_exofork() 来创建子环境。

  3. 对于 UTOP 下其地址空间中的每个可写页或写时复制页,父级调用 duppage,它应将 写时复制页 映射到子级的地址空间,然后在自己的地址空间中重新映射 “写时复制” 页。[注:此处的顺序(即在子页面中标记为 COW,然后在父页面中标记该页面)实际上很重要!你知道为什么吗?试着想一个具体的例子,在这种情况下,颠倒顺序可能会引起麻烦] duppage 设置两个 PTE,使页面不可写,并在 “avail” 字段中包含 PTE_COW,以区分写时复制页和真正的只读页。

    但是,异常堆栈不会以这种方式重新映射。相反,您需要为异常堆栈在子级中分配一个新页。由于页错误处理程序将执行实际的复制操作,而页错误处理程序在异常堆栈上运行,因此无法在写入时复制异常堆栈:谁会复制它?

    fork() 还需要处理存在但不可写或写时复制的页。

  4. 父级为子级设置用户页错误入口点,使其看起来像自己的。

  5. 子进程现在可以运行了,因此父进程将其标记为可运行的。

forktree 为例;

// user/forktree.c/umain()

--> forktree("")
    // fork 子进程 0
    --> forkchild(cur, '0')
        --> fork()
            --> set_pgfault_handler(pgfault) // Set up our page fault handler.
            --> sys_exofork() // Allocate a new environment.
            // Copy our address space and page fault handler setup to the child.
            // Map our virtual page pn (address pn*PGSIZE) into the target envid
            // at the same virtual address.
            --> duppage() 
                --> sys_page_map()
            // alloc a page and map child exception stack.
            --> sys_page_alloc()
            // 将父进程的 pgfault 函数注册给子进程。
            --> sys_env_set_pgfault_upcall()
            // 标记子进程为可运行。
            --> sys_env_set_status()
    // fork 子进程 1
    --> forkchild(cur, '1')

4、进程间通信 (IPC)

也是很重要的一个内容。

主要实现两个系统调用:

  • sys_ipc_try_send()
  • sys_ipc_recv()

进程间通过这两个系统调用实现通信。

sendpage 为例:

user/sendpage/umain()

// parent process
--> who = fork() // fork 一个子进程,返回子进程的 id。父与子进程内容基本相同。
    // kern/syscall
    --> sys_exofork() // 分配一个新进程,并返回进程 id。 
                      // 子进程的 env_parent_id 字段被设置为 parent_id。
// 父进程没有进入 if ((who = fork()) == 0) 分支,顺序执行。
--> sys_page_alloc() // 在指定位置分配一页内存。
--> memcpy()  // 将要发送的内容复制到分配的内存页地址处。
--> ipc_send()  // 父进程发送内存的页地址给子进程。
    // kern/syscall
    // 不断轮询发送直到发送成功。
    --> r = sys_ipc_try_send(to_env, val, pg, perm)
    // 若子进程没有收到,则放弃 CPU,进行调度。
    // 系统调用,进入内核态。
    --> sys_yield()
        // 调度,运行子进程。
        --> sched_yield()
    
    
// child process
// 子进程进入 if ((who = fork()) == 0)。
--> ipc_rev(&who, TEMP_ADDR_CHILD, 0) // 子进程接收信息。who 是父进程 id。
    // kern/syscall
    --> r = sys_ipc_recv() // 从指定位置接收信息。
        // 设置子进程为接收状态,调度,执行父进程。
        --> sched_yield()
    
    
// parent process
// 继续轮询,发现子进程已经进入接收状态,发送消息:
// 即设置子进程的 env_ipc_* 等属性。
--> r = sys_ipc_try_send(to_env, val, pg, perm)
    --> 发送完毕。
--> ipc_recv(&who, TEMP_ADDR, 0) // 父进程接收信息。who 是子进程id。
    --> r = sys_ipc_recv(pg) // 进入接收状态,让出 CPU。
        --> sched_yield()
    
// child process      
// 子进程执行 ipc_rev()。
--> ipc_recv()
--> ipc_send() // 子进程发送信息。
    // kern/syscall
    // 发送信息,成功。
    --> r = sys_ipc_try_send(to_env, val, pg, perm)
--> return
--> exit()
    
// parent process   
--> ipc_recv()  // 从指定位置接收信息。
    -->sys_ipc_recv(dstva)
--> return
--> exit()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容