Linux Cgroups詳解(六)


freezer子系統

freezer子系統用於掛起和恢復cgroup中的進程。freezer有一個控制文件:freezer.state,將FROZEN寫入該文件,可以將cgroup中的進程掛起,將THAWED寫入該文件,可以將已掛起的進程恢復。該文件可能讀出的值有三種,其中兩種就是前面已提到的FROZEN和THAWED,分別代表進程已掛起和已恢復(正常運行),還有一種可能的值為FREEZING,顯示該值表示該cgroup中有些進程現在不能被frozen。當這些不能被frozen的進程從該cgroup中消失的時候,FREEZING會變成FROZEN,或者手動將FROZENTHAWED寫入一次。

Freezer子系統用來管理cgroup狀態的數據結構:

struct freezer {

struct cgroup_subsys_state css;

enum freezer_state state;

spinlock_t lock; /* protects _writes_ to state */

};

其中內嵌一個cgroup_subsys_state,便於從cgrouptask獲得freezer結構,另一個字段存儲cgroup當前的狀態。

Freezer子系統是通過對freezer.state文件進行寫入來控制進程的,那我們就從這個文件的cftype定義出發。

static struct cftype files[] = {

{

.name = "state",

.read_seq_string = freezer_read,

.write_string = freezer_write,

},

};

從文件讀取是freezer_read實現的,該函數比較簡單,主要就是從freezer結構體從讀出狀態,但是對FREEZING狀態做了特殊處理:

state = freezer->state;

if (state == CGROUP_FREEZING) {

/* We change from FREEZING to FROZEN lazily if the cgroup was

 * only partially frozen when we exitted write. */

update_freezer_state(cgroup, freezer);

state = freezer->state;

}

如果是FREEZING狀態,則需要更新狀態(因為之前不能frozen的進程可能已經不在了)。我們來看update_freezer_state:

cgroup_iter_start(cgroup, &it);

while ((task = cgroup_iter_next(cgroup, &it))) {

ntotal++;

if (is_task_frozen_enough(task))

nfrozen++;

}

 

/*

 * Transition to FROZEN when no new tasks can be added ensures

 * that we never exist in the FROZEN state while there are unfrozen

 * tasks.

 */

if (nfrozen == ntotal)

freezer->state = CGROUP_FROZEN;

else if (nfrozen > 0)

freezer->state = CGROUP_FREEZING;

else

freezer->state = CGROUP_THAWED;

cgroup_iter_end(cgroup, &it);

這里對該cgroup所有的進程迭代了一遍,分別統計進程數和已經frozen的進程數,然后根據統計結果改變狀態。

下面我們來看對freezer.state寫入的情況,該情況由freezer_write來處理,該函數中從寫入值獲取目標狀態,然后調用freezer_change_state(cgroup, goal_state)來完成操作。在freezer_change_state中,根據goal_state分別調用不同的實現函數:

switch (goal_state) {

case CGROUP_THAWED:

unfreeze_cgroup(cgroup, freezer);

break;

case CGROUP_FROZEN:

retval = try_to_freeze_cgroup(cgroup, freezer);

break;

default:

BUG();

}

我們先來看frozen的情況,該情況由try_to_freeze_cgroup來處理,該函數中有:

freezer->state = CGROUP_FREEZING;

cgroup_iter_start(cgroup, &it);

while ((task = cgroup_iter_next(cgroup, &it))) {

if (!freeze_task(task, true))

continue;

if (is_task_frozen_enough(task))

continue;

if (!freezing(task) && !freezer_should_skip(task))

num_cant_freeze_now++;

}

cgroup_iter_end(cgroup, &it);

 

return num_cant_freeze_now ? -EBUSY : 0;

首先將當前狀態設成CGROUP_FREEZING,然后對cgroup中的進程進行迭代,while循環中對進程進行freeze操作,如果成功直接進行下一次迭代,如果不成功則進行進一步的判斷,如果是進程已經frozen了,那也直接進行下一次迭代,如果不是,則進行計數。最后根據計數結果進行返回,如果所有進程都順利frozen,則返回0,否則返回-EBUSY表示有進程不能被frozen

下面我們來看free_task這個函數,在這個函數中對task進行freeze操作。

if (!freezing(p)) {

rmb();

if (frozen(p))

return false;

 

if (!sig_only || should_send_signal(p))

set_freeze_flag(p);

else

return false;

}

 

if (should_send_signal(p)) {

if (!signal_pending(p))

fake_signal_wake_up(p);

} else if (sig_only) {

return false;

} else {

wake_up_state(p, TASK_INTERRUPTIBLE);

}

 

return true;

首先檢查進程是不是已經被標記為正在freezing,如果不是再做判斷。如果進程已經被frozen,則返回false。如果進程不是sig_only的或者可以發送信號(即進程無PF_FREEZER_NOSIG 標記),則設置進程的TIF_FREEZE標記。

然后根據進程是否有PF_FREEZER_NOSIG 標記進行進一步處理,如果無這個標記,則給進程發送一個信號,喚醒進程,讓進程處理TIF_FREEZE,即進行freeze操作,如果有這個標記,則如果進程是sig_only的,返回false(即不能完成free操作),否則直接喚醒進程去處理TIF_FREEZE

總結一下,對於我們這個freezer子系統的調用來說,sig_only=true,那么能成功的執行過程就是set_freeze_flag(p)->fake_signal_wake_up(p)。

下面我們來看thaw 進程的情況,該情況由unfreeze_cgroup處理,在unfreeze_cgroup中有

cgroup_iter_start(cgroup, &it);

while ((task = cgroup_iter_next(cgroup, &it))) {

thaw_process(task);

}

cgroup_iter_end(cgroup, &it);

 

freezer->state = CGROUP_THAWED;

對該cgroup中所有的進程調用thaw_process,我們來看thaw_process。該函數中有:

if (__thaw_process(p) == 1) {

task_unlock(p);

wake_up_process(p);

return 1;

}

其中__thaw_process

if (frozen(p)) {

p->flags &= ~PF_FROZEN;

return 1;

}

clear_freeze_flag(p);

如果進程已經frozen,則清掉其frozen標記,如果不是的話,說明進程已經設置了TIF_FREEZE,但還沒有frozen,所以只需要清掉TIF_FREEZE即可。

回到thaw_process中,清掉了相關標記后,只需要喚醒進程,然后內核會自動處理。

最后,我們再來看看freezer子系統結構體的定義:

struct cgroup_subsys freezer_subsys = {

.name = "freezer",

.create = freezer_create,

.destroy = freezer_destroy,

.populate = freezer_populate,

.subsys_id = freezer_subsys_id,

.can_attach = freezer_can_attach,

.attach = NULL,

.fork = freezer_fork,

.exit = NULL,

};

這里說一下can_attach,can_attach是在一個進程加入到一個cgroup之前調用的,檢查是否可以attachfreezer_can_attach中對cgroup當前的狀態做了檢查,如果是frozen就返回錯誤,這說明不能將一個進程加入到一個frozencgroup


免責聲明!

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



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