`
xiaohui_p
  • 浏览: 16543 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

控制组实现初步分析

阅读更多
控制组实现初步分析
1. 控制组简介
当前的主流linux内核提供了控制组(control group)的功能,利用该功能允许管理员对系统中运行的任务进行分组,然后为每个组分配资源,比如CPU时间,系统内存,网络带宽或者资源的组合.利用控制组系统管理员可更具体的控制系统资源的分配、优先顺序、拒绝、管理和监控,可以更好的根据用户和资源的情况合理的分配资源,提高系统总体效率.内核为了便于管理资源使用子系统表示单一资源,比如CPU时间,系统内存,网络带宽等,然后每个控制组都会关联一个或者多个子系统,进而实现资源管理和配置。当前的内核支持如下可用的子系统:
blkio:设定系统块设备的I/O限制.
cpu:对系统中CPU资源进行管理.
cpuacct:该子系统自动生成控制组使用CPU资源的报告.
cpuset:该系统为控制组分配独立CPU和内存节点.
devices:提供设备访问白名单机制.
freezer:该系统提供挂起或者恢复控制组的机制.
memory:提供控制组对内存使用的限制以及自动生成这些任务内存资源使用报告.
net_cls:允许内核流量控制器识别属于特定控制组的网络包.
ns:名称空间子系统.
此外,在2.6.35及其上内核中允许子系统编译成独立的模块,动态的安装和卸载,当前系统中支持子系统的最大个数为32(unsigned long类型占用的比特数).

2. 基本数据结构与组织
2.1 控制组文件系统
linux内核将控制组组织成若干树形的层次结构,每一个层次结构以控制组文件系统的形式进行管理和操作。用户通过操作控制组文件系统中的控制文件来对控制组进行管理.控制组根文件系统结构定义如下:
struct cgroupfs_root{
	struct super_block *sb;//根文件系统超级块指针
	unsigned long subsys_bits;//可能连接到层次结构的子系统位图
	int hierarchy_id;//该层级结构id
	unsigned long actual_subsys_bits;//实际连接到层次结构的子系统位图
	struct list_head subsys_list;//连接到该层次结构的子系统链表头
	struct cgroup top_cgroup;//层次结构的根控制组
	int number_of_cgroups;//层次结构中控制组的总数
	struct list_head root_list;//该层次结构在层次机构链表中的节点
	unsigned long flags;//层次结构标记
	char release_agent_path[PATH_MAX];//释放通知器的路径.
	char name[MAX_CGORUP_ROOT_NAMELEN];//层次结构名称
};

在系统中可同时存在若干个独立的层次结构(hierarchy),每个层次结构至少包含一个控制组(根控制组,top_cgroup字段),控制组之间按照父-子关系组织成树形结构,每个子系统只能连接到一个层次结构,一个层次结构可以链接若干子系统,显然可通过子系统来唯一的确定其关联到的层次结构.系统定义了默认的层次结构rootnode,该层次结构用于关联没有连接到其他层次结构的子系统,并且rootnode只拥有一个控制组rootnode.top_cgroup,系统启动后,系统中所有的进程均属于该控制组.

下面我们看看控制组数据结构:
struct cgroup{
	unsigned long flags;//控制组标志
	atomic_t count;//控制组的用户数,该值>0表示控制组空闲
	struct list_head sibling;//控制组在兄弟控制组链表中的节点
	struct list_head children;//控制组孩子链表头
	struct cgroup *parent;//父控制组指针
	struct dentry __rcu *dentry;//控制组在控制组文件系统中的目录项指针
	//指向注册到控制组的子系统的指针数组
	struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
	struct cgroupfs_root *root;//指向控制组文件系统根指针
	struct cgroup *top_cgroup;//指向根控制组指针
	struct list_head css_sets;//连接到该控制组中的cg_cgroup_link链表头指针
	struct list_head release_list;//被release agent回收的链表中节点
	struct list_head pidlists;//pidlist链表,用于名称空间
	struct mutex pidlist_mutex;//pidlist互斥锁
	struct list_head event_list;//用户空间希望接收到的事件列表
	spinlock_t event_list_lock;//事件列表自旋锁
};

从上面的结构可知道控制组通过sibling,children,parent组织称树形层次结构.为了快速定位控制组层次结构中控制组文件系统根和根控制组,cgroup结构中增加了root和top_cgroup项,以提高系统的性能.此外,需额外注意的是dentry数据项,它指定了控制组在控制组文件系统中目录项,在该目录下内核会创建控制文件来允许用户空间对控制组的访问与控制。关于其他的数据项现不做进一步的介绍,在后面遇到时再详细介绍.

2.2 任务与控制组的关联
内核将任务分配到不同的控制组中以对其进行管理和控制.也许你会发现上面的cgroup结构中并没有数据项显示的指定属于该控制组的任务,唯一有些相关的就是pidlist,虽然可通过进程pid来确定属于该控制组的所有任务,但是通过pid确定进程的任务结构却不是很容易的事情.为了将任务和控制组关联起来,task_struct结构增加了以下两个数据项:
struct css_set *cgroup;
struct list_head cg_list;
从上面两个数据项中依旧没有发现task_struct和cgroup的任何直接关联,而是遇到了一个新的数据结构css_set,从直觉上说这两项肯定会和cgroup有着什么的瓜葛.我们不应该作主观臆测,通过数据结构的定义才能发现其中的原委。该数据结构的定义并不复杂,其定义如下:
struct css_set{
	atomoc_t refcount;//应用计数
	struct list_node hlist;//控制组hash链表入口
	struct list_head tasks;//和该结构关联的所有任务双向链表头
	struct list_head cg_links;//连接到该结构的所有cg_cgroup_link对象的双向链表头
	//子系统状态结构指针数组
	struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
	struct rcu_head rcu_head;//用于删除时保护
};

即便是知道了css_set的定义,我们仍没有看到task_struct和cgroup的任何瓜葛,反而又遇到了一个还没有搞清楚的结构struct cgroup_subsys_state,以及若干不知道具体作用的双向链表.也许你会有些的抱怨,但是我们还是要顺藤摸瓜,内核这样的设计必然有其的道理,说不准在后面的研究中还会发现更多有趣的事情。关于subsys子系统的数组现在不是主要关注的东西,它会在下一节详细的讲述。

前面提到一个运行中的任务可同时属于不同层次结构中的控制组,也许cg_links就是关联控制组的至关重要的纽带. 从上面的注释中可以知道该数据项表示了cg_cgroup_link结构的双向链表头,揭开该结构的面纱也许可以使得事情变得清晰.和css_set结构一样cg_cgroup_link的设计也是非常的简单,但它却可以扫清眼前的阴霾,找到任务和控制组结构的关联.
struct cg_cgroup_link{
	struct list_head cgrp_link_list;
	struct cgroup *cgrp;
	struct list_head cg_link_list;
	struct css_set *cg;
};

在cg_cgroup_link结构我们终于发现了cgroup的影子,cgrp指针指向了一个cgroup结构,它应该就是和任务关联的控制组.现在似乎已经知道了任务和控制组关联的方法,但是还有一个没有解决的疑点:每个任务可能同时属于多个层次结构不同控制组,显然不可能让cgrp指针同时指向多个控制组结构,也许你会说可以用cgrp指向和任务关联的所有控制组的数组,这当然是一个解决方案,但是这样存在一个问题就是我们不可能对每个任务都这样处理(在最坏情况下我们要为每个任务维护控制组数据,想想这是为什么呢?),因为这样不仅浪费空间,而且通过控制组反过来找关联的任务就不是一件简单的事情了,还有就是不能方便的将任务添加其他控制组中了,以及数据的一致性问题等.

那么cg_cgroup_link到底是如何和cgroup关联起来的呢?为了解决这个疑问,需要回头看看cgroup结构,在该结构中存在一个css_sets数据项,它表示了和cgroup关联的cg_cgrop_link结构的双向链表头节点,结合cg_cgroup_link中的cgrp_link_list数据项,就可将所有和cgroup关联的cg_cgroup_link结构插入到该双向链表中,到此时,你应该知道cgrp的作用了,它就是指向了关联该结构实例的控制组,这样当知道cg_cgrop_link结构时就可以快速定位到关联的cgroup结构了.

如果你明白了上面的表述,那么你可以推测出cg_link_list和cg的作用了,cg指向了和cg_cgroup_link结构关联的css_set结构,而cg_link_list则是添加到和css_set关联的cg_cgroup_link结构的双向链表中的节点,该双向链表头为css_set结构中的cg_links数据项.

到现在终于明白了内核怎样将任务和控制组关联起来了:
利用task_struct结构中的cgroup数据项找到该任务关联的css_set结构,然后通过css_set结构找到和css_set关联的所有cg_link_list结构,最后根据cg_link_list结构中的cgrp指针找到任务关联的控制组,这个查找过程看起来没有直接关联起来的方便,但是这样的设计可减少task_struct和cgroup的耦合度,并且还可减少很多的数据冗余.不知道你是否还记得数据库中的范式理论,cg_link_list的实际就是为了解除数据冗余而设计的(好长时间没看数据库了,到底是第几范式,记不清楚了,如果你知道的话,请你告诉我......^_^).

反过来通过cgroup查找相应的task_struct也不再是困难的事情,根据cgroup中的css_sets数据项,可以找到所有关联到控制组的cg_link_list结构,通过cg_link_list结构中的cg可找到关联的css_set结构,最后根据css_set结构的tasks数据项找到关联到控制组的所有任务(交织在一起的双向链表,你可以理清楚的..).

需要提一下的是css_set结构中的tasks为所有关联到css_set结构的任务链表头,想必task_struct中cg_list的作用你应该也知道了,它就是连接到css_set中tasks链表的节点.


2.3 控制组与子系统的关联
将任务关联到控制组后,就可以通过连接到控制组的子系统对任务进行控制了.从数据结构上说找到和控制组关联的子系统是一件很容易的事情,现在回头看看cgroup结构中的subsys结构,它保存了cgroup_subsys_state结构的指针数组,根据cgroup_subsys_state的名称可以推测它表示了控制组子系统的状态信息,也许通过它我们可以知道很多关于子系统的信息,在源码面前所有的秘密都不再是秘密,来看看cgroup_subsys_state结构的定义吧!
struct cgrouop_subsys_state{
	struct cgroup *cgroup;//子系统关联到的控制组指针
	atomic_t refcnt;//引用计数>0表示忙碌
	unsigned long flags;//标记信息
	struct css_id __rcu *id;//css ID
};

这个数据结构也许会让你失望,通过它并没有看到什么子系统的有效信息.该结构似乎真有些名不副实,没有提供什么有用的信息,除了该结构中包含了指向控制组的指针和一些无关痛痒的数据项.

好吧,打起精神,不在这里故弄玄虚了,还是看看cgroup_subsys结构的真正定义吧!
struct cgroup_subsys{
	struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	int (*pre_destroy)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	void (*destroy)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	int (*can_attach)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	int (*can_attach_task)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	void (*cancel_attach)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	void (*pre_attach)(struct cgroup *cgrp);
	void (*attach_task)(struct cgroup *cgrp,struct task_struct *tsk);
	void (*attach)(struct cgroup_subsys *ss,struct cgroup *cgrp,struct cgroup *old_group,struct task_struct *tsk);
	void (*fork)(struct cgroup_subsys *ss,struct task_struct *task);
	void (*exit)(struct cgroup_subsys *ss,struct cgroup *cgrp,struct cgroup *old_cgrp,struct task_struct *task);
	int (*populate)(struct cgroup_subsys *ss,struct cgroup *cgrp);
	void (*bind)(struct cgroup_subsys *ss,struct cgroup *root);
	int subsys_id;
	int active;
	int disabled;
	int early_init;
	bool use_id;
#define MAX_CGROUP_TYPE_NAMELEN 32
	const char *name;
	struct mutex hierarchy_mutex;
	struct lock_class_key subsys_key;
	struct cgroupfs_root *root;
	struct list_head sibling;
	struct idr idr;
	spinlock_t id_lock;
	struct module *module;
};

密密麻麻的一个结构体,如果你是第一次看到该结构的话,我想你会和我一样头都大了(^_^!),这是正常的事情.开始的时候没有必要搞清楚所有的事情,只需要知道当前所需的东西就可以了.后面遇到的时候还可以回头仔细的研究,至少这样不会打击自信心,而且回头看时会感觉比开始的时候容易了很多.在当前需要关注的只有create,destroy,populate和subsys_id.从名字上我们可以知道create和destroy一定是创造和销毁了什么东西,从create函数指针的返回值可以知道它返回了一个struct cgroup_subsys_state结构的指针,而对于cgroup_subsys_state结构已经清楚了解,通过它可找到关联控制组.到这里你也许已经看出了cgroup_subsys_state结构的作用,子系统可以调用create指向的函数,该函数会创建一个cgroup_subsys_state对象,该对象将控制组和子系统关联起来.由于该对象是由子系统创建的,显然子系统可对其进行很好的控制.现在可确信的说cgroup_subsys_state扮演了将每个控制组和子系统关联起来的纽带,为什么说是每个控制组呢?在后面介绍子系统实现实例的时候进行介绍.populate函数指针是做什么的呢?一般情况下我们使用该函数来创建控制文件,你也许会问什么是控制文件呢?好问题,我会在下面进行一些的分析解答.subsys_id很容易理解,该项表示了子系统的id,借助它就可以很方便的管理子系统了.

2.4 任务和子系统的关联
为了方便的将任务和子系统关联起来,内核在css_set结构中定义了subsys指针数组来指向css_set关联的子系统,因此task_struct可以借助于css_set结构来快速的定位关联的子系统,内核之所有没有直接把subsys指针数组放在task_struct结构中是为了节省空间和便于管理,因为如果每个task_struct均保持subsys的备份,显然在控制组和子系统链接情况发生变化的时候,可能需要修改所有的task_struct结构,并且很容易发生多个副本不一致的情况.

2.5 控制文件
内核采用控制文件和用户空间交换控制信息,而不是提供操作API.用户通过读写控制文件对控制组信息进行管理.从用户的观点来看,控制文件和其他的文件没有任何的区别,可采用cat或者echo命令对其进行读写.在结构上控制文件的定义不算复杂,主要定义了用户对控制文件进行读写操作的接口以及文件名等信息.
struct cftype{
	char name[MAX_CFTYPE_NAME];//控制文件名,需要主要的是导出的控制文件明以子系统名和点号开头
	int private;//私有数据
	mode_t mode;//文件权限
	size_t max_write_len;//定义允许写入的最大长度,默认为64
	int (*open)(struct inode *inode,struct file *file);//打开文件处理例程
	//读操作
	ssize_t (*read)(struct cgroup *cgrp,struct cftype *cft,struct file *file,char __user *buf,size_t nbytes, loff_t *ppos);
	//读取64位无符号数
	u64 (*read_u64)(struct cgroup *cgrp,struct cftype *cft);
	//读取64位有符号数
	s64 (*read_s64)(struct cgroup *cgrp,struct cftype *cft);
	//定义一个key/value对
	int (*read_map)(struct *cont,struct cftype *cft,struct cgroup_map_cb *cb);
	//从序列文件中读取一行
	int (*read_seq_string)(struct cgroup *cont,struct cftype *cft,struct seq_file *m);
	//写文件操作
	ssize_t (*write)(struct cgroup *cgrp,struct cftype *cft,struct file *file,const char __user *buf,size_t nbytes,loff_t *ppos);
	//写64位无符号整数
	int (*write_u64)(struct cgroup *cgrp,struct cftype *cft, u64 val);
	//写64位有符号整数
	int (*write_s64)(struct cgroup *cgrp,struct cftype *cft, s64 val);
	//写入一个字符串
	int (*write_string)(struct cgroup *cgrp,struct cftype *cft,const char *buffer);
	int (*trigger)(struct cgroup *cgrp, unsigned int event);//暂时忽略
	int (*release)(struct inode *indoe, struct file *file);//暂时忽略
	int (*register_event)(struct cgroup *cgrp,struct cftype *cft,struct eventfd_ctx *eventfd,const char * args);//暂时忽略
	void (*unregister_event)(struct cgroup *cgrp,struct cftype *cft,struct eventfd_ctx *eventfd);//暂时忽略	
};

根据上面的结构可知道该结构主要定义了控制文件名、访问权限以及读写操作等接口,现只对控制文件名以及控制文件的读写接口进行简要的介绍,其他数据项在遇到的时候再详细介绍(我不想一开始就把事情弄的很复杂).控制组文件系统导出的控制文件名采用:子系统名称+.+name的方式导出,比如子系统sample定义了名称为hello的控制文件,那么该控制文件在控制组文件系统中的文件名称为:"sample.hello".有一点需要注意的是cftype定义的读写操作接口均有优先级,系统会根据优先级调用相应的读写函数,因此在实现子系统控制文件读写操作时只需实现需要的读写方式即可.
读接口的优先级为:read>read_u64>read_s64
写接口的优先级为:write>write_u64>write_s64>write_string.
比如在某个子系统实现中你同时实现了read和read_u64方法,那么当你在读取控制文件时,系统只会调用read而忽略read_u64的实现,读操作亦然.一般在子系统接口populate中使用来创建控制文件,在后面分析实现实例时进行介绍.


3. 子系统实现实例
为使大家对子系统有个直观的认识,下面对内核中devices subsys的实现进行简单剖析,然后根据剖析的结果,编写一个简单的没有任何实际意义的子系统模块来加深大家对子系统的认识.看了前面的描述大家应该对控制组有了初步的认识,虽然内核对控制组文件系统和子系统的管理有些复杂(至少要比我所描述的要复杂很多),但是编写子系统模块的确是一件比较容易的事情.当然如果想要设计出有实际使用意义的子系统模仍旧是一件很有挑战性的工作,这其中主要的难点在于怎样合理的设计以及在内核中增加hook来实现设计的功能.也许你现在已经跃跃欲试设计自己的子系统,但是在此之前还是耐心的把下面的文字看完.devices subsys通过对内核进行很小的修改实现了设备白名单机制(块设备和字符设备),如果你希望你的子系统可以加入到最新的内核,那你就朝着devices的设计努力吧.请注意在这里我并不会对所有代码进行详细细致的介绍,只对模块的结构和在内核中增加hook的代码进行介绍,详细信息可参考内核源代码.
根据前面的介绍,要实现devices subsys包含如下几个方面。
1) 首先需要做的是定义设备子系统实例:
extern int devices_subsys_id;//子系统ID
struct cgroup_subsys devices_subsys={
	.name = "devices",
	.can_attach = devcgroup_can_attach,
	.create = devcgroup_create,
	.destroy = devcgroup_destroy,
	.populate = devcgroup_populate,
	.subsys_id = devices_subsys_id,
};

从上面的定义可知道devices子系统系统实现了接口can_attach,create,populate,create和destroy.

2) 定义描述设备白名单的数据结构和子系统状态实例数据结构
//白名单条目结构
struct devices_whitelist_item{
	u32 major,minor;//设备主从设备号
	short type;//设备类型(块设备或者字符设备)
	short access;//允许的权限(读,写和mknod)
	struct list_head list;//设备白名单条目链表节点
	struct rcu_head rcu;//rcu锁,暂时忽略
};
//设备白名单子系统状态实例数据结构
struct dev_cgroup{
	struct cgroup_subsys_state css;
	struct list_head whitelist;
};

从上面的两个数据结构可知道子系统实例数据结构中包含了控制组子系统状态项和白名单条目链表头指针,通过将css结构定义在dev_cgroup中,利用container_of宏可以很容易的根据task_struct或者cgroup结构计算出dev_cgroup结构实例.container_of的具体实现见附录.转化函数如下:
static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s){
	return container_of(s,struce dev_cgroup,css);
}
static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgrp){
	return css_to_devcgroup(cgroup_subsys_state(cgroup,devices_subsys_id));
}
static inline struct dev_cgroup *task_to_devcgroup(struct task_struct *task){
	return css_to_devcgroup(task_subsys_state(task,devices_subsys_id));
}

3) 编写代码实现devcgroup_can_attach,devcgroup_create,devcgroup_destroy,dev_cgroup_populate函数

cgroup_subsys中接口can_attach在将一个任务移动到控制组之前调用,如果该接口返回错误则退出链接操作,devcgroup_can_attach主要进行权限检查,只有任务自身或者具有管理权限的任务才具有将任务移动到控制组.
static int devcgroup_can_attach(struct cgroup_subsys *ss,struct cgroup *new_cgroup,struct task_struct *task){
	if(current != task && !capable(CAP_SYS_ADMIN))
		return -EPERM;
	return 0;
}


cgroup_subsys中接口create在创建控制组时被调用,用于为控制组分配一个cgroup_subsys_state对象以便于将控制组和子系统联系起来.其实现代码主要包括:分配一个dev_cgroup结构,并且初始化设备白名单,如果控制组为根控制组,则将设备白名单初始化为对所有的设备具有所有的权限;否则继承父控制组的设备白名单.
static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,struct cgroup *cgroup){
	struct dev_cgroup *dev_cgroup,*parent_dev_cgroup;
	struct cgroup *parent_cgroup;
	int ret;
	dev_cgroup = kmalloc(sizeof(*dev_cgroup),GFP_KERNEL);
	if(!dev_cgroup)
		return ERR_PTR(_ENOMEM);
	INIT_LIST_HEAD(&dev_cgroup->whitelist);
	parent_cgroup = cgroup->parent;
	if(!parent_cgroup){//根控制组,允许所有设备的所有权限
		struct dev_whitelist_item *wh;
		wh = kmalloc(sizeof(*wh,GFP_KERNEL);
		if(!wh){
			kfree(dev_cgroup);
			return ERR_PTR(-ENOMEM);
		}
		wh->minor = wh->major = ~0;//0xFFFFFFFF表示所有设备号
		wh->access = ACC_MASK;//给你所有的权限,挂根的东西总会有额外的照顾,o(∩∩)o...
		wh->type = DEV_ALL;//所有类型的设备
		//添加到白名单中
		list_add(&wh->list,&dev_cgroup->whitelist);
	} else {
		//获取父控制组白名单子系统结构实例
		parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
		mutex_lock(&devcgroup_mutex);//获得锁定
		//复制白名单
		ret = dev_whitelist_copy(&dev_cgroup->whitelist,&parent_dev_cgroup->whitelist);
		mutex_unlock(&devcgroup_mutex);//释放锁
		if(ret){
			kfrer(dev_cgroup);
			return ERR_PTR(ret);
		}
	}
	return &dev_cgroup->css;//返回和控制组cgroup关联的cgroup_subsys_state对象
}


cgroup_subsys中接口destroy用户在删除控制组时调用进行子系统相关的清理工作,devcgroup_destroy主要进行释放申请的dev_cgroup空间以及白名单列表.
static void devcgroup_destroy(struct cgroup_subsys *ss,struct cgroup *cgroup){
	struct dev_cgroup *cgroup ;
	struct dev_whitelist_item *wh,*tmp;
	dev_cgroup = cgroup_to_devcgroup(cgroup);//获取和控制组cgroup关联的dev_cgroup对象指针
	//释放白名单条目
	list_for_each_entry_safe(wh,tmp,&dev_cgroup->whitelist,list){
		list_del(&wh->list);
		kfree(wh);
	}
	kfree(dev_cgroup);//释放dev_cgroup结构
}


cgroup_subsys中接口populate主要在控制组创建完成后调用,主要用于在控制组目录中添加需要的控制文件.
static int devcgroup_populate(struct cgroup_subsys *ss,struct cgroup *cgroup){
	return cgroup_add_files(cgroup,ss,dev_cgroup_files,ARRAY_SIZE(dev_cgroup_files));//添加控制文件
}


4) 定义控制文件结构用于在控制组文件夹中创建控制文件.
static cftype dev_cgroup_files[]={
	{
		.name = "allow",
		.write_string = devcgroup_access_write,
		.private = DEVCG_ALLOW,
	},
	{
		.name = "deny",
		.write_string = devcgroup_access_write,
		.private = DEVCG_DENY,
	},
	{
		.name = "list",
		.read_seq_string = devcgroup_seq_read,
		.private = DEVCG_LIST,
	},
};

devices子系统定义了三个控制文件devices.allow,devices.deny和devices.list以及相关读写接口,接口的实现不做论述,可参考内核代码.

5) 定义hook供其在其他内核模块调用.
devices定义了两个hook函数用于检查控制组是否对给定的设备具有相应的权限,这两个函数的实现均通过检索白名单列表来确定是否具有相应的权限,故在此就不给出代码,仅给出这两个函数的声明以及在内核其他模块的调用点.
权限检查函数:
//检查是否具有设备dev的权限mod
int devcgroup_inode_mknod(int mod, dev_t dev);
//检查inode节点是否具有权限mod
int devcgroup_inode_permission(struct inode *inode,int mask);
这两个hook分别在文件系统中创建或者访问的时候调用进行权限检查,其调用点为:
int inode_permission(struct inode *inode,int mask);//fs/namei.c
static int __blkdev_get(struct block_device *bdev,fmode_t mode,int for_part);//fs/block_dev.c
int vfs_mknod(struct inode *dir,struct dentry *dentry,int mode,dev_t dev);//fs/namei.c
该子系统只在内核其他模块中增加了3行代码就实现了设备白名单,感慨一下内核设计者的独到设计~~~~.


4. 编写简单子系统模块
根据上一节对devices子系统实现的分析,我们可以按照其格式实现一个简单的子系统,在下面的子系统中不能作任何有意的事情,仅仅用于对子系统的认识,详情参考hello.c

5. 附录
在第3节中遇到了contailer_of宏用于通过数据结构中某一数据项的地址来计算数据项所在数据结构的地址,其定义如下:
#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})


结束语
本文简要介绍了控制组相关的主要数据结构以及这些数据结构的相互关系.内核将控制组组织成一个或者多个树形层次结构,并通过控制组文件系统对控制组进行管理。为使用户空间可与控制组交互通信,控制组文件系统提供了控制文件机制,用户可通过读写控制文件来实现对控制组的操作.此外,本文还简要介绍了子系统的初步实现框架,并剖析了子系统中比较容易理解的设备白名单子系统.最后给出了一个简单的子系统模块实现.本文只是浅显的介绍控制组相关知识,没有对控制组文件系统进行详细的介绍,在后续的工作中还会对其进行进一步的分析.最后,上面的文字可能会存在错误或者不当的分析,希望大家可以批评指正.

冗长的论述有时会让人厌烦,希望您可以忍受我杂乱无章的表述,good luck!

0
2
分享到:
评论

相关推荐

    课程设计多路彩灯控制器设计

    经过分析问题及初步的整体思考,拟定以下二种方案: 方案一:总体电路共分三大块。第一块实现花型的演示;第二块实现花型的控制及节拍控制;第三块实现时钟信号的产生。 主体框图如下: 方案二:在方案一的...

    基于arduino的android蓝牙控制电器

    1、实验目的:初步实现手机对家居的远程控制,可以基本简化为手机通过软件对开关的控制,对电机的控制,以及感应器将感应到的信息反馈给手机,在通过一定的算法对数据进行分析,这便是一个简单的未来家居雏形。...

    JX001机械手自动化控制系统的PLC实现方法研究

    JX001机械手自动化控制系统的PLC实现方法研究 JX002汽车连杆加工工艺及夹具设计 JX003舵轮槽轮式穴播器设计 JX004房屋建筑设计 JX005工厂化海水养鱼循环系统的工艺流程研究 JX006滑轮轴的设计 JX007基于MATLAB的电力...

    深入分析Linux内核源码

    10.2.2 实现机制的分析 10.3 模块的装入和卸载 10.3.1 实现机制 10.3.2 如何插入和卸载模块 10.4 内核版本 10.4.1 内核版本与模块版本的兼容性 10.4.2 从版本2.0到2.2内核API的变化 10.4.3 把内核2.2移植到...

    C++ Qt 创建多线程,控制6组ABB输出到UI界面上,使用moveToThread方式,配合互斥锁条件变量代码示例

    共6组数,有6个A,12个B,所以一开始可以很直白的创建2个槽函数,来分别输出A或者B就可以初步效果了,加上互斥锁和条件变量的配合,使用一个字符计数器来全局统计一下,将计数器对3取余,判断余数就可以轻松实现效果...

    网络工程师考试考点分析与真题详解.网络设计与管理篇

    5 1.3 需求分析 8 1.3.1 需求分析的基本任务和基本原则 8 1.3.2 初步需求获取技术 9 1.3.3 收集需求的方法及产生需求歧义性的主要原因 9 1.3.4 需求分析的主要技术指标 10 1.3.5 需求规格说明与...

    微机原理课程设计—抢答器

    由于本次实验要用到数码显示、中断控制、倒计时等功能,故初步分析,需要8255、8253等芯片。当4组均未按下抢答按钮时,送入到并行接口的4位抢答状态都是0,而当微机采样到这4位数据不为0时,则表示有一组获得了抢答机会...

    语音识别的MATLAB实现

    鉴于上诉各种要求,我们决定对购买的遥控小车进行简单改造,使用PC机已有的硬件条件编写软件来完成语音的输入,采集,处理和识别,以实现对小车的控制。 三、 解决思路与模块: 整个程序大致可划分为三个模块,其...

    网络工程师考试考点分析与真题详解.网络设计与管理篇.part5

    5 1.3 需求分析 8 1.3.1 需求分析的基本任务和基本原则 8 1.3.2 初步需求获取技术 9 1.3.3 收集需求的方法及产生需求歧义性的主要原因 9 1.3.4 需求分析的主要技术指标 10 1.3.5 需求规格说明与...

    非常详细的MATLAB使用教程-初步入门大全

    MATLAB是美国MathWorks公司出品的商业数学软件,用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人,控制系统等领域。 MATLAB是matrix&laboratory两个词的组合,意为...

    网络工程师考试考点分析与真题详解.网络设计与管理篇.part4

    5 1.3 需求分析 8 1.3.1 需求分析的基本任务和基本原则 8 1.3.2 初步需求获取技术 9 1.3.3 收集需求的方法及产生需求歧义性的主要原因 9 1.3.4 需求分析的主要技术指标 10 1.3.5 需求规格说明与...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    4.3 数据输入输出的概念及在 C 语言中的实现 54 4.4 字符数据的输入输出 54 4.4.1 putchar 函数(字符输出函数) 54 4.4.2 getchar函数(键盘输入函数) 55 4.5 格式输入与输出 55 4.5.1 printf 函数(格式输出函数...

    遗传算法 理论、应用及软件实现.pdf

    第4章分析了遗传算法的多种改进方法;第5章初步介绍了进货计算理论体系;第6章介绍了遗传算法应用于数值优化问题;第7章介绍了遗传算法应用于组合优化问题;第8章介绍了遗传算法应用于机器学习;第9章讨论了遗传算法...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    4.3 数据输入输出的概念及在 C 语言中的实现 54 4.4 字符数据的输入输出 54 4.4.1 putchar 函数(字符输出函数) 54 4.4.2 getchar函数(键盘输入函数) 55 4.5 格式输入与输出 55 4.5.1 printf 函数(格式输出函数...

    VC与Labview、Matlab编程论文资料[2].rar

    VC与Matlab混合编程实现成组图像边缘的连续提取.pdf VC与MATLAB混合编程实现方法及具体实例研究.pdf VC与Matlab混合编程的研究与实现.pdf VC与STK的集成及在****数字可视化仿真中的应用.pdf VC中应用MSComm控件...

    VC与Labview、Matlab编程论文资料

    VC与Matlab混合编程实现成组图像边缘的连续提取.pdf VC与MATLAB混合编程实现方法及具体实例研究.pdf VC与Matlab混合编程的研究与实现.pdf VC与STK的集成及在****数字可视化仿真中的应用.pdf VC中应用MSComm控件...

    VC与Labview、Matlab编程论文资料[4].rar

    VC与Matlab混合编程实现成组图像边缘的连续提取.pdf VC与MATLAB混合编程实现方法及具体实例研究.pdf VC与Matlab混合编程的研究与实现.pdf VC与STK的集成及在****数字可视化仿真中的应用.pdf VC中应用MSComm控件...

Global site tag (gtag.js) - Google Analytics