Linux Cgroups詳解(五)


devices子系統

    使用devices 子系統可以允許或者拒絕cgroup中的進程訪問設備。devices子系統有三個控制文件:devices.allow,devices.deny,devices.list。devices.allow用於指定cgroup中的進程可以訪問的設備,devices.deny用於指定cgroup中的進程不能訪問的設備,devices.list用於報告cgroup中的進程訪問的設備。devices.allow文件中包含若干條目,每個條目有四個字段:typemajorminor 和 accesstypemajor 和 minor 字段中使用的值對應 Linux 分配的設備。

type指定設備類型:

a - 應用所有設備,可以是字符設備,也可以是塊設備

b- 指定塊設備

c - 指定字符設備

majorminor指定設備的主次設備號。

access 則指定相應的權限:

r - 允許任務從指定設備中讀取

w - 允許任務寫入指定設備

m - 允許任務生成還不存在的設備文件

devices子系統是通過提供device whilelist 來實現的。與其他子系統一樣,devices子系統也有一個內嵌了cgroup_subsystem_state的結構來管理資源。在devices子系統中,這個結構是:

struct dev_cgroup {

struct cgroup_subsys_state css;

struct list_head whitelist;

};

這個結構體除了通用的cgroup_subsystem_state之外,就只有一個鏈表指針,而這個鏈表指針指向了該cgroup中的進程可以訪問的devices whilelist

下面我們來看一下devices子系統如何管理whilelist。在devices子系統中,定義了一個叫dev_whitelist_item的結構來管理可以訪問的device,對應於devices.allow中的一個條目。這個結構體的定義如下:

struct dev_whitelist_item {

u32 major, minor;

short type;

short access;

struct list_head list;

struct rcu_head rcu;

};

majorminor用於指定設備的主次設備號,type用於指定設備類型,type取值可以是:#define DEV_BLOCK 1

#define DEV_CHAR  2

#define DEV_ALL   4 

對應於之前devices.allow文件中三種情況。

access用於相應的訪問權限,access取值可以是:

#define ACC_MKNOD 1

#define ACC_READ  2

#define ACC_WRITE 4

也和之前devices.allow文件中的情況對應。

List字段用於將該結構體連到相應的dev_cgroupwhitelist指向的鏈表。

通過以上數據結構,devices子系統就能管理一個cgroup的進程可以訪問的devices了。 光有數據結構還不行,還要有具體實現才行。devices子系統通過實現兩個函數供內核調用來實現控制cgroup中的進程能夠訪問的devices。首先我們來第一個函數:

int devcgroup_inode_permission(struct inode *inode, int mask)

{

struct dev_cgroup *dev_cgroup;

struct dev_whitelist_item *wh;

 

dev_t device = inode->i_rdev;

if (!device)

return 0;

if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))

return 0;

 

rcu_read_lock();

 

dev_cgroup = task_devcgroup(current);

 

list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {

if (wh->type & DEV_ALL)

goto found;

if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))

continue;

if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))

continue;

if (wh->major != ~0 && wh->major != imajor(inode))

continue;

if (wh->minor != ~0 && wh->minor != iminor(inode))

continue;

 

if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))

continue;

if ((mask & MAY_READ) && !(wh->access & ACC_READ))

continue;

found:

rcu_read_unlock();

return 0;

}

 

rcu_read_unlock();

 

return -EPERM;

}

    我們來簡單分析一下這個函數,首先如果該inode對應的不是devices,直接返回0,如果既不是塊設備也不是字符設備,也返回0,因為devices只控制塊設備和字符設備的訪問,其他情況不管。接着獲得當前進程的dev_cgroup,然后在dev_cgroup中whitelist指針的鏈表中查找,如果找到對應設備而且mask指定的權限和設備的權限一致就返回0,如果沒有找到就返回錯誤。

    這個函數是針對inode節點存在的情況,通過對比權限來控制cgroup中的進程能夠訪問的devices。還有一個情況是inode不存在,在這種情況下,一個進程要訪問一個設備就必須通過mknod建立相應的設備文件。為了達到對這種情況的控制,devices子系統導出了第二個函數:

int devcgroup_inode_mknod(int mode, dev_t dev)

{

struct dev_cgroup *dev_cgroup;

struct dev_whitelist_item *wh;

 

if (!S_ISBLK(mode) && !S_ISCHR(mode))

return 0;

 

rcu_read_lock();

 

dev_cgroup = task_devcgroup(current);

 

list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {

if (wh->type & DEV_ALL)

goto found;

if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))

continue;

if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))

continue;

if (wh->major != ~0 && wh->major != MAJOR(dev))

continue;

if (wh->minor != ~0 && wh->minor != MINOR(dev))

continue;

 

if (!(wh->access & ACC_MKNOD))

continue;

found:

rcu_read_unlock();

return 0;

}

 

rcu_read_unlock();

 

return -EPERM;

}

這個函數的實現跟第一個函數類似,這里就不贅述了。

下面我們再來看一下devices子系統本身的一些東西。跟其他子系統一樣,devices同樣實現了一個cgroup_subsys

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相應的三個控制文件:

static struct 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,

},

};

其中allowdeny都是通過devcgroup_access_write實現的,只是通過private字段區分,因為二者的實現邏輯有相同的地方。devcgroup_access_write最終通過調用devcgroup_update_access來實現。在devcgroup_update_access根據寫入的內容構造一個dev_whitelist_item ,然后根據文件類型做不同的處理:

switch (filetype) {

case DEVCG_ALLOW:

if (!parent_has_perm(devcgroup, &wh))

return -EPERM;

return dev_whitelist_add(devcgroup, &wh);

case DEVCG_DENY:

dev_whitelist_rm(devcgroup, &wh);

break;

default:

return -EINVAL;

}

allow的話,就將item加入whitelistdeny的話,就將itemwhitelist中刪去。

作者曰:devices子系統的實現相對比較簡單,通過在內核對設備訪問的時候加入額外的檢查來實現。而devices子系統本身只需要管理好可以訪問的設備列表就行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM