首页 > 其他分享 >操作系统实战45讲00057

操作系统实战45讲00057

时间:2022-12-30 11:45:48浏览次数:76  
标签:架构 操作系统 int 45 内核 节课 设备 00057 函数

你好,我是编辑宇新。

春节将至,先给你拜个早年:愿你2022年工期变长,需求变少,技术水平更加硬核。

距离我们专栏更新结束已经过去了不少时间,给坚持学习的你点个赞。学习操作系统是一个长期投资,需要持之以恒,才能见效。无论你是二刷、三刷的朋友,还是刚买课的新同学,都建议你充分利用留言区,给自己的学习加个增益buff。这种学习讨论的氛围,也会激励你持续学习。

今天这期加餐,我们整理了课程里的思考题答案,一次性发布出来,供你对照参考,查漏补缺。

建议你一定要先自己学习理解,动脑思考、动手训练,有余力还可以看看其他小伙伴的解题思路,之后再来对答案。

第1节课

Q:为了实现C语言中函数的调用和返回功能,CPU实现了函数调用和返回指令,即上图汇编代码中的“call”,“ret”指令,请你思考一下:call和ret指令在逻辑上执行的操作是怎样的呢?

A:一般函数调用的情况下call和ret指令在逻辑上执行的操作如下:

1.将call指令的下一条指令的地址压入栈中;
2.将call指令数据中的地址送入IP寄存器中(指令指针寄存器),该地址就是被调用函数的地址;
3.由于IP寄存器地址设置成为被调用函数的地址,CPU自然跳转到被调用函数处开始执行指令;
4.在被调用函数的最后都有一条ret指令,当CPU执行到ret指令时,就从栈中弹出一个数据到IP寄存器,而这个数据通常是先前执行call指令的下一条指令的地址,即实现了函数返回功能。

第2节课

Q:以上printf函数定义,其中有个形式参数很奇怪,请你思考下:为什么是“…”形式参数,这个形式参数有什么作用?

 

A:在C语言中经常使用printf(“%s :%d”,“number is :”,20);printf(“%x :%d”,0x10,20);printf(“%x,%x :%d”,0xba,0xff,20);可以看出,这些printf函数参数个数都不同,因为C语言的特性支持变参函数。而“…”表示支持0个和多个参数,C语言是通过调用者传递参数的,刚好支持这种变参函数。

第3节课

Q:其实我们的内核架构不是我们首创的,它是属于微内核、宏内核之外的第三种架构,请问这是什么架构?

A:我们的内核架构是混合内核架构,是介于微、宏架构之间的一种架构,这种架构保证了宏架构的高性能又兼顾了微架构的可移植、可扩展性。

第4节课

Q:Windows NT内核属于哪种架构类型?

A:Windows NT内核架构其实既不属于传统的宏内核架构,也不是新的微内核架构,说NT是微内核架构是错误的,NT这种内核架构其实是宏内核的变种——混合内核。

第5节课

Q:请问实模式下能寻址多大的内存空间?

A:由于实模式下访问内存的地址是这样产生的:16位段寄存器左移4位,加一个16位通用寄存器,最后形成了20位地址,所以只能访问1MB大的内存空间。

第6节课

Q:分页模式下,操作系统是如何对应用程序的地址空间进行隔离的?

A:操作系统会给每个应用程序都配置独立的一套页表数据。应用程序运行时,就让CR3寄存器指向该应用程序的页表数据。运行下一个应用程序时,则会执行同样的操作。

第7节课

Q:请你思考一下,如何写出让CPU跑得更快的代码?由于Cache比内存快几个数量级,所以这个问题也可以转换成:如何写出提高Cache命中率的代码?

A:第一,定义变量时,尽量让其地址与Cache行大小对齐。

int a __attribute__((aligned (64))); 
int b __attribute__((aligned (64))); 

第二,操作数据时的顺序,尽量和数据在内存中布局顺序保持一致。

int arr[M][M];
for(int i = 0; i < M; i++) {
    for(int k = 0; k < M; k++) {
        arr[i][k] = 0;
    }
}
//而非这样
for(int i = 0; i < M; i++) {
    for(int k = 0; k < M; k++) {
        arr[k][i] = 0;
    }
}

第三,尽量少用全局变量。

第8节课

Q:请用代码展示一下自旋锁或者信号量,可能的使用形式是什么样的?

A:最常规的形式是在设计共享数据结构时,在其中包含自旋锁或者信号量。

