集群系统MOSIX分析(5)_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4064 | 回复: 0   主题: 集群系统MOSIX分析(5)        下一篇 
cc
注册用户
等级:中校
经验:1900
发帖:195
精华:0
注册:2011-7-25
状态:离线
发送短消息息给cc 加好友    发送短消息息给cc 发消息
发表于: IP:您无权察看 2011-9-5 11:31:11 | [全部帖] [楼主帖] 楼主

迁移守护进程

在内核成功装入内存以及一些关键硬件(如已经在低层设置过内存管理器MMU)后,内核将跳转到start_kernel完成其余的系统初始化工作。start_kernel在完成初始化内核自身的部分组件-内存、硬件中断、调度程序,分析内核各种选项,测试CPU的缺陷后,将转而执行init函数,在其中通过init_mosix()函数进行MOSIX系统的初始化。

MOSIX系统初始化时,将会首先产生三个守护进程mosix_mig_daemon和mosix_info_daemon,mosix_mem_daemon。它们都是作为一个内核态线程运行的,没有虚拟存储空间,直接使用物理地址空间。这三个守护进程必须驻留在宿主节点(UHN)中而不能被迁移走。

迁移守护进程mosix_mig_daemon的主要功能则是处理迁移请求。它在固定的迁移端口(COMM_MIGD)监听迁移请求,每收到一个迁移请求,则fork一个相应的子进程去处理该请求。整个过程就如同C/S结构网络编程中socket( )、bind( ),listen( )、accept( )、fork( ) 一致。当一个进程A需要迁移到另一个节点时,它将首先向该节点上的迁移守护进程建立一条连接。迁移守护进程将accept该请求,建立一条连接, 并fork一个子进程B执行mig_remote_receive_proc函数,处理该进程的迁移工作。子进程利用从父进程继承下来的刚刚建立的那条连接,接收进程A的状态,并据此修改自身的进程状态。子进程B是迁移守护进程mosix_mig_daemon通过user_thread创建的,它最终退出时会进入用户态,而此时它的进程状态已经被要迁移的进程A的状态所修改,因此,它会从A被迁移前暂停的地方继续恢复运行。此时,进程A将会一直运行在核心态,成为deputy部分,而进程B则成为remote部分,通过它们之间建立的那条网络连接互相通讯。关于user_thread的细节以后将会解释。



MOSIX进程迁移的一般过程

1 选择要迁移进程和目标节点:

MOSIX中选择迁移进程有两种方式。一种是用户明确控制,通过MOSIX附带的系统命令migrate Pid Node,将进程标识符为Pid的进程迁移到节点Node上。命令mosrun 则允许在指定的节点上初始运行进程。另一种方式则是由负载平衡模块进行自动调度,根据集群的负载情况和历史信息,按照特定的平衡算法,选择某个进程迁移到比较空闲的节点上,以达到整个集群的最佳性能。每个进程都有一个MOSIX控制块(mosix_task),它的成员whereto如果不为0,则为该进程需要迁往的目的节点。

Mosix是采用接收者启动的方式。在Linux中,所有进程部分时间运行于用户模式,部分时间运行于系统模式。进程不能被抢占,只要能够运行它们就不能被停止( 除非时间片被用完)。当进程必须等待某个系统事件时,它才决定释放出CPU。因此,如果负载平衡模块在核心态中,决定了要迁移走本地进程a。它只是通过mosix_do_add_to_whereto( )设置进程a的whereto,标识它将要被迁移,而无法立即将进程迁移走。如果a能被Mosix事件唤醒的话,还会将其放入运行队列中。由于计算进程优先级时(goodness( )),具有Mosix内容的进程的优先级会被提升。这样,在下一次进程调度(schedule( ))中, 具有Mosix事件的进程将更容易获得CPU。当进程a被调度选中执行时,在返回用户态之前,将会首先处理完所有能够处理的Mosix事件,并且判断是否被选中迁移。如果被选中,则向目的节点的迁移守护进程发出迁移请求。如果该进程A需要被迁移,将会在此时被迁移走。因此,从概念上来说,进程可以说是"主动"迁移,即迁移进程的动作实际发生在当前进程的上下文中,迁移的也是当前进程的内容,然后,该进程实际变为远程进程的代理(deputy)。

