以下只是个人看了《linux内核完全注释》的一点理解,如果有错误,欢迎指正!

1 eip中保存的地址是逻辑地址、线性地址还是物理地址?

  这个应该要分情况。eip保存的是下一条要执行的指令地址,也就是说cpu是根据eip到内存中去寻找指定的内容。如果cpu工作在实模式,那么eip保存的就是物理地址;如果cpu工作在保护模式下,那么cpu在去内存寻找指定的内容之前要先将eip加上当前程序代码段的基址(通过当前cs所指向的代码段描述符获得),即获得当前程序的线性地址,如果cpu没有开启分页机制,那么这个线性地址就是实际的物理地址了;如果cpu开启了分页机制,那么就要通过线性地址查找页目录表和页表来获得实际的物理地址。所以如果cpu在保护模式下,不管有没有开启分页机制,eip保存的都是程序的逻辑地址。

2 每个进程有几个堆栈?

  每个进程都有两个堆栈,一个是工作在用户态的用户堆栈,一个是工作在内核态的内核堆栈。内核堆栈只有一页,即4k,该页的低地址保存了任务结构。

3 进程的状态是怎么变化的?

  一般发生硬件中断,程序异常,程序执行系统调用,cpu会将当前执行的程序转换为内核状态去执行中断处理程序,并且使用内核堆栈。系统调用是进程让自己主动转换为内核状态的唯一方法,而硬件中断和程序异常是cpu强制将当前进程转换为内核态去执行中断处理程序。linux0.12是通过将系统调用0x80设置为DPL为3的陷阱门,而硬件中断和程序异常都被设置为DPL为0的中断门来实现的。

4 任务是怎样被切换的?

  是通过ljmp指令实现的,如果ljmp的操作数是GDT表(全局描述符表)中某个任务的任务段描述符或者IDT表(中断描述符表)中的任务门描述符,那么cpu会切换去执行这个新任务,在切换前会将cpu中各种寄存器的状态存放到被切换出去的任务的任务段中。并把该新任务任务段中的信息恢复到cpu的各个寄存器中。

5 进程为什么会被切换?

  让进程切换有几个原因,一个是进程的时间片用完了,时间中断处理程序会在每次被调用的时候检查当前进程的时间片是否用户,如果用完了就切换当前进程;一个是进程通过调用系统调用(如pause,wait等)让出cpu,即程序为了等待资源而主动让出cpu。

6 信号处理程序是什么时候被调用的?

  每次中断处理程序(包括时钟中断,系统调用等)结束之前都会去检查当前程序的信号位图,如果某个信号位图相应的位为1,并且该信号未被阻塞,则通过调用do_signal中断程序来设置内核态堆栈(通过修改保存在堆栈中的用户态eip来指向信号处理程序)和用户态堆栈(保存调用系统调用的下一条指令地址,信号处理程序参数等),使得在系统调用返回后能马上去执行信号处理程序而不是执行调用系统调用的程序的下一条指令。

7 等待信号发生的程序是怎样被唤醒的?

  每次调度程序schedule被调用的时候,都会去检查系统中所有任务,如果该任务的状态为可中断的等待,并且该任务有未阻塞的信号到达,则将该任务的状态设置为可运行状态等待被调度。

8 每个进程的线性地址是怎么计算的?

  每个进程都有64M的地址空间,每个进程都有自己的页表,但都共用存放在物理地址为0的页目录表,每个进程在页目录表中有16项。页目录表占一个内存页,即4k,每个目录项为4B,所以目录表中对多有1024项,即系统总共可以运行64个进程。每个进程的段基址可以通过该进程的任务号nr乘以64M得到,段基址加上逻辑地址即得到进程的线性地址。

9 内核态的进程是否可以访问所有的内存空间?

  linux0.12能访问的内存最大为16M,head.s程序将4个4k的页表放在页目录表的后面,并且该四个页表占用页目录的前四项,也即内核程序最多为16M。并且将GDT表中的代码段和数据段的段基址设置为0,也就是说此时内核程序的逻辑地址和物理地址是一样的,所以内核程序都能访问16M的物理地址,即所有的内存空间。

10 处于内核态的进程是否会发生任务切换?

  不会,即使进程的时间片用完了,当然如果进程自己主动让出cpu,那也是会切换任务的。