如下所示:

typedef struct s_DATA
{   
    spinlock_t d_lock;
    sem_t d_sem;
    int a;
    int b;
    long state;
}data_t;
data_t da;
do_da_write(data_t* d)
{
    x86_spin_lock(&d->d_lock);
    d->a = 0;
    d->b = 1;
    d->state = 2;
    x86_spin_unlock(&d->d_lock);
}
do_da_sem_write(data_t* d)
{
    krlsem_down(&d->d_sem);
    d->a = 20;
    d->b = 10;
    d->state = 4;
    krlsem_up(&d->d_sem);
}
do_da_write(&da);
do_da_sem_write(&da);

第9节课

Q:请试着回答:上述Linux的读写锁,支持多少个进程并发读取共享数据?这样的读写锁有什么不足?

A:第一个问题,根据上述描述,读写锁本质上就是一个计数器。锁变量的初始值为0x01000000,即表示最多可以有0x01000000个进程同时获取读锁。

第二个问题,读写锁的不足是,如果一直有很多读取数据的进程占有读锁,因而可能导致修改数据的进程饥饿的情况。操作系统会加以控制,让修改数据的进程优先得锁。

第10节课

Q:请问,我们为什么要把虚拟硬盘格式化成ext4文件系统格式呢?

A:有两点原因。第一,GRUB在加载系统映像文件时,能够识别ext4文件系统格式;二,我们在Linux下生成系统映像文件时,要复制到虚拟硬盘中去,所以这个文件系统格式必须被Linux所识别,那么选ext4就最合适。

第11节课

Q:请问GRUB头中为什么需要_entry标号和_start标号的地址?

A:这是GRUB规定的。GRUB正是通过_entry标号和_start标号的地址,控制内核文件被加载到什么内存地址,又应该从什么内存地址开始运行。

第12节课

Q:请你想一下,init_bstartparm()函数中的init_mem820()函数,这个函数到底干了什么?

A:init_mem820()函数是把e820map_t结构数组复制到内核文件之后的内存空间,并且重新填写了机器信息结构。

它的代码如下:

void init_meme820(machbstart_t *mbsp)
{
    //源e820map_t结构数组地址
    e820map_t *semp = (e820map_t *)((u32_t)(mbsp->mb_e820padr));
    //e820map_t结构数组元素个数
    u64_t senr = mbsp->mb_e820nr;
     //获取下一段空闲内存空间的首地址,即e820map_t结构数组的新地址 
    e820map_t *demp = (e820map_t *)((u32_t)(mbsp->mb_nextwtpadr));
    //检查地址空间冲突
    if (1 > move_krlimg(mbsp, (u64_t)((u32_t)demp), (senr * (sizeof(e820map_t)))))
    {
        kerror("move_krlimg err");
    }
    //复制
    m2mcopy(semp, demp, (sint_t)(senr * (sizeof(e820map_t))));
    //并重新填写了对应的机器信息结构字段
    mbsp->mb_e820padr = (u64_t)((u32_t)(demp));
    mbsp->mb_e820sz = senr * (sizeof(e820map_t));
    mbsp->mb_nextwtpadr = P4K_ALIGN((u32_t)(demp) + (u32_t)(senr * (sizeof(e820map_t))));
    mbsp->mb_kalldendpadr = mbsp->mb_e820padr + mbsp->mb_e820sz;
    return;
}

第13节课

Q:请你画出Cosmos硬件抽象层的函数调用关系图。

A:Cosmos硬件抽象层的函数调用关系图如下。

第14节课

Q:为什么要用C代码mkpiggy程序生成piggy.S文件,并包含vmlinux.bin.gz文件呢?

A:因为mkpiggy程序在读取vmlinux.bin.gz文件,知道了其长度等信息,它就会把这些信息保存在piggy.S文件相关的字段中。在解压vmlinux.bin.gz文件时,解压的代码需要用到这些信息。

第15节课

Q:你能指出上文中Linux初始化流程里,主要函数都被链接到哪些对应的二进制文件中了?

A:它们的链接结构如下。

1._start、main函数链接在setup.elf文件中,而setup.elf文件生成了setup.bin。

2.startup_32、startup_64、extract_kernel链接在linux/arch/x86/boot/compressed目录下的vmlinux文件中,而这个文件生成了vmlinux.bin。

