发信人: ChunJie (飞翔的白鸽), 信区: CRC
标  题: 集群系统MOSXI分析---4
发信站: 哈工大紫丁香 (2002年04月19日09:37:30 星期五), 站内信件

  
内容: 
 
数据结构 
MOSIX连接层接口 
关于作者 
 
 
相关内容: 
 
(1)进程迁移简介  
(2)MOSIX系统简介  
(3)MOSIX代理远程机制  
集群系统专栏  
 
 
 
 
MOSIX代理远程机制 
黄翊 (easysing@yahoo.com)
2002 年 1 月 22 日

集群系统的各组成部分经常要互相通讯,MOSIX提供了一个连接层抽象(linker layer
),在套接字接口之上使用TCP/UDP协议进行通信。
作为一个集群系统,节点之间要经常合作,不时的散布和收集负载信息,获取其它节
点的情况。另外,我们也已经看到,在MOSIX中,一个迁移进程实际上是由deputy和
remote两部分组成的。Deputy和remote是两个单独的进程,分别位于不同的节点,但
在逻辑上,它们却是看出一个独立的进程的。它们之间经常要通过频繁的通讯来合作
。因此,通信机制是至关重要的。MOSIX提供了一个连接层抽象(linker layer),
在套接字接口之上封装了一层,提供了一系列类似BSD SOCKET的接口函数用于在进程
间接收和发送MOSIX相关数据的。MOSIX连接是位于内核中的,对用户态进程是不可见
的。

作为一个集群系统,MOSIX中的每个节点都被分配一个唯一的节点号,节点号是一个
整型值,从1开始连续分配的。0表示的是当前节点。通过节点号抽象,可以很方便的
定位某个节点。但是通过网络发送和接收数据,都是以IP地址来寻找节点的。因此,
MOSIX系统提供了一系列函数用于在节点号和IP地址间的互相转换和匹配。每个节点
在/etc/目录下都存在着一个配置文件mosix.map,保存着整个集群系统节点的IP地址
信息。该文件的每一行都包含3个域,分别为节点范围的起始号,IP地址或主机名,
该范围中的节点数目。例如某系统的配置文件如下:


1  172.26.4.138  1

2  172.26.4.139  2

4  MOSIX_4    1

机器MOSIX_4的IP为172.26.4.22。在这个系统中,节点1的IP为172.26.4.138。 节点
2,3的IP则分别为172.26.4.139,172.26.4.140。节点4的IP则为172.26.4.22。

数据结构

struct mosix_link表示一条MOSIX连接,它维持着该连接的状态和一些控制信息。定
义如下:


struct mosix_link {
    struct socket *sock; /* 套接字控制结构,用于通讯,通过它进行实际的网络操
作*/
    int flags;      /* 状态标志 */
    int dlen;       /*悬挂数据的长度 */
    int peer;       /* 连接另一端的节点号*/
    char *hideptr;      /*指向当前隐藏数据的指针*/
    char *hidebuf;      /*指向隐藏数据缓冲区起始地址的指针 */
    int hidelen;        /* 隐藏数据的长度*/
    char head[COMM_DEF_HEADSIZE];   /* 存放着消息头*/
 };
struct comm_header {消息格式
    unsigned short olen;//Option data length??
    unsigned short hlen;
    int type;
    int dlen;
    int regs;
    int dfsalen;
};
#define COMM_HLEN   (sizeof(struct comm_header))

 




MOSIX中,每个进程的控制块中都保持着一条MOSIX连接contact(struct mosix_link
 *contact),用于deputy和remote之间的通讯。进程的remote部分可以在节点间多
次迁移,但是deputy总能根据它的contact连接来定位remote部分;remote也能根据
它的contact连接和deputy部分通讯合作。对于未迁移的进程,contact总是为NULL。


MOSIX连接层接口

MOSIX连接层提供了一系列接口用于集群间进程之间互相发送和接收数据。从这些接
口的名字上,我们就很容易判断出它们的功能。

