Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1078654
  • 博文数量: 104
  • 博客积分: 3715
  • 博客等级: 中校
  • 技术积分: 1868
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-30 08:38
文章分类

全部博文(104)

文章存档

2013年(1)

2012年(9)

2011年(41)

2010年(3)

2009年(3)

2008年(47)

分类: 系统运维

2011-01-19 10:26:27

    下面,我们继续看Minix3进程的实现。本文中主要介绍的是Minix3内核定义的几个重要的全局数据结构。

    记住,minix3系统是由若干个相互独立的程序组成的,程序之间仅仅通过消息进行交互。在阅读OSDI时
发现,minix3用了很多c语言的"标准"trick,呵呵,到读代码时,还不定多痛苦呢。这本书的2.6节开始时,
介绍了很多minix通用的头文件及其定义,从这里可以看出,minix真的还是一个实验用的OS,离实际使用(
比如linux)还有很大一段距离。

    Quota: "get it right once at one place, then forget the details elsewhere"

* 进程表
    首先,我们来看一下Minix3进程表的定义。Minix3进程表定义在文件src/kernel/proc.h中。在进程表中,
记录了每个进程的信息,包括寄存器、标志、调度优先级、内存映射、计费、IPC信息等等。这里面定义的东西
是内核最底层的部分之一,在进程切换的时候,某些汇编代码也会访问进程表中的一些字段,在
arch/xxx/sconst.h中,定义了汇编需要访问字段的偏移量。所以,在修改进程表定义的时候,也应该修改对应
的sconst.h中的偏移量。
    所谓的进程表,其实就是struct proc结构体构成的一个数组,数组的大小是NR_TASK+NR_PROCS。NR_TASK
是内核空间的任务数目,目前定义为4,包括kernel,idle,clock,system;NR_PROCS是用户空间最多的进程
数目,目前定义为256。对于单用户的小系统应该够了,对于多用户系统或者服务器系统,可能还需要增加这个
数值。进程表中每个表项的结构是struct proc,这是一个100多行的结构体。这个结构体仅仅包含了内核需要
的进程信息,在PM和FS中还包含了另一些进程信息。下面,我们就来看一下这个进程表项中包含的一些主要信息。
    struct proc的头部是一个struct stackframe_s结构体,这个结构体是体系结构相关的。以i386为例,它
的定义是(在文件src/include/arch/i386/stackframe.h中):

struct stackframe_s {
u16_t gs;                     /* last item pushed by save */
u16_t fs;                     /*  ^ */
u16_t es;                     /*  | */
u16_t ds;                     /*  | */
reg_t di;                     /* di through cx are not accessed in C */
reg_t si;                     /* order is to match pusha/popa */
reg_t fp;                     /* bp */
reg_t st;                     /* hole for another copy of sp */
reg_t bx;                     /*  | */
reg_t dx;                     /*  | */
reg_t cx;                     /*  | */
reg_t retreg;                 /* ax and above are all pushed by save */
reg_t retadr;                 /* return address for assembly code save() */
reg_t pc;                     /*  ^  last item pushed by interrupt */
reg_t cs;                     /*  | */
reg_t psw;                    /*  | */
reg_t sp;                     /*  | */
reg_t ss;                     /* these are pushed by CPU during interrupt */
};
    
    从上面可见,这个部分就是包含了CPU所有的寄存器。当进行进程切换时,用于保存、恢复CPU的状态。
类似的,第二个成员struct fpu_state_s用于保存、恢复FPU的状态。
    struct priv *p_priv用于指向该进程对应的系统权限结构,每个系统进程有不同的结构,所有的用户进程
共享一个特权结构。

    下面几个是比较重要的调度相关的信息:
    p_rts_flags:该bitmap表示了进程的状态,只有它为全0时,对应的进程才可以运行。
    p_misc_flags:这是进程的另外一些标志,这些标志不会导致进程阻塞。
    p_priority:当前进程优先级;
    P_cup_time_left:当前还剩下的CPU时间;
    p_scheduler:指向负责该进程调度的调度器进程,如果为NULL,或者指向自己,则使用内核默认的调度器。

    p_accounting成员用于记账。

    struct mem_map p_memmap[NR_LOCAL_SEGS]:该成员记录进程的内存映射信息。其中,NR_LOCAL_SEGS
