Chinaunix首页 | 论坛 | 博客
  • 博客访问: 661528
  • 博文数量: 156
  • 博客积分: 4833
  • 博客等级: 上校
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-21 19:36
文章分类

全部博文(156)

文章存档

2016年(2)

2013年(1)

2012年(13)

2011年(30)

2010年(46)

2009年(29)

2008年(23)

2007年(12)

分类: LINUX

2012-04-26 15:42:33

设计一个资源管理系统对于一个综合性的资源访问系统来说是十分必要的,而好的设计一定是不复杂的,甚至说是十分简单的,原因就是精简机构,消除冗 余,或者说精兵简政在任何时候任何方面总是必要的,我们不希望管理机制本身消耗大量的资源(时间,空间,或者其它),因此在设计之前一定要有好的实体抽 象,这些实体抽象不一定要多但一定要完整。我自认为自己的一个设计是比较不错的,实话说,它真的不错,如果你否认这一点的话,请看完本文,最终你会明白我 为何如此自恋。首先抽象出受控进程,控制元素以及管理组的概念,在详细分析设计步骤之前,先把这些抽象的概念形象化一下:
受控进程:ssh,sshd,httpd...
控制元素:cpu占用率,内存总量,磁盘使用...
管理组:组1,组2,组3
控制粒度:基于进程和控制元素,也就是说管理组(组x)不必控制控制元素中的所有元素,只需要控制其中一个或者几个即可,当然也可以是全部。
有了以上假设,那么几种对应关系就很显然了,指出这些对应关系之前首先定义几种角色:
主体:受控进程
客体:控制元素
管理者:管理组
总体描述:主体在管理者的管理下对客体产生动作。
以上这些都是最最基本的元素了,我认为不能比这更简单了,因此我认为它是完美的,理解了角色和总的描述,它们的关系如下:
受控进程->控制元素:这是个一对多的关系,一个进程可以接受多种元素的控制
控制元素->受控进程:这是个一对多的关系,一个控制元素可以控制多个进程
受控进程<=>控制元素:因此这是个多对多的关系
控制元素->管理组:这是个一对一的关系,即某个进程的一个受控元素只能由一个管理组控制
管理组->控制元素:这是个一对多的关系,即一个管理组可以管理一组进程的多个控制元素
管理组<=>受控进程:这是个多对多的关系,即一个管理组可以可以控制多个进程,反过来一个进程的不同控制元素也能被多个管理组控制。
     有了以上的关系,我想任何关系数据库的高手都能设计出表结构来的,甚至都不需要是高手,只要会一些数据库的知识应该就不成问题,这个关系结构很显然,表结 构也很简单可是在不允许查询数据库或者没有数据库环境的情况下,如何实现这个方案呢?比如在一个嵌入式的小内存的设备上,如何仅仅通过c语言或者别的轻量 级语言编程实现它呢?要知道数据库的查询操作实际上仅仅给外部提供一个接口,而其实现是由查询引擎完成的,在这个意义上,我们要实现的就是一个小型的查询 引擎了,因此首先我们需要先设计一下数据结构,最基本的就是主体,客体,管理者的设计了:
主体:
struct task {
    void *other;
    List *targets;
}Task;
客体:
struct target {
    void *other;
    List *tasks;
    Manager *man;
}Target;
管理者
struct manager {
    void *other;
    List *targets;
}Manager;
以上是最初步的设计,已经展现了对应关系,但是无法索引当前的客体,因此有必要将所有的客体组织成数组的形式:
Target ts[] = {cpu,memory,file,...};
主体重新设计为:
struct task {
    void *other;
    Target targets[N];
}Task;
但是这样的话,每一个Target的结构体中除了tasks字段之外的数据将会重复,这就浪费了内存,因此有必要将task和target进行解除耦合,于是设计一个中间结构,用于拼接这两个结构:
struct middle {
    List *tasks;       //所有的使用这个middle的task链表
    Target targets[N]; //以上tasks这些进程的N控制元素
}Mid;
struct task {
    void *other;
    Mid *mid;
}Task;
struct target {
    void *other;
    Manager *man;
}Target;
这 样,所有的被同一些控制元素控制的task就可以共享一个target了,从而节省了内存。由于task和target是多对多的关系,因此使用了一个 middle来解除其耦合,达到了共享一方的效果,同样的道理,task和manager的关系也是多对多的关系,于是同样的措施必然带来同样的效果。于 是重新设计:
struct manager {
    void *other;
    List *targets;
}Manager;
struct middle2 {
    List *tasks;       //所有的被这个管理者管理的task链表
    List *managers;    //一个task属于的所有的管理者
}Mid2;
struct task {
    void *other;
    Mid *mid;
    Mid2 *mid2;
}Task;
仔 细一想,这样不妥,原因有二,第一个原因是一个task虽然属于很多manager,但是它是基于target的,这个信息没有体现出来,第二,信息冗 余,这样在修改或者同步起来非常不便,于是必然要重新设计,由于manager和target的联系已经建立了,然而这个关系是单向的,数据查询因此也只 能是单向的,我们能通过一个task得到它归于哪些manager,却无法反过来从一个manager得到它都控制了哪些task,由于管理者基于一组 target来分组,因此所有这些task拥有共同的有效target[N],因此必然需要建立一个反向的关系,首先在Manager中建立一个新的 List字段:
struct manager {
    void *other;
    List *targets;
    List *tasks;
}Manager;
从 基于target组进行管理的前提并且task和target已经通过middle联系得知,这个manager需要和middle用类似middile 的结构体进行联系,姑且将该结构体设为middle2,这个middle2对于manager来说,它代表一个middle,也就是一组task以及对应 的一组target,这个middle2对于一个middle来说,它代表一个manager,为了表现manager和middle是对对多的关 系,middle2的设计和middle的设计几乎一样:
struct middle2 {
    List *manager;   //对于一个middle来说,该链表包含所有的targets数组的manger字段的并集,当然由于一个middle对应N个target,具体哪个 target对应哪个manager,需要查阅middle的targets数组来一一确定。
    List *mids;     //对于一个manager来说,该链表包含它所参与管理的所有的middle
    Mid *mid;    //一个mid2唯一对应一个mid。这是基于targets集合意义的,而不是基于单独的target的
}Mid2;
最终的设计如下:
struct middle {
    List *tasks;       //所有的使用这个middle的task链表
    Target targets[N]; //这里使用数组而没有使用List完全是为了代码的简洁,可以直接通过enum枚举名来直接访问
}Mid;
struct middle {
    List *tasks;      
    List *mid2;   //由于下面的N个target并不一定属于一个manager,因此一个middle可能也属于多个manager,这个关系通过mid2来耦合。
    Target targets[N];
}Mid;
struct middle2 {
    List *manager;  
    List *mids;
    Mid *mid;   
}Mid2;
struct task {
    void *other;
    Mid *mid;
}Task;
struct target {
    void *other;
    Manager *man;
}Target;
struct manager {
    void *other;
    List *targets;
    List *mid2;
}Manager;
这 么一个设计基本就完美了,我们试一下几个查询。首先,从一个task查询一下它的各个控制元素都受哪个管理者的控制,这个很简单,从task取出mid字 段,然后遍历整个targets数组,从每一个元素中取出manager字段即可;接下来根据一个manager查询一下它都控制了哪些进程,取出 manager的mid2字段,遍历其中的每一个middle2元素,对于每一个middle2,取出其mid字段,然后遍历其task字段,取出所有的 task即可。着重需要说明的是middle2结构,它具有两重身份,mids是作为单独的target起作用的,毕竟一个middle结构体拥有不同的 target,这些target不一定有相同的manager的,而mid字段是作为一组targets起作用的,换句话说,它针对的是一个middle 而不是middle中单独的target。
     本设计可以使用数据库,但是却没有使用数据库,因为本设计的精髓在于设计的实现而不是关系模型,可以看出,linux内核在设计数据结构的时候和本文的方 式有些类似,实际情况是:本文就是参考linux内核的cgroups做出来的,因此我才敢说它很完美(尽管linux内核并不总是很完美)!看完了本 文,再去理解linux内核代码中关于cgroups的复杂代码,我相信就清晰多了。在此给出一些对应:
task--task_struct
target--cgroup_subsys_state
manager--cgroup
middle--css_set
middle2--cg_cgroup_link
其中在find_existing_css_set函数中有下面的判断:
if (root->subsys_bits & (1UL << i))
这 说明,虽然你建立了一个新的cgroup,然后这个cgroup中具体哪些subsys起作用,所依据的是你mount cgroup时的-o参数,因此虽然将一个进程放入了一个cgroup,如果这个cgroup在mount的时候没有memory参数而只有cpu参数的 话,该进程的内存使用情况还将受原先cgroup的限制而不受这个新的cgroup的限制。另外需要注意的是,linux内核的cgroup使用了OO的 思想,cgroup_subsys_state并不提供任何信息,如果你想提供一些信息,比如想提供内存使用方面的限制,那么建立一个结构体,让 cgroup_subsys_state作为第一个元素(或者第n个元素),这样cgroup_subsys_state就是一个根类对象,让所有其它的 具体类继承它即可:
struct memctrl {
    cgroup_subsys_state cst;
    ...//其它的具体字段
};
然后用container_of取出具体的结构即可!

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/dog250/archive/2010/11/03/5985710.aspx

阅读(1771) | 评论(0) | 转发(0) |
0

上一篇:bsd coredump

下一篇:switch_mm

给主人留下些什么吧!~~