11 进程是怎样主动让出cpu的?

  不管进程处在用户态还是内核态,只要进程调用interruptible_sleep_on或者sleep_on函数,进程就会主动让出cpu。interruptible_sleep_on和sleep_on函数都是调用__sleep_on函数,__sleep_on函数输入参数是某等待队列的头指针(这个等待队列是task_struct结构的,用于存储等待同一类资源的所有任务指针,至于要传入等待队列头指针的原因是一般程序主动让出cpu是由于所需的资源当前不可用)。值得注意的是如果等待同一资源的进程调用__sleep_on函数,表示等待该资源的等待队列的头指针会指向这个新的进程,而在__sleep_on函数中有一个task_struct结构的指针temp指向上一个等待该资源的进程,这样就形成了一个由task_struct结构组成的链表等待队列。当等待队列中某个进程获得cpu时,如果它不是等待队列的头,该进程会唤醒等待队列的头运行,而自己则继续等待,重复这一过程,直到等待队列的头是当前进程。

图1 buffer_wait等待队列

12 linux缓冲区管理的原理是什么?

  linux在内存中内核代码的结束后面放了一定大小的缓冲区(大小根据内存实际大小安排)。初始化缓冲区时,从缓冲区头和尾同时进行,缓冲区头存放缓冲块头结构,缓冲区尾存放实际的缓存块,缓冲块大小和实际磁盘块大小一样,为1K,一个缓冲区头管理一个缓冲区块。所有缓存块的头被链接成一个双向链表结构,如图2。free_list是指向空闲缓冲块链表的头,free_list的b_prev_free指向指向空闲缓冲块头链表的尾,而缓冲块头链表的尾则指向缓冲块链表的头,这样空闲缓冲块链表就形成了一个循环双链表,如图3。

图2 缓冲区初始化

图2 空闲缓冲块双向循环链表

  缓冲块头结构如下:

struct buffer_head {
char * b_data; /* pointer to data block (1024 bytes) */
unsigned long b_blocknr; /* block number */
unsigned short b_dev; /* device (0 = free) */
unsigned char b_uptodate;
unsigned char b_dirt; /* 0-clean,1-dirty */
unsigned char b_count; /* users using this block */
unsigned char b_lock; /* 0 - ok, 1 -locked */
struct task_struct * b_wait;
struct buffer_head * b_prev;
struct buffer_head * b_next;
struct buffer_head * b_prev_free;
struct buffer_head * b_next_free;
};

  b_data是缓冲块地址;b_blocknr和b_dev表示缓冲块中存放的是哪个设备的几号块;b_uptodate表示当前缓冲块是否有效,为1是有效,为0是无效;b_dir表示当前缓存块是否和磁盘块的数据一致,为1表示不一致,为0表示一致;b_count表示引用当前缓冲块的进程数;b_lock表示当前缓冲块是否被锁定(一般缓冲块与实际物理磁盘传输数据时,缓冲块是被锁定的);b_wait指向等待当前缓冲块的任务队列的头;b_prev和b_next主要用于查找缓冲块,具体见下面;b_prev_free和b_next_free用于形成空闲缓冲块双向循环链表。

  为了快速而有效地在缓冲区寻找并判断出请求的数据块已经被读入缓冲区中,将每个已经读入缓冲区的数据块加入到一个hash链表中,每个hash链表的头指针存放在hash数组中,通过b_blocknr和b_dev共同决定将数据块放到哪个hash链中,如图3所示。

图3 hash缓冲链

  从图中可以看出每个hash链都是一个双向链表,通过b_prev和b_nex来实现。如果要判断某个数据块是否在缓冲区中, 通过b_blocknr和b_dev直接找到对应的hash链表,在链表中查看是否有b_blocknr和b_dev一致的数据块就可以判断数据块是否在缓冲区中了。为了实现缓冲区满了要替换出最近最久未使用(LRU)的缓冲块,每次都将新分配的缓冲块放到free_list的最后面,而每次找空闲缓存块则从free_list最前面开始找,这样就实现LRU了。