定义为3,分别代表T(代码段),D(数据段),S(栈段)的地址映射。
    
    用于串联进程结构的几个指针:
    p_nextready:指向该结构所在调度队列中下一个进程的指针;
    p_caller_q:指向想要向该进程发送消息的进程队列;
    p_q_link:用于指向p_caller_q队列中下一个进程。

    消息缓冲区:
    p_sendmsg:该进程要发送的消息;
    p_delivermsg:发给该进程的消息;

    虚拟内存管理:
    p_vmrequest:这个用于虚拟内存管理。以后说明。

    除了进程表定义外,在proc.h文件中还定义了很多相关的宏,主要包括两大类:
    1.  标志、常量宏;
    2.  字段访问宏;
** Runtime flags
    这些标志用于p_rts_flags字段,一个进程可运行,当且仅当p_rts_flags为0。详细定义请参考代码。举例:
    RTS_SLOT_FREE,进程表项空闲;
    RTS_PAGEFAULT,页错误;
    RTS_SENDING,由于正在发送消息而阻塞。等等。

* 权限表
    前面我们提到,每个进程结构体都指向一个权限结构,该结构定义在头文件priv.h中。这个权限表是一个
struct priv的数组,数组大小是NR_SYS_PROCS(默认是64).如前文所说,在系统中,每个系统进程都有自己单独
的一个权限结构,所有的用户进程共享同一个权限结构。
    "进程表和权限表使用了一个共同的技巧——构造一个指针数组来加速访问表中的成员。"
    权限结构体的几个关键成员包括:
    1.  s_proc_nr:指向该结构体的进程的数目;
    2.  s_id:该结构体的下标(0是用户进程,系统进程从1开始依此分配);
    3.  s_flags:标志,包括PREEMPTIBLE,BILLABLE,SYSTEM;
    4.  s_trap_mask:允许的系统调用ipc方式,包括ECHO,SEND,RECEIVE,BOTH(指sendrec),
            NOTIFICATION;
    5.  s_ipc_to:这是一个位掩码,表示ipc的目标可以是哪个进程。在这个变量中,所有的用户进程共享
        bit(0),其它位,每位对应一个系统进程。
    6.  s_k_call_mask:这个位掩码表示了该进程允许调用的kernel calls;
    7.  s_sig_mgr/s_bak_sig_mgr:对于系统进程,signal的管理者;
    8.  s_notify_pending/s_int_pending/s_sig_pending:对系统进程而言的pending的通知、终端、信号;
    9.  s_alarm_timer:对与系统进程的alarm计时器;
    10. s_nr_io_range/s_io_table:对于系统进程允许访问的io端口;
    11. s_nr_mem_range/s_mem_table:对于系统进程允许的内存区域;
    12. s_nr_irq/s_irq_table:对于系统进程,允许的中断线;
    上面,“对于系统进程”的几项对与用户进程没有意义。例如alarm timer,(前面提到过,所有的用户进程
共享一个priv结构体,但它们不能共享一个timer),每个用户进程使用的alarm timer在PM中管理。

* 全局量的定义
    前面,我们看到了两个重要的全局数据结构,进程表和权限表,其实内核还使用了别的全局数据结构。这些
结构的空间实际分配是由table.c和机器相关的汇编代码完成的。除了在头文件中声明的以外,table.c中还包含
了两个重要的结构:

** 栈
    这里的栈是给内核中的任务使用的栈,其实,kernel任务的栈是被机器相关的汇编语言分配的,此处分配
的栈空间用于idle、system、clock任务。

** BootTable
    这个表是一个boot_image结构体构成的数组,每个结构体包含:
    1.  proc_nr:进程号
    2.  flags:进程标志
    3.  stacksize:栈大小;
    4.  proc_name:一个展示名;
    5.  endpoint:进程的endpoint号,用于消息传递的目标/源。
    这个结构体数组已经被初始化好了。在内核初始化的时候,它们用于提供各个已经加载的进程的相关信息。
内核会使用这个表中的信息初始化那些已经被bootloader加载的进程。

阅读(2435) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~