xv6是一个支持多处理器的Unix-like操作系统,

近日阅读源码时发现xv6在记录当前CPU和进程状态时非常tricky

首先,上代码:

 extern struct cpu cpus[NCPU];
 extern int ncpu;

 // Per-CPU variables, holding pointers to the
 // current cpu and to the current process.
 // The asm suffix tells gcc to use "%gs:0" to refer to cpu
 // and "%gs:4" to refer to proc.  seginit sets up the
 // %gs segment register so that %gs refers to the memory
 // holding those two variables in the local cpu's struct cpu.
 // This is similar to how thread-local variables are implemented
 // in thread libraries such as Linux pthreads.
 extern struct cpu *cpu asm("%gs:0");       // &cpus[cpunum()]
 extern struct proc *proc asm("%gs:4");     // cpus[cpunum()].proc

其中struct cpu是一个用来保存每个cpu运行状态的结构体,

代码第一行定义了结构体数组cpus[NCPU],NCPU对应cpu的总数(最大为8),也就是说cpus用来存储所有cpu的运行状态

那么问题来了:上面的内核代码是运行于每个cpu之中的,那每个cpu如何知道自身的当前运行状态呢?

对于这个问题,我们可以通过lapic获取cpu自身编号,再利用编号对cpus寻址即可,

也就是说,对于任意一个cpu,自身状态的存储位置可以这样获得: struct cpu *c = &cpus[cpunum()];

然而,第二个问题来了:我们不可能每次引用cpu自身状态时都通过lapic获取编号啊,能不能弄一个全局变量把状态位置一次性存储下来呢?

像是这样, struct cpu *cpu; //全局变量,存储cpu自身状态 ,然后在初始化代码中 cpu = c ;

对于记录每个cpu正在运行的进程也有这样的问题,能不能写成: struct proc *proc; //全局变量,存储当前cpu正在运行的进程状态

那么,第三个问题来了:每个cpu是独立并行的,在每个cpu上运行的内核代码都是一样的,页表也一样,

这意味着全局变量cpu和proc的地址也是一样的,这样便不可以用来区分不同cpu的状态了。

因此,我们需要一种方法,可以让我们在每个cpu中都用同一个符号记录状态,但这些符号却是映射到不同的地址。

既然页表一样,我们自然不能用一个绝对的数值来寻址啦,仔细想想,页表之上有什么?页表之上,还有段表啊。

所以我们需要用segment register来寻址,只要我们在建立段表时把该段都映射到不同的内存区域不就可以了,所以我们有了以下声明:

 extern struct cpu *cpu asm("%gs:0");       // &cpus[cpunum()]
 extern struct proc *proc asm("%gs:4");     // cpus[cpunum()].proc

我们用gs作为段寄存器,cpu指向[%gs],proc指向[%gs+4],

其中为什么开头要用extern呢?我问过某大神,他说是因为gs段是在外部建立的,相当于外部定义的。。。

OK,最后一个问题来了,gs段应该指向哪,才能确保每个cpu的gs段都位于不同的区域?

最直观的想法当然是指向对应的cpus[num]内部啦,所以在struct cpu尾部增加两个域:

 struct cpu{
    ........  //cpu状态

    // Cpu-local storage variables; see below
   struct cpu *cpu;
   struct proc *proc;           // The currently-running process.
 }

然后在建立段表时,增加gs段,并映射至尾部这两个域:

   c = &cpus[cpunum()];

   ......... //建立其他段

   // 建立gs段,共两个域(存储cpu和proc地址),起始地址为&c->cpu
   c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, , );

   //加载gdt
   lgdt(c->gdt, sizeof(c->gdt));
   //加载gs
   loadgs(SEG_KCPU << );

   // 把当前cpu和proc状态的地址赋给cpu和proc全局变量
   //而cpu变量实质为%gs:0, proc变量实质为%gs:4
   cpu = c;
   proc = ;

其实在这里cpu和proc变量跟线程局部存储的性质差不多,每个处理器都可以引用同一个变量,但这些变量都对应不同的存储区域。

有可能这种实现技巧跟TLS(线程局部存储)差不多,有空研究下TLS的实现看看是不是。

