Cgroup文件系統
Cgroups用戶空間管理
Cgroups用戶空間的管理是通過cgroup文件系統實現的。
比如要創建一個層級:
mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
這個命令就創建一個名為cpu_and_mem的層級,這個層級上附加了cpu,cpuset,memory三個子系統,並把層級掛載到了/cgroup/cpu_and_mem.
創建一個cgroup:
cd /cgroup/cpu_and_mem
mkdir foo
通過以上兩個命令,我們就在剛才創建的層級下創建了一個叫foo的cgroup。
你再cd foo,然后ls
你會發現一些文件,這是cgroups相關子系統的控制文件,你可以讀取這些控制文件,這些控制文件存儲的值就是對相應的cgrouop的控制信息,你也可以寫控制文件來更改控制信息。
在這些文件中,有一個叫tasks的文件,里面的包含了所有屬於這個cgroup的進程的進程號。
在剛才創建的foo下,你cat tasks,應該是空的,因為此時這個cgroup里面還沒有進程。你cd /cgroup/cpu_and_mem 再cat tasks,你可以看到系統中所有進程的進程號,這是因為每創建一個層級的時候,系統的所有進程都會自動被加到該層級的根cgroup里面。Tasks文件不僅可以讀,還可以寫,你將一個進程的進程號寫入到某個cgroup目錄下的tasks里面,你就將這個進程加入了相應的cgroup。
Cgroup文件系統的實現
在講cgroup文件系統的實現之前,必須簡單的介紹一下Linux VFS。
VFS是所謂的虛擬文件系統轉換,是一個內核軟件層,用來處理與Unix標准文件系統的所有系統調用。VFS對用戶提供統一的讀寫等文件操作調用接口,當用戶調用讀寫等函數時,內核則調用特定的文件系統實現。具體而言,文件在內核內存中是一個file數據結構來表示的。這個數據結構包含一個f_op的字段,該字段中包含了一組指向特定文件系統實現的函數指針。當用戶執行read()操作時,內核調用sys_read(),然后sys_read()查找到指向該文件屬於的文件系統的讀函數指針,並調用它,即file->f_op->read().
VFS其實是面向對象的,在這里,對象是一個軟件結構,既定義數據也定義了之上的操作。處於效率,Linux並沒有采用C++之類的面向對象的語言,而是采用了C的結構體,然后在結構體里面定義了一系列函數指針,這些函數指針對應於對象的方法。
VFS文件系統定義了以下對象模型:
超級塊對象(superblock object)
存放已安裝文件系統的有關信息。
索引節點對象(inode object)
存放關於具體文件的一般信息。
文件對象(file object)
存放打開文件與進程之間的交互信息
目錄項對象(dentry object)
存放目錄項與對應文件進行鏈接的有關信息。
基於VFS實現的文件系統,都必須實現定義這些對象,並實現這些對象中定義的函數指針。cgroup文件系統也不例外,下面我們來看cgroups中這些對象的定義。
cgroup文件系統的定義:
static struct file_system_type cgroup_fs_type = {
.name = "cgroup",
.get_sb = cgroup_get_sb,
.kill_sb = cgroup_kill_sb,
};
這里有定義了兩個函數指針,定義了一個文件系統必須實現了的兩個操作get_sb,kill_sb,即獲得超級塊和釋放超級塊。這兩個操作會在使用mount系統調用掛載cgroup文件系統時使用。
cgroup 超級塊的定義:
static const struct super_operations cgroup_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.show_options = cgroup_show_options,
.remount_fs = cgroup_remount,
};
Cgroup 索引塊定義:
static const struct inode_operations cgroup_dir_inode_operations = {
.lookup = simple_lookup,
.mkdir = cgroup_mkdir,
.rmdir = cgroup_rmdir,
.rename = cgroup_rename,
};
在cgroup文件系統中,使用mkdir創建cgroup或者用rmdir刪除cgroup時,就會調用相應的函數指針指向的函數。比如:使用mkdir創建cgroup時,會調用cgroup_mkdir,然后在cgroup_mkdir中再調用具體實現的cgroup_create函數。
Cgroup 文件操作定義:
static const struct file_operations cgroup_file_operations = {
.read = cgroup_file_read,
.write = cgroup_file_write,
.llseek = generic_file_llseek,
.open = cgroup_file_open,
.release = cgroup_file_release,
};
在cgroup文件系統中,對目錄下的控制文件進行操作時,會調用該結構體中指針指向的函數。比如:對文件進行讀操作時,會調用cgroup_file_read,在cgroup_file_read中,會根據需要調用該文件對應的cftype結構體定義的對應讀函數。
我們再來看cgroup文件系統中的cgroups控制文件。Cgroups定義一個cftype的結構體來管理控制文件。下面我們來看cftype的定義:
struct cftype {
char name[MAX_CFTYPE_NAME];
int private; /*
mode_t mode;
size_t max_write_len;
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);
u64 (*read_u64)(struct cgroup *cgrp, struct cftype *cft);
s64 (*read_s64)(struct cgroup *cgrp, struct cftype *cft);
int (*read_map)(struct cgroup *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);
int (*write_u64)(struct cgroup *cgrp, struct cftype *cft, u64 val);
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 *inode, 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);
};
cftype中除了定義文件的名字和相關權限標記外,主要是定義了對文件進行操作的函數指針。不同的文件可以有不同的操作,對文件進行操作時,相關函數指針指向的函數會被調用。
綜合上面的分析,cgroups通過實現cgroup文件系統來為用戶提供管理cgroup的工具,而cgroup文件系統是基於Linux VFS實現的。相應地,cgroups為控制文件定義了相應的數據結構cftype,對其操作由cgroup文件系統定義的通過操作捕獲,再調用cftype定義的具體實現。