3.Linux内核的startup_64、x86_64_start_kernel、start_kernel、arch_call_rest_init、rest_init、kernel_init、try_to_run_init_process、run_init_process函数链接在顶层linux目录下的vmlinux中。这是一个elf格式的文件,由objcoopy去除符号信息后用压缩工具压缩,包含到piggy.S中,从而形成了piggy.o,最终和其它文件一起生成了vmlinux.bin。

第16节课

Q:我们为什么要以 2 的(0~52)次方为页面数来组织页面呢?

A:以2的(0~52)次方为页面数来组织页面,是为了每组连续的页面能对半分割。对半分割是为了保证连续的页面空间最大化,同时保证在下一次释放时,能最大可能地合并一个整体,这么做的目的只有一个:在满足最小、最大页面请求时,保证内存碎片的最小化。

第17节课

Q:请问在 4GB 的物理内存的情况下,msadsc_t 结构实例变量本身占用多大的内存空间?

A:4GB有1M个页面,那就对应1M个msadsc_t结构,每个msadsc_t结构为40个字节,所以占用40MB的内存空间。

第18节课

Q:在内存页面分配过程中,是怎样尽可能保证内存页面连续的呢?

A:因为分配内存页面一开始就是连续的,然后在分配时始终以2的幂次分隔,所以能保证内存页面的最大连续性。

第19节课

Q:为什么我们在分配内存对象大小时,要按照Cache行大小的倍数分配呢?

A:因为这使得我们分配的内存对象的地址空间是和Cache行对齐的,那么这个内存对象中的数据就极有可能被Cache命中,从而大大提升程序的性能。

第20节课

Q:请问内核虚拟地址空间为什么有一个 0xFFFF800000000000~0xFFFF800400000000 的线性映射区呢?

A:内核的线性映射区0xFFFF800000000000~0xFFFF800400000000,会映射到物理地址空间的0~~0x400000000。因为内核本身运行在虚拟地址空间,本身使用虚拟地址,但是它又必须访问物理内存,所以有了这个线性映射区,就可以把这个区域的物理地址转换成虚拟地址,也可以直接把虚拟地址转换成物理地址。

另外,因为它们之间就是一个常数:0xFFFF800000000000。所以,内核就可以很方便地操作自身数据结构和设备寄存器。这个设备寄存器是物理地址,内核很方便就能转换为虚拟地址,然后通过这个虚拟地址访问设备寄存器。

第21节课

Q:请问,x86 CPU 的缺页异常,是第几号异常?缺页的地址保存在哪个寄存器中?

A:x86 CPU的缺页异常,是14号异常。缺页的地址保存在x86 CPU的CR2寄存器中。

第22节课

Q:在默认配置下,Linux伙伴系统能分配多大的连续物理内存?

A:Linux伙伴系统能分配多大的连续物理内存,取决于MAX_ORDER。MAX_ORDER的值默认为11,因为是free_area数组的下标,所以要MAX_ORDER-1 = 10, 结果就是2 << 10 = 1024,而1024个连续的页面(一个页面4KB)是4MB,即Linux伙伴系统能分配多大的连续物理内存是4MB。

第23节课

Q:Linux的SLAB,使用kmalloc函数能分配多大的内存对象呢?

A:kmalloc函数能分配32MB的内存对象。

第24节课

Q:各个进程是如何共享同一份内核代码和数据的?

A:只需要将每个进程的上半部分虚拟地址空间(0xFFFF800000000000~0xFFFFFFFFFFFFFFFF)的MMU页表设为相同的映射关系就行了,这样每个进程都可以共享内核的代码的数据,但是又不能读取和修改这部分地址空间中的数据,因为权限不够。

第25节课

Q:请问当调度器函数调度到一个新建进程时,为何要进入 retnfrom_first_sched 函数呢?

A:因为新建的进程内核中只有CPU默认的寄存器状态,没有从内核其它任何位置调用进入krlschedul函数。因此没有调用krlschedul函数的调用路径,所以无从返回,只能通过retnfrom_first_sched函数,强制初始化CPU寄存器状态,从而让进程开始运行。

第26节课

Q:我们让进程进入等待状态后,进程会立马停止运行吗?

A:进程不会立马停止运行,因为在调用krlsched_wait函数后,进程的上下文并没有切换。需要在krlsched_wait函数的外层,通过调用krlschedul函数进行进程调度,才能让该进程停止运行,进入等待状态。

第27节课