comm_open(): 为MOSIX 通讯打开一条连接 
mosix_link * comm_open(int mos, mosix_addr *maddr, unsigned long timo) 
如果mos > 0,则连接到第#mos号节点上的迁移守护进程。 
如果mos = COMM_TOADDR,则连接maddr->saddr中给定的地址。 
如果mos = COMM_ACCEPT,则打开一个SOCKET,并且可以接受连接。 
如果mos = COMM_MIGD,则为迁移守护进程设置SOCKET。 
如果mos = COMM_INFOD,则为信息守护进程设置SOCKET。 
如果mos = COMM_LOOSE,则允许连接多个守护进程。 

comm_use() :为进程设置新的连接并返回旧的连接 
mosix_link *comm_use(struct task_struct *p, mosix_link *mlink) 
进程p将使用mlink连接进行通讯。 

comm_close():关闭Mosix通讯连接 
void comm_close(mosix_link *mlink) 
调用comm_shutdown(mlink)关闭对连接的写入,然后通过sock_release(mlink->sock
)释放该连接上的套接字。 

comm_accept() :在Mosix通讯套接字上接受连接 
int comm_accept(mosix_link *ml, mosix_link **mlp, mosix_addr *ma, unsigned
 long t) 
接受建立连接请求,mlp指向建立的新连接,连接的另一端的地址保存在ma中。 

comm_waitaccept() :在Mosix通讯套接字上接受连接 
static int 
comm_waitaccept()comm_waitaccept()调用comm_accept接收连接请求并建立一条新
的连接,然后comm_use这条新的连接并关闭旧的连接。 