读《linux内核完全注释》的FAQ的更多相关文章

  1. linux内核代码注释 赵炯 第三章引导启动程序

    linux内核代码注释 第三章引导启动程序 boot目录中的三个汇编代码文件   bootsect.s和setup.s采用近似intel的汇编语法,需要8086汇编器连接器as86和ld86 head ...

  2. linux内核参数注释与优化

    目录 1.linux内核参数注释 2.两种修改内核参数方法 3.内核优化参数生产配置 参数解释由网络上收集整理,常用优化参数对比了网上多个实际应用进行表格化整理,使查看更直观. 学习linux也有不少 ...

  3. 赵炯博士《Linux内核完全注释》

    赵炯:男,1963年10月5日出生,江苏苏州人,汉族. 同济大学机械工程学院机械电子教研室副教授,从事教学和科研工作. 现在主要为硕士和博士研究生开设<计算机通信技术>.<计算机控制 ...

  4. LINUX内核完全注释

    学习教材:LINUX内核完全注释,内核版本0.11,修正版V3.0 赵炯编著 参考教材:UNIX操作系统设计--M. J. Bach, programming the 80x86  --John H. ...

  5. (转)linux内核参数注释与优化

    linux内核参数注释与优化 原文:http://blog.51cto.com/yangrong/1321594 http://oldboy.blog.51.cto.com/2561410/13364 ...

  6. Linux内核通用队列的使用笔记(读linux内核设计与实现)

    Linux内核通用队列实现 Kfifo位置:kernel/kififo.c 使用需要包含头文件#include <kernel/kififo> 1.创建队列(动态创建)int kfifo_ ...

  7. Linux内核完全注释阅读笔记1:O(1)时间复杂度查找timeout定时器

    前言 一直有Linux kernel情节,之前也一直在看Linux kernel相关的书和代码,但是每次到最后又由于兴趣转变而荒废了.这次终于静下心来想把Linux内核相关的代码好好看看,算是对自己的 ...

  8. Linux内核0.11体系结构 ——《Linux内核完全注释》笔记打卡

    0 总体介绍 一个完整的操作系统主要由4部分组成:硬件.操作系统内核.操作系统服务和用户应用程序,如图0.1所示.操作系统内核程序主要用于对硬件资源的抽象和访问调度. 图0.1 操作系统组成部分 内核 ...

  9. Linux内核完全注释之编程语言和环境(一)

    as86汇编器 1.来源与对于linux的用途 as86来源minix-386开发的intel 8086.80386汇编编译程序和链接程序,他主要为linux创建16位的启动引导扇区程序boot/bo ...

随机推荐

  1. jsp页面 如何通过el表达式获取request属性值

    1. 我在一个超连接后加个参数如:      http://localhost:8080/test/testjstl.jsp?pid=001    此时在jsp页面中,获取jsp传过来的pid的参数值 ...

  2. day 2 Linux基础

    6.用户.群组和权限 1) 新建用户natasha,uid为1000,gid为555,备注信息为"master" useradd natasha usermod -u1000 na ...

  3. HTTP协议详解(转)

    转自:http://blog.csdn.net/gueter/archive/2007/03/08/1524447.aspx Author :Jeffrey 引言 HTTP是一个属于应用层的面向对象的 ...

  4. LL LR SLR LALR 傻傻分不清

    [转] 一:LR(0),SLR(1),规范LR(1),LALR(1)的关系     首先LL(1)分析法是自上而下的分析法.LR(0),LR(1),SLR(1),LALR(1)是自下而上的分析法.   ...

  5. Android操作HTTP实现与服务器通信(转)

    Android操作HTTP实现与服务器通信   本示例以Servlet为例,演示Android与Servlet的通信. 众所周知,Android与服务器通信通常采用HTTP通信方式和Socket通信方 ...

  6. HTML和CSS经典布局5

    如下图: 需求: 1. 如图 2. 可以从body标签开始. 3. 页面内容高度设置高点,把窗口的滚动条显示出来,但是busy indicator不滚动. <!DOCTYPE html> ...

  7. java 反编译利器JD-JUI

    下载地址: http://download.csdn.net/download/suixingbugai/4145221

  8. C# Azure 存储-队列

    1.前言 本篇文章是根据Azure的官网document总结,如果想直接跳过本文章,可以点击下面的链接进入. https://www.azure.cn/zh-cn/documentation/arti ...

  9. org.artofsolving.jodconverter.office.OfficeException: failed to start and connect

    org.artofsolving.jodconverter.office.OfficeException: failed to start and connect docviewer 调用 openo ...

  10. Oracle 创建普通用户,并赋予权限

    采用sys or system / manager as sysdba; 连接数据库. 创建普通用户konglin: create user konglin identified by pwd_ora ...