2 向目标节点发送迁移请求并协商:

当节点A的调度函数schedule( )调度进程a运行时,在a退出核心态之前,OS将会检查该标志位,发现进程a要被迁移到节点B,则会向节点B上的迁移守护进程mosix_mig_daemon发出迁移请求。 节点B上的迁移守护进程接收到该请求后,将会派生(fork)出一个新的进程b来处理迁移请求,自己则继续在迁移端口监听迁移请求信息。b被标志为Remote进程。此时,进程b和进程a之间已经建立一条TCP的通讯连接,用于相互间交换信息。进程b则首先向B的负载平衡模块询问,是否要接受该请求。负载平衡模块根据平衡算法来决定接收或拒绝迁移请求。

3 提取并传送进程状态:

当节点B同意进程a迁移到本节点后,将向节点A发出同意应答。A收到同意应答后,则开始获取a的进程状态,并通过a与b之间打开的连接将状态传送到进程b。进程b则接收进程a的状态,并据此修改自身的进程状态。

4 恢复进程的执行:

a的进程状态迁移完后,a将成为进程的deputy部分,一直处于核心态的deputy_main_loop循环中,直到进程死亡或被迁移回home节点。在节点B上,进程b被改为就绪状态,加入运行队列中,等待被调度执行。当b被调度后,返回到用户空间后,将从a迁移前的用户指令处继续往下执行。至此,整个迁移过程完毕。



进程状态提取和恢复

在Linux中,一个进程有关的所有信息都保存在进程控制块(struct task_struct)中,包括进程调度、进程标识和用户标识、信号量处理、文件系统管理、内存管理、进程标志等等信息。进程标识符pid唯一的标识着一个进程。全局变量current指针指向当前正在运行进程的进程控制块。MOSIX是采用抢先式进程迁移机制,迁移的是当前的活跃进程。因此,通过全局变量current,我们很容易获取到正在迁移进程的的进程状态信息。

进程内存状态:

task_struct的数据成员mm指向关于存储管理的struct mm_struct结构。它包含着进程内存管理的很多重要数据,如进程代码段、数据段、未初始化数据段、调用参数区和进程环境区的起始地址和结束地址,栈的起始地址和进程空间的大小。用户程序和代码一般占据虚拟地址空间的下方,这一部分是内存映射部分,其内容来自映射文件。界于用户执行映象和堆栈之间的部分是堆,在用户程序中动态申请的地址空间(如malloc)来源于堆。虚拟内存结构mm_struct中的start_brk和brk记录有关堆的信息。其中start_brk是用户虚拟地址空间初始化时,堆的终止位置;brk是当前堆的终止位置。

北京联动北方科技有限公司

下面列出了struct mm_struct结构的一些重要成员:

struct mm_struct {
       struct vm_area_struct * mmap; /* 指向VMA双向链表的指针*/
       struct vm_area_struct * mmap_avl; /*指向VMA段AVL树的指针*/
       struct vm_area_struct * mmap_cache; /*保存上次 find_vma 的结果 */
       pgd_t * pgd; /*进程页目录的起始地址*/
       atomic_t mm_users; /*用户空间的用户数 */
       atomic_t mm_count; /* "struct mm_struct" 的引用计数 */
       int map_count; /* VMAs的个数 */
       unsigned long start_code, end_code, start_data, end_data;
       unsigned long start_brk, brk, start_stack;
       unsigned long arg_start, arg_end, env_start, env_end;
      mm_context_t context; /* 体系结构相关的 MM上下文 */
      struct rw_semaphore mmap_sem; /*读写信号量*/
};


MOSIX中进程的mm_struct结构中提取内存状态,并被封装成struct mm_stats_h结构,和MIG_MM_STATS 消息一起发送到目的进程去(参见mig_send_mm_stats())