xv6中存储cpu和进程信息的技巧的更多相关文章

  1. 从零自学Java-3.在程序中存储和修改变量信息

    1.创建变量: 2.使用不同类型的变量: 3.在变量中存储值: 4.在数学表达式中使用变量: 5.把一个变量的值赋给另一个变量: 6.递增/递减变量的值. 程序Variable:使用不同类型的变量并赋 ...

  2. MySQL 调优基础(一) CPU与进程

    一般而言,MySQL 的调优可以分为两个层面,一个是在MySQL层面上进行的调优,比如SQL改写,索引的添加,MySQL各种参数的配置:另一个层面是从操作系统的层面和硬件的层面来进行调优.操作系统的层 ...

  3. linux中使用top获取进程的资源占用信息

    在linux中使用top获取进程的资源占用信息: Cpu(s):  1.0%us,  0.0%sy,  0.0%ni, 98.3%id,  0.7%wa,  0.0%hi,  0.0%si,  0.0 ...

  4. 【Linux】 linux中的进程信息相关的一些内容

    _ linux进程信息 ■ top top命令用于动态地查看系统的进程和其他一些资源的信息.开启top的时候可以加上-t <sec>来设置top更新的频率高低.进入top界面之后,可以输入 ...

  5. Android中获取系统内存信息以及进程信息-----ActivityManager的使用(一)

    本节内容主要是讲解ActivityManager的使用,通过ActivityManager我们可以获得系统里正在运行的activities,包括 进程(Process)等.应用程序/包.服务(Serv ...

  6. 在Linux中通过Top运行进程查找最高内存和CPU使用率

    按内存使用情况查找前15个进程,在批处理模式下为"top" 使用top命令查看有关当前状态,系统使用情况的更详细信息:正常运行时间,负载平均值和进程总数. 分类:Linux命令操作 ...

  7. C#获取CPU占用率、内存占用、磁盘占用、进程信息

    代码: using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading ...

  8. JAVA 在程序中存储和修改信息

    1.语句和表达式 计算机程序是一组告诉计算机什么的指令,每一个指令称为语句. 2.指定变量类型 变量名.变量存储的信息类型 整型int(-2.14*109~2.14*109).浮点型float(38位 ...

  9. Linux中查看CPU信息【转】

    [转自]:http://blog.chinaunix.net/uid-23622436-id-3311579.html cat /proc/cpuinfo中的信息 processor       逻辑 ...

随机推荐

  1. 数据结构与算法分析——C语言描述

    P1.1 选择问题,选择出第K大的数,并画出N为不同值的运行时间,K=N/2 毕业两年半,重写排序,感觉良好.代码使用冒泡排序,库函数clock计算大致运行时间. // P1_1.cpp : Defi ...

  2. N2N 对等VPN网络

    n2n是一个二层的虚拟专网,允许用户开发网络中典型的P2P应用而不是在应用层开发.这就意味着用户可以透明的得到本地地址,只要新的IP地址在一个子网内,无论两台机器处于什么位置都能够ping通对方. N ...

  3. .\Obj\uCOSDemo.axf: Error: L6218E: Undefined symbol LCD_Fast_DrawPoint (refe

    这个问题是 没有定义此函数 解决方法是 定义并声明一下 这个函数!!!

  4. 基于TCP/IP的Matlab Modbus与M340 PLC通讯

    本人原创,代码拿出来供大家交流学习经验,勿作他用. 废话不多说,代码直接上. 1.创建链接 function link = connect_create(client_addr,port) %**** ...

  5. dreamvc框架(一)ioc容器的集成

    我的dreamvc框架最终写得差点儿相同了,借鉴了非常多开源框架,SpringMVC.Struts2等,眼下放在github上面.地址请猛戳我 写得差点儿相同了,是要写一个总结,把自己当时的思路记录下 ...

  6. 如何更改应用在app store的名称

    如何修改应用在app store 的名称,我修改了plist里面的bundle display name,现在安装后在设备上会显示修改后的名称,可是app store里面还是原来的名称. 何把app ...

  7. PHP面向对象之旅:抽象类继承抽象类(转)

    可以理解为对抽象类的扩展 抽象类继承另外一个抽象类时,不用重写其中的抽象方法.抽象类中,不能重写抽象父类的抽象方法.这样的用法,可以理解为对抽象类的扩展. 下面的例子,演示了一个抽象类继承自另外一个抽 ...

  8. 编译安装GCC 5.2.0

    https://blog.atime.me/note/install-gcc-5.2.0-from-source.html 记录编译GCC 5.2.0时遇到的问题和解决方法,以备日后查询. 平时使用的 ...

  9. 粗谈pcap_next_ex()

      pcap_next_ex(pcap_t* p,struct pcap_pkthdr** pkt_header,const u_char** pkt_data) 功能: 从interface或离线记 ...

  10. 在Ubuntu 12.10 上安装部署Openstack

    OpenStack系统有几个关键的项目,它们能够独立地安装但是能够在你的云计算中共同工作.这些项目包括:OpenStack Compute,OpenStack Object Storage,OpenS ...