Q:想一想,Linux 进程的优先级和 Linux 调度类的优先级是一回事儿吗?

A:不是一回事儿。一个调度类管理着同一类的多个进程,而进程的优先级是该调度类下的各个进程间的优先级。

第28节课

Q:请你写出一个用来访问设备的接口函数,或者想一下访问一个设备需要什么参数。

A:比如打开一个设备的接口函数,如下所示:

int open(devid_t *devid, uint_t flgs);

必须至少要有设备的devid参数。里面要包含设备的类型和设备号,这样才能找到一个具体的设备。

第29节课

Q:请你写出帮驱动程序开发者自动分配设备ID接口函数。

A:很明显,这需要驱动程序提供一个设备类型,然后到设备表中搜索该设备类型还没有占用的设备ID,最后返回这个设备ID。代码如下所示:

drvstus_t krlnew_devid(devid_t *devid)
{
    device_t *findevp;
    drvstus_t rets = DFCERRSTUS;
    cpuflg_t cpufg;
    list_h_t *lstp;
    devtable_t *dtbp = &osdevtable;//获取设备表
    uint_t devmty = devid->dev_mtype;
    uint_t devidnr = 0;
    if (devmty >= DEVICE_MAX)
    {
        return DFCERRSTUS;
    }

    krlspinlock_cli(&dtbp->devt_lock, &cpufg);
    if (devmty != dtbp->devt_devclsl[devmty].dtl_type)
    {
        rets = DFCERRSTUS;
        goto return_step;
    }
    //检查这个设备类型链表是不是为空
    if (list_is_empty(&dtbp->devt_devclsl[devmty].dtl_list) == TRUE)
    {
        rets = DFCOKSTUS;
        devid->dev_nr = 0;
        goto return_step;
    }
    //扫描该设备类型链表下的所有设备
    list_for_each(lstp, &dtbp->devt_devclsl[devmty].dtl_list)
    {
        findevp = list_entry(lstp, device_t, dev_intbllst);
        if (findevp->dev_id.dev_nr > devidnr)
        {
            //获取最大的设备号
            devidnr = findevp->dev_id.dev_nr;
        }
    }
    //新的设备号等于最大设备号加一
    devid->dev_nr = devidnr++;
    rets = DFCOKSTUS;
return_step:
    krlspinunlock_sti(&dtbp->devt_lock, &cpufg);
    return rets;
}

第30节课

Q:请你想一想,为什么没有 systick 设备这样周期性的产生中断,进程就有可能霸占 CPU 呢?

A:如果一个应用程序,它不调用任何系统接口,也不退出系统,就在主函数中执行一个死循环,这样这个进程一旦运行,内核将再也没有办法从应用手中夺回CPU,其代码如下:

void main()
{
    for(;;);
    return;
}

第31节课

Q:为什么无论是我们加载miscdrv.ko内核模块,还是运行App测试,都要在前面加上sudo呢?

A:Linux系统的安全是基于用户类型的,并且是多用户的系统,所以有些影响系统的操作,必须要root用户才能完成。比如你加载一个内核模块,这个内核模块是不是友好的?会不会干坏事?这需要管理员root用户评估;应用程序访问设备,同样是系统特权操作,也需要root用户,而sudo命令就是暂时让应用以root用户运行。

第32节课

Q:请问,我们文件系统的储存单位为什么要自定义一个逻辑储存块?

A:有两点考量:一是储存设备都按块为单位储存;二是为了文件系统代码的可移植性和可扩展性。因为储存设备的储存块大小各不相同,有512B、1KB、2KB、4KB,我们自己定义一个逻辑储存块,就能很好地适应不同的储存设备。

第33节课

Q:请问,建立文件系统的超级块、位图、根目录的三大函数的调用顺序可以随意调换吗,原因是什么?

A:不能随意调换,因为建立位图要依赖于超级块,而建立根目录时需要依赖于位图,所以必须是先调用建立超级块的函数,然后调用建立位图的函数,最后调用建立根目录的函数。

第34节课

Q:请你想一想,我们这个简单的、小的,却五脏俱全的文件系统有哪些限制?

A:我们这个文件系统有如下限制:

  1. 不能创建目录,所有文件都在根目录“/”下,即文件路径名都是这样的形式:“/file”、“/file1”、“/file2”等;
  2. 每个文件最多只能分配一个储存块(4KB大小);
  3. 暂不支持文件随机读写,一旦发生读写操作,我们的文件系统会把一个文件的全部数据都返回给请求者,或者更新该文件的全部数据。