struct mm_stats_h
{
       unsigned long start_code, end_code, start_data, end_data;
       unsigned long start_brk, brk, start_stack;
       unsigned long arg_start, arg_end, env_start, env_end;
       unsigned long task_size;
};


进程虚地址空间:

为了能以自然的"方式"管理进程虚空间,Linux定义了虚存段VMA(vitual memory area),一个VMA是进程的一段连续的虚存空间,在这段虚存里的所有单元拥有相同的特征,如相同的访问权限、属于同一进程等。VMA由struct vm_area_struct描述,域vm_start 和vm_end是两个虚拟地址,它们描述了一个虚拟内存区域在进程虚拟地址空间中的位置;域vm_file记录了该区域所映射的文件,而vm_offset 表示该区域在映射文件中的开始位置。如果这两个域存在,它们和vm_start 和vm_end一起,描述了文件vm_file和虚拟内存之间的映射关系。域vm_next是个指针,用来将同一进程的所有虚拟内存区域连接成一个链表,表头位于mm_struct结构的mmap成员。因此,能够从mmap表头出发,依次获得所有的虚拟内存区域的状态。下面列出了struct vm_area_struc的一些主要成员。

struct vm_area_struct {
       struct mm_struct * vm_mm; /* 所属的地址空间,回指mm_struct结构. */
       unsigned long vm_start; /* 开始地址. */
       unsigned long vm_end; /* 结束地址*/
       struct vm_area_struct *vm_next; /*指向本进程下一个VMA ,按地址排序 */
       pgprot_t vm_page_prot; /*该VMA的访问属性. */
       unsigned long vm_flags; /* 标志位 */
       struct vm_operations_struct * vm_ops; /*该VMA的操作集*/
      struct file * vm_file; /* 指向映射文件 (可以为 NULL). */
       /* AVL树,按地址排序*/
       short vm_avl_height;
       struct vm_area_struct * vm_avl_left;
       struct vm_area_struct * vm_avl_right;
       /*共享VMA*/
       struct vm_area_struct *vm_next_share;
       struct vm_area_struct **vm_pprev_share;
}


物理页面:

Linux采用"按需调页"(demand paging)算法,支持三层页式存储管理策略。第一级是页目录(pgd),第二级是页中间目录(pmd),第三级是页表(pte)。页表由页表项组成,每个页表项保存着物理内存页的首址和页属性。task_struct的数据成员mm的pgd成员为进程页目录的起始地址。Linux提供了一系列宏将虚地址转换成物理地址。下面给出一段示例性代码,将虚地址vaddr转换成物理地址addr:

unsigned char* GeuphyAddr(struct vm_area_struct* vma,unsigned long vaddr)
{
      pgd_t *pgdir; pmd_t *pmdir; pte_t *pte;
       pgdir = pgd_offset(vma->vm_mm, vaddr);
      if(pgd_none(*pgdir) ||pgd_bad(*pgdir))
       return -EINVAL;
      pmdir = pmd_offset(pgdir, vaddr);
      if(pmd_none(*pmdir) ||pmd_bad(*pmdir))
      return -EINVAL;
      pte = pte_offset(pmdir, vaddr);
      if(pte_present(*pte) )
{addr=(unsigned char )pte_page(*pte);return addr;}
      return -EINVAL;
}


对进程的每个VMA,以物理页面大小(I386结构为4K)为间隔,分别进行三级地址映射,得到物理页面的起始地址,将物理内存依次拷贝到远程节点。远程节点则分配物理页面,在目的进程b上建立相应的页表项。下面给出一段示例性代码,接收一个页面:

