BUAA-OS 实验笔记之 Lab4

一、Lab4 前言 Lab4 主要实现了系统调用,并通过系统调用实现了进程的创建和通信等操作。按照提示编写代码的难度应该不大(除非你的 Lab3 schedule 函数有 bug,很可惜我就是这样 :(),所以本次的笔记更多的讨论了一些和实验无关的代码。希望不会显得太啰嗦。 二、系统调用 (1)从一个用户程序引入 在之前的几篇文章中,我们大致循着内核初始化的过程进行分析。可是在这 Lab4 中这一思路就不适用了。因为在本次实验中我们所要实现的,不过是一些由内核提供的,可供用户程序调用的接口而已。这种调用被称为系统调用。 但是为了保持文章行文的一致性,我们还是希望确定一个入口开始讲解。正好在 mips_init 中有这样的语句,那我们就从被创建的这个用户程序开始。 // lab4: // ENV_CREATE(user_tltest); 这里需要插一嘴,在 Lab3 中我们就已经使用 ENV_CREATE 完成了一些程序的加载,可你有没有仔细看过被加载的程序的源代码是什么样的?代码在 user/bare 路径下。我们查看其中的 put_a.c 程序 void _start() { for (unsigned i = 0;; ++i) { if ((i & ((1 << 16) - 1)) == 0) { // Requires `e->env_tf.cp0_status &= ~STATUS_KUp;` in kernel to work *(volatile char *)0xb0000000 = 'a'; *(volatile char *)0xb0000000 = ' '; } } } 你会发现这些所谓的程序并没有 main 函数,而是 _start。实际上和内核一样,我们同样在用于用户程序编译的链接器脚本中将程序入口设定为 _start。该脚本为 user/user....

四月 13, 2023 · 18 分钟 · 3782 字 · Wokron

BUAA-OS 实验笔记之 Lab3

一、Lab3 前言 不知道为什么,虽然写 Lab3 所用的时间比 Lab2 少,但这次的笔记居然比 Lab2 长。我认为可能是因为自己在本篇文章中讲了更多和实验本身无关的东西。不过既然讲了,应该也会对进一步认识操作系统起到一些作用吧。希望本篇文章不会显得太啰嗦。 二、内核初始化(再续) Lab2 中,我们在内核初始化阶段初始化了虚拟内存的相关信息,Lab3 中我们要继续这一过程。本次实验中我们会完成进程控制的初始化。 (1)再度 mips_init 我们查看 Lab3 中 init/init.c 的 mips_init 函数的内容变化。与 Lab2 相比,其中多调用了如下的方法 env_init、ENV_CREATE_PRIORITY、kclock_init 和 enable_irq。 void mips_init() { printk("init.c:\tmips_init() is called\n"); // lab2: mips_detect_memory(); mips_vm_init(); page_init(); // lab3: env_init(); // lab3: ENV_CREATE_PRIORITY(user_bare_loop, 1); ENV_CREATE_PRIORITY(user_bare_loop, 2); // lab3: kclock_init(); enable_irq(); while (1) { } } 其中 env_init 用于进程控制的初始化,ENV_CREATE_PRIORITY 手工创建了两个进程,kclock_init 和 enable_irq 设置了时钟中断并启用了中断。后两者将分别在第三和四节介绍。本届只介绍前者。 (2)进程管理的数据结构 让我们深入在 kern/env.c 中的 env_init,在该函数中,首先初始化了两个列表 void env_init(void) { int i; /* Step 1: Initialize 'env_free_list' with 'LIST_INIT' and 'env_sched_list' with * 'TAILQ_INIT'....

三月 30, 2023 · 14 分钟 · 2901 字 · Wokron

BUAA-OS 实验笔记之 Lab2

一、Lab2 前言 这篇文章应该是我目前写过的文章中长度排行前几的了。Lab2 的内容着实繁多,不仅是分页内存管理本身的理论和实现细节颇多;操作系统的基本知识和注意事项也占据了很大的篇幅。后者在不理解的情况下实在会对本次实验产生许多困惑。本人也是在逐步地探索之后才得以有了较多的认识——当然,这一认识或许也只是片面的。 本文逐函数、逐代码地讲解了 Lab2 中新增的内容。主要在于内核初始化中关于内存的部分以及分页内存管理的实现。在本文中,关于链表宏和虚拟/物理内存的辨析也占据了比较多的内容。 二、内核初始化(续) 在 Lab1 中,我们的内核初始化过程只进行了一部分。因为 Lab1 中 mips_init 函数几乎没有任何功能。在 Lab2 中,我们会继续推进这一过程。 在 Lab2 中,我们会建立操作系统的内存管理机制。具体来说,我们会在 mips_init 中调用三个函数 mips_detect_memory、mips_vm_init 和 page_init。这三个函数会分别完成探测内存、初始化虚拟地址和初始化页的工作。接下来我们会分别介绍这三个函数。 Lab2 中 mips_init 的结构如下: void mips_init() { printk("init.c:\tmips_init() is called\n"); // lab2: mips_detect_memory(); mips_vm_init(); page_init(); while (1) { } } (1)探测内存 mips_detect_memory 的作用是获取总物理内存大小,并根据物理内存计算分页数。 注意!是物理内存 void mips_detect_memory() { /* Step 1: Initialize memsize. */ memsize = *(volatile u_int *)(KSEG1 | DEV_MP_ADDRESS | DEV_MP_MEMORY) 第一步中的这条语句似乎使人困惑。为什么这样就可以获得物理内存大小了呢?我们可以查看一下 DEV_MP_ADDRESS 和 DEV_MP_MEMORY 所在的头文件。它们定义在 include/driver/dev_mp....

三月 20, 2023 · 11 分钟 · 2267 字 · Wokron

BUAA-OS 实验笔记之 Lab1

一、总前言 操作系统是一门重课,我并不知晓自己是否做好了准备。“在这样的情况下就开始写文章,是否太着急了?” 我这样想,不知道对这门课自己是否有写文章的水平,也不知道自己是否会半途而废。 但我还是决定开始,并不是因为有什么十足的信心,而是希望这一系列文章能帮助自己更深入的理解操作系统的知识,在讲解的过程中发现自己的不足。我希望这系列能持续下去,希望未来的自己看到结果时能够满意;希望他人也能从中得到收获。 二、进入操作系统 (1)操作系统的启动 操作系统的 boot 过程是一个复杂繁琐的过程,从 bios 从上电后的启动地址开始执行,初始化硬件,读取磁盘的主引导记录,跳转到 bootloader;到加载内核程序,跳转到操作系统入口。这一整个过程难以详述…… 不过幸好在本实验中,这些都不是问题,因为我们所使用的 GXemul 模拟器不会去执行上述环节,它可以直接加载 ELF 格式内核。也就是说,我们的操作系统实验是从跳转到操作系统入口开始的。 (2)内核的入口和内存布局 所以,哪里是操作系统入口?内核入口的设置在 kernel.lds 中,这是一个链接器脚本,用于帮助链接器确定最终生成的文件的组织形式。 我们看一下该文件的开头。 /* * Set the architecture to mips. */ OUTPUT_ARCH(mips) /* * Set the ENTRY point of the program to _start. */ ENTRY(_start) 其中 OUTPUT_ARCH(mips) 设置了最终生成的文件采用的架构,对于 MOS 来说就是 mips。而 ENTRY(_start) 便设置了程序的入口函数。因此 MOS 内核的入口即 _start。这是一个符号,对应的是 init/start.S 中的 .text EXPORT(_start) .set at .set reorder /* disable interrupts */ mtc0 zero, CP0_STATUS /* omit....

三月 14, 2023 · 6 分钟 · 1206 字 · Wokron

系统编程之线程管理

一、Linux 多线程简述 进程和线程的关系老生常谈。线程是最小的调度单位,进程是最小的资源分配单位。同一进程中的多个线程是在共享的内存空间中并发的多道执行路径,它们共享一个进程的资源。 对于Linux来说,Linux线程属于用户级线程,即线程的调度是在用户空间执行的。也就是说,Linux线程的实现是在内核之外的,多线程的概念对于内核来说并不是真实存在的,而只是通过线程库中的程序模拟的并发效果。 Linux线程遵循POSIX线程接口,称为pthread。pthread在其他平台也有对应的实现,如在windows。 二、线程操作 (1)库的使用 在开始多线程编程之前,需要说明一下 pthread.h 库。在编译使用pthread.h库的代码时,一般需要加-lpthread。pthread在glibc2.34之前是在glibc里面的,之后分出来变成一个单独的库,因此有的情况下,不加-lpthread也能编译成功。 (2)基本操作 创建线程 int pthread_create(pthread_t _Nullable * _Nonnull __restrict, const pthread_attr_t * _Nullable __restrict, void * _Nullable (* _Nonnull)(void * _Nullable), void * _Nullable __restrict); 该函数中第一个参数为指向一个线程标识变量的指针。第二个参数用来手动设置线程的各项属性,一般可以用NULL选择默认属性。第三个参数为一个函数指针,表示新建线程时需要执行的函数。注意该函数的参数类型和返回值类型,使用时需要进行强制类型转换。第四个参数为传递给函数的参数,也就是线程执行的函数的参数。不传递参数时可设置为NULL。 如下举一个创建线程的例子。 pthread_t tid; if (pthread_create(&tid, NULL, do_something, NULL)) { // error handler } 线程退出 void pthread_exit(void *ral_ptr); 当某一线程执行该函数时,会导致该线程结束。结束时会将ral_ptr指针传递给pthread_join 函数的 rval_ptr 线程取消 int pthread_cancel(pthread_t tid); 某一线程调用该函数,可以终止同一进程内的其他线程。 tid 即要终止的线程。 线程挂起 int pthread_join(pthread_t thread, void **rval_ptr); 某一线程调用该函数会阻塞该线程,直到参数 thread 所指示的线程退出。第二个参数为一个指向 pthread_exit 所设置的 ral_ptr 指针的指针。...

十二月 22, 2022 · 4 分钟 · 745 字 · Wokron

系统编程之进程间通信

一、进程间通信简述 进程是程序的一次运行的动态过程,为了完成一个任务,很多进程之间需要进行通信,从而相互合作以实现需要的功能。操作系统内核中提供了进程间通信的方法,主要有以下几种: 管道: 管道是最基本的进程通信机制,可以想象成一个管道,两端分别连着 2 个进程,一个进程往里面写,一个进程从里面读。如果读或写管道的时候没有内容可供读或写,进程将被阻塞,直到有内容可供读写为止。 消息队列: 消息队列本质上在内核空间中开辟了一块内存空间,这块内存是其他进程可以访问到的,在其中使用链表的方式实现了一个队列,进程可以向该队列中发送数据块或读取数据块,从而达到进程间通信的目的。其中每个数据块包含两部分,首先是一个类型为 long 的 type,然后是具体的数据,数据块的 type 可以作为进程之间相互约定好的协议。例如一个进程发送 type 为123的消息,另一个进程接收 type 为123 的消息,后者便可确认这就是前者发送的信息,并信任该数据块中的数据。 信号量: 不同进程之间存在对资源的竞争,信号量就是用来标明可用资源的数量的数据结构,本质是为了实现多个进程之间的同步。需要注意,信号量(semaphore)与 “信号”(signal)没有关系。 共享内存: 共享内存的本质就是把两个或多个进程的虚拟地址映射到同一块物理内存。这样,一个进程通过对这块内存的读写就能被其他进程访问到,从而实现进程间通信的功能。 二、进程间通信操作 (1)准备操作 获取 key #include <sys/types.h> #include <sys/ipc.h> key_t ftok( const char * fname, int id ); 共享内存,消息队列,信号量等进程间通信方式都需要寻找一个中间介质来进行通信。不同的介质需要用不同的信息来进行区分,这就是进程间通信的 key。ftok() 函数就可以生成一个唯一的 key,该函数获取一个文件路径和一个字序号,生成一个用于区分的 key。 注意,选择文件路径只是因为文件的编号是独有的。设置的文件路径与代码和程序并没有什么关系。 命令管理进程间通信 若没有调用控制函数进行删除,则已分配的进程间通信不会自动释放。如果共享内存,消息队列,信号量在新进程执行时依旧有之前残留的信息,可能导致程序运行结果错误。可以通过 ipcs 和 ipcrm 命令进行管理。 ipcs # 显示所有进程间通信信息 ipcrm -q MsgID # 删除消息队列 ipcrm -s SemID # 删除信号量 ipcrm -m ShrID # 删除共享内存 (2)消息队列 消息队列获取 #include <sys/types....

十一月 16, 2022 · 5 分钟 · 962 字 · Wokron