第35节课

Q:请说一说 super_block,dentry,inode 这三个数据结构 ,一定要在储存设备上对应存在吗?

A:不一定要在储存设备上对应存在,具体的文件系统可以有自己的实现,但是在运行时刻必须要能转换成内存中对应的super_block,dentry,inode这三大数据结构。转换方法由具体文件系统实现。

第36节课

Q:我们这节课从宏观的角度分析了网络数据的运转,但是在内核中网络数据包怎么运转的呢?请你简单描述这个过程。

A:内核网络数据包处理流程如下:

  1. 网卡驱动初始化
  2. 中断注册
  3. 重要结构体初始化
  4. 网络收发包

 

第37节课

Q:我们已经了解到了操作系统内核和网络协议栈的关系,可是网络协议栈真的一定只能放在内核态实现么?

A:我们发现传统的收发方式有一些弊端,比如:内核态用户态切换会引入Cache Miss、流水线失效、硬中断、锁、额外的拷贝等等额外开销。这些开销在C10K的并发规模可能无法体现出来,可是一旦到了C10M的规模,这些开销就不容小视了,于是DPDK这种用户态网络栈就应运而生了。

第38节课

Q:请思考一下,我们目前的互联网架构属于中心化架构还是去中心化架构呢?你觉得未来的发展趋势又是如何?

A:早期的传统互联网架构下,我们如果要配置交换机,一般都是直接用配置线连接交换机,然后命令行配置的,但出现什么问题可能就要跑到机房了。而且那个年代,中小型运营商也比较多,且分布式技术不够成熟。所以,诞生了如OSPF、BGP、ISIS之类的分布式、自组织的动态路由协议。

而随着分布式技术成熟,以及电信、互联网巨头逐渐聚集,我们现在逐渐演进到了以Google B4为代表的中心化架构的SDN上了,这也就是为什么万维网之父Tim Berners-Lee爵士会表示对今天的中心化Web 非常不满,却还是搞出了开源的去中心化平台 Solid项目的原因。

至于未来,个人认为随着以大数据、区块链为代表的去中心化架构逐渐成熟,也许互联网的基础架构会回归去中心化。当然为了实现这个目标,就需要我们大家一起努力了。

第39节课

Q:套接字也是一种进程间通信机制,它和其他通信机制有什么不同?

A:它可用于不同机器间的进程通信。

第40节课

Q:我们了解的 TCP 三次握手,发生在 socket 的哪几个函数中呢?

A:第一次握手:客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;

第二次握手:服务器监听到连接请求,即收到SYN J包,就会调用accept函数接收请求,向客户端发送SYN K ,接着ACK J+1,这时accept进入阻塞状态;

第三次握手:客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回。至此三次握手完毕,连接建立。

第41节课

Q:请问 int 指令后面的常数能不能大于255,为什么?

A:int 指令后面的常数不能大于255,因为int指令会经过中断门,后面的常数就是中断门的索引,而我们中断门最多256(0~255)个,所以不能大于255。

第42节课

Q:请说说syscall指令和int指令的区别是什么?

A:syscall指令不需要经过中断门,执行syscall指令后的进入内核的入口地址,是内核在初始化时写入到特殊寄存器。这个寄存器应用程序不能访问,处理器在硬件层还对syscall指令执行逻辑做了一定的优化,而int要经过中断门进入到内核,做权限检查又还要读取内存,这会导致性能下降。

第43节课

Q:有了KVM作为虚拟化的基石之后,如果让你从零开始,设计一款像各大云厂商IAAS平台一样的虚拟化平台,还需要考虑哪些问题呢?

A:如果只是在一台物理机上开启多个虚拟机,KVM确实已经做的很棒了,但是如果我们扩展到多个机架、多个机房,问题就变得更加复杂了。

我们除了要考虑之前讲过的网络问题,还需要考虑分布式环境下的计算、存储、消息传输、状态同步、动态迁移、扩缩容、镜像、身份认证、编排与调度、UI管理面板等很多问题。

当然,业界也有一些开源解决方案,比如大名鼎鼎的OpenStack,不过笔者觉得OpenStack由于设计实现得比较早,所以存在集群规模有限,部署、维护、二次开发复杂度高,历史包袱重等问题。和多位架构师沟通交流之后,我们正在尝试重新设计并实现一套更现代化的、轻量级的、IAAS云平台,感兴趣的同学可以加入课程群多多交流。课程交流群点这里,按加群提示操作后加入。

