I/O复用详解
I/O复用简介I/O多路复用(I/O multiplexing)是一种高效的I/O处理技术,它允许单个线程或进程同时监视多个文件描述符的状态,以确定哪些文件描述符已准备好进行I/O操作。
处理场景
客户端程序要同时处理多个socket,非阻塞的connect。
客户端程序要同时处理用户输入和网络连接。比如本章将要讨 论的聊天室程序。
TCP服务器要同时处理监听socket和连接socket。
服务器要同时处理TCP请求和UDP请求。比如本章将要讨论的 回射服务器。
服务器要同时监听多个端口,或者处理多种服务。
IO多路复用技术是为了解决高并发场景下的资源利用问题。IO多路复用通过内核级别的机制,如select、poll、epoll等,能够在一个线程或进程中同时监控多个IO通道的状态,仅在有事件发生时才进行相应的IO操作。
I/O复用是阻塞I/O
I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依次处理其中的每一个文件描述符,这使得服务器程序看起来像是串行工作的。如果要实现并发,只能使用多进程或多线 ...
Socket网络编程基础
Socket网络编程C/S模型服务器-客户机,即Client-Server(C/S)结构。C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务。
在C/S结构中,应用程序分为两部分:服务器部分和客户机部分。服务器部分是多个用户共享的信息与功能,执行后台服务,如控制共享数据库的操作等;客户机部分为用户所专有,负责执行前台功能,在出错提示、在线帮助等方面都有强大的功能,并且可以在子程序间自由切换。
IP层每台互联网中的主机都运行实现TCP/IP协议,并且所有现代计算都支持这个协议。互联网中的客户端与服务端混合使用套接字(socket)接口函数与Unix的I/O函数进行通信。
ip地址一个ip地址是32位无符号整数。如下述所示网络程序将IP地址存放在一个结构体中
1234/* IP address structure */struct in_addr { uint32_t s_addr; /* Address in network byte order (big-endian) */};
字节序字节序分为大端字节序(big endian)和小 ...
xv6:文件系统
File system
写在前面:本文是基于xv6以及MIT6.S081课程内容所写的笔记,在阅读时配合xv6源码理解效果更佳。
简介 设计文件系统的主要目的是为了组织与存储数据。文件系统中提供了共享用户与程序的数据的方式。同时也提供了持久化(persistence),也就是在设备丢失并重启后,数据仍然能够保存,众所周知内存(RAM)设备的数据断电后是会丢失的。
xv6的文件系统提供了UNIX风格的文件、目录、路径名和在磁盘存储数据(persistence)的方式。同时这样的文件系统也会给设计带来一些挑战:
保存在硬盘上(on-disk)的数据结构:文件系统需要表示目录和文件名称的树(tree),记录每个文件内容块的标识,并记录磁盘中那些块是空闲的。
crash恢复(crash recovery):如果电脑crash发生,文件系统需要在重启后正常工作。问题在于在crash会中断更新的顺序,并且导致on-disk数据结构与文件数据不一致。
维持不变量(maintian in variant):不同的进程同时操作一个文件的时候,需要协调数据的更新,预防并发读写的数据竞 ...
Mit6.S081:lab mmap
实验:Lab: mmap
实验开始之前需要将git分支切换到fs分支不然有些文件你是没有的
123$ git fetch$ git checkout mmap$ make clean
Lab: mmap (hard)简介 本次实验是实现mmap与munmap这两个系统调用,它们可用于在进程之间共享内存,将文件映射到进程地址空间,并作为用户级缺页方案。在本实验中,我们将向xv6添加mmap和munmap,重点关注内存映射文件。如果忘记了缺页是如何实现的可以回顾COW实验。
如果你不知道mmap系统调用的相关作用以及标识的意义,那么可以在linux下使用man命令查看mmap文档:
123456789101112ubuntu% man 2 mmap----------------------------------------------------------------MMAP(2) Linux Programmer's Manual ...
Mit6.S081:lab fs
实验:Lab: fs
实验开始之前需要将git分支切换到fs分支不然有些文件你是没有的
123$ git fetch$ git checkout fs$ make clean
Large files (moderate)简介 在xv6中支持的最大文件大小是268KB,是因为在inode中保存磁盘块的地址指针只有13个(12直接指针,1个间接指针)。本次实验中我们需要扩大这个限制,主要的实现思路是使用二级间接指针,其具体思路请参考inode章节讲解。
任务:本次实验主要是将inode的地址字段修改为11直接指针+1间接指针+1间接指针,并修改fs.c中bmap函数与itruc函数,,实现二级指针的检测与删除,通过bigfile与usertests -q测试便可以完成本次实验。如果有疑问还请阅读实验文档。
本次实验的测试程序为bigfile,它会在一个文件中创建65803个块大小的数据,如果不修改源码就会出现下述的问题。并且通过bigfile测试大概会花费2分半左右的时间完成,期间会打印.告诉你程序在运行,如果没有持续的打印,那么说明你的程序出现死锁了。
1234 ...
Mit6.S081:lab locks
实验:Lab: locks
实验开始之前需要将git分支切换到locks分支不然有些文件你是没有的
123$ git fetch$ git checkout lock$ make clean
Memory allocator(moderate)简介 在kalloctest程序会对内存分配器进行压力测试:三个进程测试点会增长和压缩内存空间,该过程中多次调用在kalloc.c文件中的kalloc()与kfree(),这两个函数又会多次调用锁(kmem.lock)。这时会造成锁的竞争(lock contention),也就是多个CPU一起竞争想要获取同一把锁,这样会导致CPU资源占用从而影响性能。
注意锁竞争(lock contention)与数据竞争(data race)是不同的:虽然两者都是并发中的概念,但是锁竞争只会影响并发的性能,而数据竞争会造成数据的完整性与正确性,通常使用锁来保证数据不被竞争。
在kalloctest运行时,会打印acquire调用(获得kmem.lock)次数与锁竞争(尝试获得kmem.lock)的次数,如下面打印结果所示:#test- ...
Mit6.S081:lab net
实验:Lab: networking
实验开始之前需要将git分支切换到net分支不然有些文件你是没有的
123$ git fetch$ git checkout net$ make clean
写在前面:本节内容分为实验部分与源码部分,内容可能会有多。如果想要多了解一下源码的读者可以观察源码分析部分。
networking(hard)简介
qemu模拟设备硬件
本次实验是通过使用qemu在risc-v主板上模拟一个网卡设备称为E1000。这个网卡设备可以连接到真实的局域网中(LAN)。但是在实验中,局域网与网卡设备都是通过qemu模拟出来的。xv6(客户端)的IP地址是10.0.2.15,连接到IP地址为10.0.2.2的局域网上。当xv6使用E1000向10.0.2.2发送数据包时,qemu会将包发送给局域网中目的主机(服务端)中。
要了解e1000网卡的相关内容,可以根据实验中给出的硬件手册的相应章节进行阅读。
网络协议栈
在该实验中提供了较为简单的网络协议栈的代码实现,在后续的源码分析中会又所介绍。陈列一下相关的文件:
sysnet.c:定义了套接字结构,实现了 ...
xv6:中断与设备驱动
写在前面:本文主要是介绍了中断的原理以及驱动的设计,其中介绍了大部分的xv6的驱动代码,建议读者可以提前阅读一下相关源码,在阅读本文是可以使用GDB去调试过一遍中断的流程。对于时钟中断的代码可能有点晦涩,因为在课程中没有介绍,所有这些都是看xv6书和源码得到的结论,本文对此的介绍可能有些许问题,仅供参考。
中断简介中断对应的场景,就是硬件需要得到操作系统的关注,例如网卡收到了一个packet,网卡会生成一个中断;用户通过键盘按下了一个按键,键盘会产生一个中断。操作系统需要做的是,保存当前的工作,处理中断,处理完成之后再恢复之前的工作。
中断与系统调用的不同,同时也是驱动设计的难点
异步性(asynchronous)。当硬件生成中断时,interrupt handler与当前运行的进程在CPU上没有任何关联。但如果是系统调用的话,系统调用发生在运行进程的上下文(context)下。
并行性(concurrency)。对于中断来说,CPU和生成中断的设备是并行的在运行。网卡自己独立的处理来自网络的packet,然后在某个时间点产生中断,但是同时,CPU也在运行。所以我们在CPU ...
xv6:trap机制
简述:trap机制有三种事件会导致CPU搁置普通命令的执行,强制将控制权转交给处理该事件的特殊代码。
系统调用(system call):执行ecall指令,向内核请求并操作特定硬件资源。
异常(exception):由尝试做非法事件的指令触发,例如除零操作。
设备中断(device interrupt):当设备发出信号时提示内核需要注意,例如当磁盘完成读写请求时。
xv6使用trap(陷阱)作为这三种情况的术语。通常,代码在执行时发生 trap,之后都会被恢复,而且不需要意识到发生了什么特殊的事情(trap 是透明的)。
透明性对于中断十分重要,被中断的代码通常不会意识到会发生trap。
trap执行的顺序为:
控制权转移到内核(在内核中忽略这一步)
内核保存寄存器和其他状态(存储到内存中)
内核执行特定的处理程序代码
内核恢复保存的状态,从trap中返回
代码从发起trap的地方恢复
xv6中trap处理的四个阶段
RISC-V CPU采取硬件行动(特殊指令,如ecall)
执行汇编指令(trampoline.S)进入内核的C语言代码(kern ...
Mit6.S081:lab Multithread
实验:Lab: Multitrheading
实验开始之前需要将git分支切换到thread分支不然有些文件你是没有的
123$ git fetch$ git checkout thread$ make clean
Uthread: switching between threads (moderate)简介 本次实验是要实现用户级的线程上下文切换机制。在实验中提供了两个文件user/uthread.c和user/uthread_switch.S,在uthread.c文件中提供了一些用户级线程要使用的包,还有三个线程的代码,在这个文件中缺少了创建线程与切换上下文的代码。
任务:创建线程和保存/恢复寄存器以在线程之间切换的计划,并实现该计划。当你完成后,评分应该表明你的解决方案通过了线程测试。
当完成之后可以运行uthread,输出的结果如下所示:
123456789101112131415161718192021$ make qemu...$ uthreadthread_a startedthread_b startedthread_c startedthread_c ...