comm_send() :发送一条消息 (head + data) 
int comm_send(int type, void *head, int hlen, void *data, int dlen, int uspace

如果要发送的数据来自用户空间(uspace),且可能会导致远程page-fault(!dirty_all_remote_pages
),则先把数据从用户空间拷入内核,然后检查地址范围的有效性(!ucache_ok)。
 
如果type & COMM_MFREGS或则type & COMM_MFIDENT,则分别通过comm_packregs(0,
 0)和comm_packident(0)进行压缩,可能产生选项数据。 
将数据按照消息格式组装起来,然后调用套接字操作集上的sendmsg操作将消息发送
出去。 

comm_sendto() : 发送一个数据报 
int comm_sendto(int mos, void *data, int len, mosix_link *mlink, mosix_addr
 *to) 
通过mlink连接,发送len长的数据到to指明的地址,发送的数据在data中。 
to指明目的地址,如果为空,则数据是发送给节点mos上的负载信息守护进程(INFO_DAEMON
)。 
调用套接字操作集上的sendmsg操作将消息发送到指定的地址。 

comm_dorecv() :从连接中可靠的读取数据 
int comm_dorecv(struct socket *sock, struct msghdr *msg, int len) 
根据msg,通过调用套接字操作集上的recvmsg操作从网络连接上读取数据。它读的是
字节流,并没有格式的。 

comm_recv() :接收消息头 
int comm_recv(void **headp, int *hlen) 
如果当前进程的连接处于等待接受连接请求状态(COMM_WAITACCEPT),则等待直到
接受请求。然后通过comm_dorecv()接收消息头长度(COMM_HLEN)的数据,得到消
息头的实际长度(hlen+olen),然后准备空间存放消息头数据(comm_mkhead),
再通过comm_dorecv()接收实际的消息头数据,并根据其中的信息,调用不同的解
压处理程序。因为为了减少传输的数据量,消息数据发送前都经过了压缩。 
如果有数据COMM_MFDATA,则将数据长度保存在连接的数据长度中(mlink->dlen = 
header.dlen),这样,随后调用comm_copydata和comm_recvdata时,我们将知道应
该能够从网络中读取多少数据。 

comm_copydata():从消息中拷贝数据 
int comm_copydata(void *data, int len, int uspace) 返回0表示成功。 
从消息中拷贝长度为len的数据。读入的数据保存在data指向的buffer中。Uspace表
明data是指向用户空间还是内核空间。成功时返回0。 
如果要data指向用户空间(uspace),且会导致远程page-fault(!dirty_all_remote_pages
),则返回内存不足错误。 
如果连接中不存在数据,但是隐藏数据缓冲区不为空,则从隐藏数据缓冲区拷贝len
长数据到data中。否则则通过comm_dorecv函数从网络中读取数据。 

comm_recvdata():从连接中将所有数据读取到已分配缓冲区中/* 
int comm_recvdata(void **data) 返回0表示成功。 
如果连接中存在隐藏数据(COMM_HIDEDATA),则data指向隐藏数据缓冲区,然后置
连接的隐藏数据缓冲区为NULL。 
否则,调用comm_malloc分配内存,通过comm_dorecv试图将连接所有的数据(mlink-
>dlen)都接收放入到其中。data将指向分配的内存缓冲区。 

comm_recvfrom():接收数据报 
int comm_recvfrom(void *data, int len, mosix_link *mlink, mosix_addr *from
, unsigned long timo) 
读取长为len的数据放在data中,from返回接收数据报的源地址。timo指明超时值,
单位为微秒。返回接收数据的长度( >0)或者错误。 
调用套接字操作集上的recvmsg操作来接收数据,数据报的源地址返回在from中。如
果接收到数据,则通过net_to_mos检查该源地址是否属于MOSIX节点。 
如果不属于MOSIX节点,则comm_recvfrom()返回错误。 

comm_free() :释放不再被使用的消息头或数据。 
void comm_free(void *head) 
如果当前进程的该连接是COMM_INFOLINK,则释放head,返回。 
首先检查head是否超出连接消息头范围。如果head指向消息头的起始地址,则标记该
消息头不再被使用(~COMM_HEADINUSE)。否则释放head所指的内存空间。 

comm_mkhead() :准备指定大小的消息头空间 
void *comm_mkhead(int hlen) 
从内核中分配hlen大小的GFP_KERNEL内存空间,mlink->head指向分配的起始地址。
 

comm_flushdata():清空前一个消息剩余的数据 
void comm_flushdata(int dlen) 
如果当前连接是隐藏数据(mlink->flags & COMM_HIDEDATA),则从隐藏数据中flush
长度为dlen的数据。否则,调用comm_dorecv从连接中读取长度为dlen的数据。 

comm_peek():检查该连接是否有数据悬挂 
int comm_peek(void) 
调用套接字操作集上的poll函数来判断是否有数据悬挂。 

comm_poll():等待直到有通讯事件、中断或MOSIX事件产生 
int comm_poll(int mask, int interruptible, unsigned long timo) 
interruptible指明是否可以被中断,timeo设置等待超时时间。 

comm_wait() :等待消息到达或者MOSIX事件产生 
int comm_wait(void) 
返回1表示消息到达,返回0则表示先产生了一个事件。 

comm_send_urgent():使用紧急数据(OOB)发送事件通知 
int comm_send_urgent(void) 
紧急事件通知只能由代理发送。MOSIX中将OOB数据定为0xdb。它只发送一个字节的有
效数据(0xdb),通过将msghdr的msg_flags标志位设置MSG_OOB|MSG_NOSIGNAL来表
示紧急情况。 

comm_test_urgent():检查是否悬挂了紧急数据(OOB) 
int comm_test_urgent(void) 
通过设置MSG_OOB|MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL属性来调用套接字操作集上
的recvmsg操作来接收数据。如果接收到的长度为1且等于0xdb,则表明悬挂了紧急数
据。 

comm_take_urgent():从流中取出悬挂的OOB数据,以免被转化为普通数据处理 
void comm_take_urgent(void) 
调用者必须确保流中没有其它的数据。 

首先以MSG_OOB|MSG_DONTWAIT|MSG_NOSIGNAL属性读取一字节,判断是否有OOB数据,
然后设置套接字SO_OOBINLINE选项为on,让带外数据保留在正常的输入队列中。然后
调用套接字操作集上的recvmsg操作来读取这一个字节的带外数据。最后关闭套接字
的SO_OOBINLINE选项。

关于作者: 
黄翊,男,国防科大计算机学院硕士研究生,对操作系统和集群技术感兴趣,欢迎您
通过easysing@yahoo.com.与他联系。  
  



--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.228.212]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:17.813毫秒