第44节课

Q:在我们启动容器后,一旦容器退出,容器可写层的所有内容都会被删除。那么,如果用户需要持久化容器里的部分数据该怎么办呢?

A:可以通过实现volume(数据卷),在容器文件系统里创建挂载点,把宿主机文件目录挂载到容器挂载点,启动过程中读取数据卷。

第45节课

Q:除了 ARM 指令集,如果想开发一款 CPU,我们还有更好的 RISC 指令集可选么?

A:RISC-V是2010年加州大学柏克莱分校创建的开源指令集架构。由于这个指令集是完全开放,允许任何人用于任何目的而设计,还不需要付高昂的专利费,所以开源之后IBM、高通、恩智浦、甲骨文、华为、阿里等知名公司也纷纷加入基金会,并且投入大量资源来进行研发与优化。由此可见,RISC-V是一个非常有潜力的项目,我们也会在后续课程结束后,发起Cosmos配套的开源芯片研发项目,感兴趣的同学可以加入课程群一起多多交流。

第45节课

Q:请问,ARMv8有多少特权级?每个特权级有什么作用?

A:ARMv8,有4个特权级,E0~E3, E0运行APP,E1运行OS, E2运行虚拟机监控软件,E3运行安全监视软件。

到这里,思考题答案公布完毕,同学们学习加油呀!

标签:架构,操作系统,int,45,内核,节课,设备,00057,函数
From: https://www.cnblogs.com/gongxianjin/p/17014503.html

相关文章

  • 操作系统实战45讲00032
    你好,我是LMOS。在上一课中,我们实现了建立设备的接口,这相当于制定了部门的相关法规,只要遵守这些法规就能建立一个部门。当然,建立了一个部门,是为了干活的,吃空饷可不行。其......
  • 操作系统实战45讲00034
    你好,我是LMOS。你有没有想过,蜜蜂把劳动成果变成蜜糖存放在蜂巢中,人类把劳动成果量化成财富存放在银行,但一个进程的劳动成果放在哪里呢?看到这里,你可能有疑问,进程有劳动成......
  • 操作系统实战45讲00035
    你好,我是LMOS。上一节课中,我们已经设计好了文件系统数据结构,相当于建好了仓库的基本结构。今天,我将和你一起探索仓库的划分,即什么地方存放仓库的管理信息,什么地方存放进......
  • 操作系统实战45讲00036
    你好,我是LMOS。我们在上一节课中,已经建立了仓库,并对仓库进行了划分,就是文件系统的格式化。有了仓库就需要往里面存取东西,对于我们的仓库来说,就是存取应用程序的文件。所......
  • 操作系统实战45讲00037
    你好,我是LMOS。在前面的课程中,我们已经实现了Cosmos下的文件系统rfs,相信你已经感受到了一个文件系统是如何管理文件的。今天我们一起来瞧一瞧Linux是如何管理文件,也验证一......
  • 操作系统实战45讲00038
    你好,我是LMOS。从这节课起,我们就要开始学习网络篇的内容了。网络是一个极其宏大的知识结构,我会通过五节课带你了解计算机网络的关键内容。具体我们是这样安排的。作为网......
  • 操作系统实战45讲00039
    你好,我是LMOS。上节课我们对一次请求到响应的过程积累了一些宏观认识,相信你已经对整个网络架构有了一个整体蓝图。这节课,让我们来仔细研究一下网络数据是如何在内核中流......
  • 操作系统实战45讲00040
    你好,我是LMOS。上节课我们学习了单机状态下网络数据在内核中流转的全过程,并且带你一起梳理了网络栈移植的关键步骤。这节课我会带你看看,现实世界中网络请求是如何穿过重......
  • 操作系统实战45讲00023
    你好,我是LMOS。今天,我们继续研究操作系统如何实现虚拟内存。在上节课,我们已经建立了虚拟内存的初始流程,这节课我们来实现虚拟内存的核心功能:写出分配、释放虚拟地址空间的......
  • 操作系统实战45讲00025
    你好,我是LMOS。上节课我们学习了伙伴系统,了解了它是怎样管理物理内存页面的。那么你自然会想到这个问题:Linux系统中,比页更小的内存对象要怎样分配呢?带着这个问题,我们来一......