int receive_page(unsigned long addr)
{
      pgd_t * pgd; pmd_t * pmd; pte_t * pte;
      unsigned long page = 0;
      struct vm_area_struct * vma;
      struct task_struct *p = current
      if(!(vma = find_vma(p->mm, addr)))
       return -EINVAL;
      if(!(page = __get_free_page(GFP_KERNEL)))
      return -EINVAL;
      /*从接收到的数据中拷贝PAGE_SIZE 大小的数据到page指向的区域*/
      CopyData((void *)page, PAGE_SIZE, 0)) ;
      /*建立并设置新的页表项 */
      pgd = pgd_offset(p->mm, addr);
      if (!(pmd = pmd_alloc(pgd, addr))) return -EINVAL;
      if (!(pte = pte_alloc(pmd, addr))) return -EINVAL;
      if (!pte_none(*pte)) return -EINVAL;
      set_pte(pte, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
}


由于物理页面的迁移很费时间,为了给其它进程必要的运行时间,MOSIX并不会一次将所有页面完全迁移,而是迁移一定数目的页面后,就再次调度schedule( ),给予其它进程运行的机会。

浮点处理器状态

task_struct结构中,used_math成员表明进程是否使用了浮点运算器,struct thread_struct 类型的thread成员则保存了该进程和CPU特定相关的状态。如果进程使用了浮点运算器的话,则也要迁移浮点处理器状态,这可以从thread.i387结构中获得。

union i387_union {
       struct i387_fsave_struct fsave;
       struct i387_fxsave_struct fxsave;
       struct i387_soft_struct soft;
};


局部描述符表(LDT)

局部描述符表是一个段,它其中存放的是局部的、不需要全局共享的段描述符。例如,可以为每个正在运行的任务定义一个LDT,其中包含仅能被该任务使用的段描述符,如任务的代码段、数据段、堆栈段以及一些门描述符等。struct mm_struct的成员context 保存着体系结构相关的MM上下文。但是i386 结构不存在 MMU 上下文, 而是将段信息保存在其中。LDT保存于mm的segment成员中。每个进程最多有LDT_ENTRIES (8192)个条目,每个条目大小为LDT_ENTRY_SIZE (8)。

其它进程状态信息

包括其它的一些进程状态,如进程的时间数据成员,进程标识,进程资源管理等。

  • 资源管理信息(limits)
    结构rlimit中定义了与进程相关的资源限制信息。包括一个用户可以拥有的进程数、一个进程可以打开的文件数、地址空间限制、文件大小、数据大小等。进程可以随意减少自己的当前资源限制,也可以增加自己的当前资源限制,但增加的总数不能超过最大值,而且最大总数不能修改。一个进程只能修改自己的资源限制。进程中与资源控制相关的是数组rlim,其定义如下:

    struct rlimit {
           long rlim_cur; /* 当前值 */
           long rlim_max; /* 最大值 */
    };


    task_struct中rlim[RLIM_NLIMITS]数组保存了该进程得资源限制信息。迁移时,将从中取得CPU时间(RLIMIT_CPU)、最大数据长度(RLIMIT_CPU)、栈大小上界(RLIMIT_CPU)、驻留进程集上界(RLIMIT_CPU),地址空间界限(RLIMIT_CPU)等限制信息,传送到目的进程。

  • 进程标识信息
    包括进程的用户标识(uid)、用户组标识(gid)、进程的组标识号(pgrp)、 会话标识号(session)、可执行文件名(comm)。
  • 时间数据成员
    包括it_virt_value;和it_prof_value软定时值。
  • MOSIX控制相关状态



迁移限制

MOSIX的抢先式进程迁移机制进程支持对绝大多数进程的迁移,但是并不是所有的进程都能被迁移的。具有下面这些情况的进程是无法被迁移的。

  • 进程以共享内存的方式使用文件或者使用了SysV IPC的共享内存。
  • 进程映射了一些设备内存(device memory)。
  • 进程处于8086仿真模式。
  • 进程为内核守护进程。
  • 进程的部分内存被mlock系统调用锁住。
  • 进程与其它线程共享它的内存。
  • 进程为实时调度的进程。因为迁移后无法精确控制时间。
  • 进程被允许直接使用in/out IO指令(通过ioperm系统调用)。
  • init(1号)进程不能被迁移。
  • 进程映射硬件内存来进行原始(raw )I/O。



赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论