1. 程式人生 > >Linux cgroup機制分析之框架分析

Linux cgroup機制分析之框架分析

八:cgroup中檔案的操作 接下來,就來看cgroup檔案的操作.在上面曾分析到:檔案對應的操作集為cgroup_file_operations.如下所示: static 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, } 7.1:cgrou檔案的open操作 對應的函式為cgroup_file_open().程式碼如下: static int cgroup_file_open(struct inode *inode, struct file *file) {     int err;     struct cftype *cft;     err = generic_file_open(inode, file);     if (err)         return err;     /*取得檔案對應的struct cftype*/     cft = __d_cft(file->f_dentry);     if (!cft)         return -ENODEV;     /*如果定義了read_map或者是read_seq_string*/     if (cft->read_map || cft->read_seq_string) {         struct cgroup_seqfile_state *state =             kzalloc(sizeof(*state), GFP_USER);         if (!state)             return -ENOMEM;         state->cft = cft;         state->cgroup = __d_cgrp(file->f_dentry->d_parent);         file->f_op = &cgroup_seqfile_operations;         err = single_open(file, cgroup_seqfile_show, state);         if (err < 0)             kfree(state);     }     /*否則呼叫cft->open()*/     else if (cft->open)         err = cft->open(inode, file);     else         err = 0;     return err; } 有兩種情況.一種是定義了read_map或者是read_seq_string的情況.這種情況下,它對應的操作集為cgroup_seqfile_operations.如果是其它的情況.呼叫cftype的open()函式.第一種情況,我們等以後遇到了這樣的情況再來詳細分析. 7.2:cgroup檔案的read操作 對應函式為cgroup_file_read().程式碼如下: static ssize_t cgroup_file_read(struct file *file, char __user *buf,                    size_t nbytes, loff_t *ppos) {     struct cftype *cft = __d_cft(file->f_dentry);     struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);     if (!cft || cgroup_is_removed(cgrp))         return -ENODEV;     if (cft->read)         return cft->read(cgrp, cft, file, buf, nbytes, ppos);     if (cft->read_u64)         return cgroup_read_u64(cgrp, cft, file, buf, nbytes, ppos);     if (cft->read_s64)         return cgroup_read_s64(cgrp, cft, file, buf, nbytes, ppos);     return -EINVAL; } 如上程式碼所示.read操作會轉入到cftype的read()或者read_u64或者read_s64的函式中. 7.3:cgroup檔案的wirte操作 對應的操作函式是cgroup_file_write().如下示: static ssize_t cgroup_file_write(struct file *file, const char __user *buf,                         size_t nbytes, loff_t *ppos) {     struct cftype *cft = __d_cft(file->f_dentry);     struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);     if (!cft || cgroup_is_removed(cgrp))         return -ENODEV;     if (cft->write)         return cft->write(cgrp, cft, file, buf, nbytes, ppos);     if (cft->write_u64 || cft->write_s64)         return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos);     if (cft->write_string)         return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos);     if (cft->trigger) {         int ret = cft->trigger(cgrp, (unsigned int)cft->private);         return ret ? ret : nbytes;     }     return -EINVAL; } 從上面可以看到.最終的操作會轉入到cftype的write或者wirte_u64或者wirte_string或者trigger函式中. 7.4:debug subsytem分析 以debug subsystem為例來說明cgroup中的檔案操作 Debug subsys定義如下: struct cgroup_subsys debug_subsys = {     .name = "debug",     .create = debug_create,     .destroy = debug_destroy,     .populate = debug_populate,     .subsys_id = debug_subsys_id, } 在cgroup_init_subsys()中,會以dummytop為引數呼叫debug.create().對應函式為debug_create().程式碼如下: static struct cgroup_subsys_state *debug_create(struct cgroup_subsys *ss,                            struct cgroup *cont) {     struct cgroup_subsys_state *css = kzalloc(sizeof(*css), GFP_KERNEL);     if (!css)         return ERR_PTR(-ENOMEM);     return css; } 這裡沒啥好說的,就是分配了一個cgroup_subsys_state結構. 然後,將cgroup掛載.指令如下: [[email protected] ~]# mount -t cgroup cgroup -o debug /dev/cgroup/ 在rebind_subsystems()中,會呼叫subsys的bind函式.但在debug中無此介面.故不需要考慮. 然後在cgroup_populate_dir()中會呼叫populate介面.對應函式為debug_populate().程式碼如下: static int debug_populate(struct cgroup_subsys *ss, struct cgroup *cont) {     return cgroup_add_files(cont, ss, files, ARRAY_SIZE(files)); } Debug中的files定義如下: static struct cftype files[] =  {     {         .name = "cgroup_refcount",         .read_u64 = cgroup_refcount_read,     },     {         .name = "taskcount",         .read_u64 = taskcount_read,     },     {         .name = "current_css_set",         .read_u64 = current_css_set_read,     },     {         .name = "current_css_set_refcount",         .read_u64 = current_css_set_refcount_read,     },     {         .name = "releasable",         .read_u64 = releasable_read,     }, } 來觀察一下 /dev/cgroup下的檔案: [
[email protected]
~]# tree /dev/cgroup/ /dev/cgroup/ |-- debug.cgroup_refcount |-- debug.current_css_set |-- debug.current_css_set_refcount |-- debug.releasable |-- debug.taskcount |-- notify_on_release |-- release_agent `-- tasks 0 directories, 8 files 上面帶debug字樣的檔案是從debug subsys中建立的.其它的是cgroup.c的files中建立的. 我們先來分析每一個subsys共有的檔案.即tasks,release_agent和notify_on_release. 7.5:task檔案操作 Tasks檔案對應的cftype結構如下: static struct cftype files[] = {     {         .name = "tasks",         .open = cgroup_tasks_open,         .write_u64 = cgroup_tasks_write,         .release = cgroup_tasks_release,         .private = FILE_TASKLIST,     } 7.5.1:task檔案的open操作 當開啟檔案時,流程就會轉入cgroup_tasks_open().程式碼如下: static int cgroup_tasks_open
(struct inode *unused, struct file *file) {     /*取得該檔案所在層次的cgroup*/     struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);     pid_t *pidarray;     int npids;     int retval;     /* Nothing to do for write-only files */     /*如果是隻寫的檔案系統*/     if (!(file->f_mode & FMODE_READ))         return 0;     /*      * If cgroup gets more users after we read count, we won't have      * enough space - tough.  This race is indistinguishable to the      * caller from the case that the additional cgroup users didn't      * show up until sometime later on.      */      /*得到該層cgroup所關聯的程序個數*/     npids = cgroup_task_count(cgrp);     /*為npids個程序的pid存放分配空間*/     pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL);     if (!pidarray)         return -ENOMEM;     /* 將與cgroup關聯程序的pid存放到pid_array_load陣列.       * 並且按照從小到大的順序排列      */     npids = pid_array_load(pidarray, npids, cgrp);     sort(pidarray, npids, sizeof(pid_t), cmppid, NULL);     /*      * Store the array in the cgroup, freeing the old      * array if necessary      */      /* 將npids,pidarray資訊存放到cgroup中.如果cgroup之前        * 就有task_pids.將其佔放的空間釋放        */     down_write(&cgrp->pids_mutex);     kfree(cgrp->tasks_pids);     cgrp->tasks_pids = pidarray;     cgrp->pids_length = npids;     cgrp->pids_use_count++;     up_write(&cgrp->pids_mutex);     /*將檔案對應的操作集更改為cgroup_task_operations*/     file->f_op = &cgroup_tasks_operations;     retval = seq_open
(file, &cgroup_tasks_seq_operations);     /*如果操作失敗,將cgroup中的pid資訊釋放*/     if (retval) {         release_cgroup_pid_array(cgrp);         return retval;     }     ((struct seq_file *)file->private_data)->private = cgrp;     return 0; } 首先,我們來思考一下這個問題:怎麼得到與cgroup關聯的程序呢? 回到在上面列出來的資料結構關係圖.每個程序都會指向一個css_set.而與這個css_set關聯的所有程序都會鏈入到css_set->tasks連結串列.而cgroup又可能通過一箇中間結構cg_cgroup_link來尋找所有與之關聯的所有css_set.從而可以得到與cgroup關聯的所有程序. 在上面的程式碼中,通過呼叫cgroup_task_count()來得到與之關聯的程序數目,程式碼如下: int cgroup_task_count(const struct cgroup *cgrp) {     int count = 0;     struct cg_cgroup_link *link;     read_lock(&css_set_lock);     list_for_each_entry(link, &cgrp->css_sets, cgrp_link_list) {         count += atomic_read(&link->cg->refcount);     }     read_unlock(&css_set_lock);     return count; } 它就是遍歷cgro->css_sets.並調其轉換為cg_cgroup_link.再從這個link得到css_set.這個css_set的引用計數就是與這個指向這個css_set的task數目. 在程式碼中,是通過pid_array_load()來得到與cgroup關聯的task,並且將程序的pid寫入陣列pidarray中.程式碼如下: static int pid_array_load(pid_t *pidarray, int npids, struct cgroup *cgrp) {     int n = 0;     struct cgroup_iter it;     struct task_struct *tsk;     cgroup_iter_start(cgrp, &it);     while ((tsk = cgroup_iter_next(cgrp, &it))) {         if (unlikely(n == npids))             break;         pidarray[n++] = task_pid_vnr(tsk);     }     cgroup_iter_end(cgrp, &it);     return n; } 我們在這裡遇到了一個新的結構:struct cgroup_iter.它是cgroup的一個迭代器,通過它可以遍歷取得與cgroup關聯的task.它的使用方法為: 1:呼叫cgroup_iter_start()來初始化這個迭程式碼. 2:呼叫cgroup_iter_next()用來取得cgroup中的下一個task 3:使用完了,呼叫cgroup_iner_end(). 下面來分析這三個過程: Cgroup_iter_start()程式碼如下: void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) {     /*      * The first time anyone tries to iterate across a cgroup,      * we need to enable the list linking each css_set to its      * tasks, and fix up all existing tasks.      */     if (!use_task_css_set_links)         cgroup_enable_task_cg_lists();     read_lock(&css_set_lock);     it->cg_link = &cgrp->css_sets;     cgroup_advance_iter(cgrp, it); } 我們在這裡再次遇到了use_task_css_set_links變數.在之前分析cgroup_post_fork()中的時候,我們曾說過,只有在use_task_css_set_link設定為1的時候,才會調task->cg_list鏈入到css_set->tasks中. 所以,在這個地方,如果use_task_css_set_link為0.那就必須要將之前所有的程序都鏈入到它所指向的css_set->tasks連結串列.這個過程是在cgroup_enable_task_cg_lists()完成的,這個函式相當簡單,就是一個task的遍歷,然後就是連結串列的鏈入,在這裡就不再詳細分析了.請自行閱讀它的程式碼.*^_^* 然後,將it->cg_link指向cgrp->css_sets.我們在前面說過,可以通過cgrp->css_sets就可以得得所有的與cgroup關聯的css_set. 到這裡,這個迭代器裡面還是空的,接下來往裡面填充資料.這個過程是在cgroup_advance_iter()中完成,程式碼如下示: static void cgroup_advance_iter(struct cgroup *cgrp,                   struct cgroup_iter *it) { struct list_head *l = it->cg_link; struct cg_cgroup_link *link; struct css_set *cg; /* Advance to the next non-empty css_set */ do {     l = l->next;     if (l == &cgrp->css_sets) {         it->cg_link = NULL;         return;     }    link = list_entry(l, struct cg_cgroup_link, cgrp_link_list);     cg = link->cg; } while (list_empty(&cg->tasks)); it->cg_link = l; it->task = cg->tasks.next; } 通過前面的分析可得知,可通過it->cg_link找到與之關聯的css_set,然後再通過css_set找到與它關聯的task連結串列.因此每次往cgroup迭代器裡填充資料,就是找到一個tasks連結串列不為空的css_set.取資料就從css_set->tasks中取.如果資料取完了,就找下一個tasks連結串列不為空的css_set. 這樣,這個函式的程式碼就很簡單了.它就是找到it->cg_link上tasks連結串列不為空的css_set項. cgroup_iter_next()的程式碼如下: struct task_struct *cgroup_iter_next(struct cgroup *cgrp,                     struct cgroup_iter *it) {     struct task_struct *res;     struct list_head *l = it->task;     /* If the iterator cg is NULL, we have no tasks */     if (!it->cg_link)         return NULL;     res = list_entry(l, struct task_struct, cg_list);     /* Advance iterator to find next entry */     l = l->next;     if (l == &res->cgroups->tasks) {         /* We reached the end of this task list - move on to          * the next cg_cgroup_link */         cgroup_advance_iter(cgrp, it);     } else {         it->task = l;     }     return res; } 如果it->cg_link為空表示it->cg_link已經遍歷完了,也就不存放在task了.否則,從it->task中取得task.如果已經是最後一個task就必須要呼叫cgroup_advance_iter()填充迭代器裡面的資料.最後將取得的task返回. cgroup_iter_end()用來對迭程式碼進行收尾的工作,程式碼如下: void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it) {     read_unlock(&css_set_lock); } 它就是釋放了在cgroup_iter_start()中持有的鎖. 回到cgroup_tasks_open()中.我們接下來會遇到kernel為sequential file提供的一組介面.首先在程式碼遇到的是seq_open().程式碼如下: int seq_open(struct file *file, const struct seq_operations *op) {     struct seq_file *p = file->private_data;     if (!p) {         p = kmalloc(sizeof(*p), GFP_KERNEL);         if (!p)             return -ENOMEM;         file->private_data = p;     }     memset(p, 0, sizeof(*p));     mutex_init(&p->lock);     p->op = op;     file->f_version = 0;     /* SEQ files support lseek, but not pread/pwrite */     file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);     return 0; } 從程式碼中可以看出,它就是初始化了一個struct seq_file結構.並且將其關聯到file->private_data.在這裡要注意將seq_file->op設定成了引數op.在我們分析的這個情景中,也就是cgroup_tasks_seq_operations.這個在我們分析檔案的讀操作的時候會用到的. 7.5.2:task檔案的read操作 從上面的程式碼中可看到.在open的時候,更改了file->f_op.將其指向了cgroup_tasks_operations.該結構如下: static struct file_operations cgroup_tasks_operations = {     .read = seq_read,     .llseek = seq_lseek,     .write = cgroup_file_write,     .release = cgroup_tasks_release, } 相應的,read操作就會轉入到seq_read()中.由於該函式篇幅較大,這裡就不列出了.感興趣的可以自己跟蹤看一下,其它就是迴圈呼叫seq_file->op->start() à seq_file->op->show() à seq_file->op->next() à seq_file->op->stop()的過程. 我們在上面分析task檔案的open操作的時候,曾經提配過,seq_file->op被指向了cgroup_tasks_seq_operations.定義如下: static struct seq_operations cgroup_tasks_seq_operations = {     .start = cgroup_tasks_start,     .stop = cgroup_tasks_stop,     .next = cgroup_tasks_next,     .show = cgroup_tasks_show, } Cgroup_tasks_start()程式碼如下: static void *cgroup_tasks_start(struct seq_file *s, loff_t *pos) {     /*      * Initially we receive a position value that corresponds to      * one more than the last pid shown (or 0 on the first call or      * after a seek to the start). Use a binary-search to find the      * next pid to display, if any      */     struct cgroup *cgrp = s->private;     int index = 0, pid = *pos;     int *iter;     down_read(&cgrp->pids_mutex);     if (pid) {         int end = cgrp->pids_length;         while (index < end) {             int mid = (index + end) / 2;             if (cgrp->tasks_pids[mid] == pid) {                 index = mid;                 break;             } else if (cgrp->tasks_pids[mid] <= pid)                 index = mid + 1;             else                 end = mid;         }     }     /* If we're off the end of the array, we're done */     if (index >= cgrp->pids_length)         return NULL;     /* Update the abstract position to be the actual pid that we found */     iter = cgrp->tasks_pids + index;     *pos = *iter;     return iter; } 它以二分法從cgrp->tasks_pids[ ]中去尋找第一個大於或者等於引數*pos值的項.如果找到了,返回該項.如果沒找到.返回NULL. cgroup_tasks_show()程式碼如下: static int cgroup_tasks_show(struct seq_file *s, void *v) {     return seq_printf(s, "%d\n", *(int *)v); } 它就是將pid轉換為了字串. cgroup_tasks_next()就是找到陣列中的下一項.程式碼如下: static void *cgroup_tasks_next(struct seq_file *s, void *v, loff_t *pos) {     struct cgroup *cgrp = s->private;     int *p = v;     int *end = cgrp->tasks_pids + cgrp->pids_length;     /*      * Advance to the next pid in the array. If this goes off the      * end, we're done      */     p++;     if (p >= end) {         return NULL;     } else {         *pos = *p;         return p;     } } cgroup_tasks_stop()程式碼如下: static void cgroup_tasks_stop(struct seq_file *s, void *v) {     struct cgroup *cgrp = s->private;     up_read(&cgrp->pids_mutex); } 它只是釋放了在cgroup_tasks_start()中持有的讀寫鎖. 7.5.3:task檔案的close操作 Task檔案close時,呼叫的相應介面為cgroup_tasks_release().程式碼如下: static int cgroup_tasks_release(struct inode *inode, struct file *file) {     struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent);     if (!(file->f_mode & FMODE_READ))         return 0;     release_cgroup_pid_array(cgrp);     return seq_release(inode, file); } 它就是將cgroup中的pid資訊與seqfile資訊釋放掉. 到這裡,我們已經分析完了task檔案的open,read,close操作.我們現在就可以實現一下,看上面的分析是否正確. 在前面已經分析中cgroupfs_root.top_cgroup會將系統中的所有css_set與之關聯起來,那麼通過cgroupfs_root_top_cgroup找到的程序應該是系統當前的所有程序.那麼相應的,在掛載目錄的task檔案的內容.應該是系統中所有程序的pid. 如下所示: [[email protected] cgroup]# cat tasks 1 2 3 ……… ……… 2578 其實,這樣做是cgroup子系統開發者特意設定的.它表示所有的程序都在hierarchy的控制之下. 反過來,當我們在掛載目錄mkdir一個目錄,它下面的task檔案內容應該是空的.因為在mkdir後,它對應的cgroup並沒有關聯任何task. 如下所示: [[email protected] cgroup]# mkdir eric [[email protected] cgroup]# cat eric/tasks [[email protected] cgroup]# 下面我們來看一下task檔案的寫操作,也就是怎樣將程序新增進cgroup. 7.5.4:task檔案的write操作 根據上面的檔案,可得知task檔案的write操作對應的函式為int cgroup_tasks_write().程式碼如下: static int cgroup_tasks_write(struct cgroup *cgrp, struct cftype *cft, u64 pid) {     int ret;     /*如果cgroup已經被移除了,非法*/     if (!cgroup_lock_live_group(cgrp))         return -ENODEV;     /*將PID為pid的程序與cgroup關聯*/     ret = attach_task_by_pid(cgrp, pid);     cgroup_unlock();     return ret; } Attach_task_by_pid()的程式碼如下: static int attach_task_by_pid(struct cgroup *cgrp, u64 pid) {     struct task_struct *tsk;     int ret;     /*如果pid不為0.尋找PID為pid的task.並增加其引用計數*/     if (pid) {         rcu_read_lock();         tsk = find_task_by_vpid(pid);         if (!tsk || tsk->flags & PF_EXITING) {             rcu_read_unlock();             return -ESRCH;         }         get_task_struct(tsk);         rcu_read_unlock();         if ((current->euid) && (current->euid != tsk->uid)             && (current->euid != tsk->suid)) {             put_task_struct(tsk);             return -EACCES;         }     }     /*如果pid為0.表示是將當前程序新增進cgroup*/     else {         tsk = current;         get_task_struct(tsk);     }     /*將cgroup與task相關聯*/     ret = cgroup_attach_task(cgrp, tsk);     /*操作完成,減少其引用計數*/     put_task_struct(tsk);     return ret; } 如果寫入的是一個不這0的數,表示的是程序的PID值.如果是寫入0,表示是將當前程序.這個操作的核心操作是cgroup_attach_task().程式碼如下: int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) {     int retval = 0;     struct cgroup_subsys *ss;     struct cgroup *oldcgrp;     struct css_set *cg = tsk->cgroups;     struct css_set *newcg;     struct cgroupfs_root *root = cgrp->root;     int subsys_id;     /*得到與cgroup關聯的第一個subsys的序號*/     get_first_subsys(cgrp, NULL, &subsys_id);     /* Nothing to do if the task is already in that cgroup */     /*找到這個程序之前所屬的cgroup*/     oldcgrp = task_cgroup(tsk, subsys_id);     /*如果已經在這個cgrp裡面了.*/     if (cgrp == oldcgrp)         return 0;     /* 遍歷與hierarchy關聯的subsys       * 如果subsys定義了can_attach函式,就呼叫它       */     for_each_subsys(root, ss) {         if (ss->can_attach) {             retval = ss->can_attach(ss, cgrp, tsk);             if (retval)                 return retval;         }     }     /*      * Locate or allocate a new css_set for this task,      * based on its final set of cgroups      */      /*找到這個task所關聯的css_set.如果不存在,則新建一個*/     newcg = find_css_set(cg, cgrp);     if (!newcg)         return -ENOMEM;     task_lock(tsk);     /*如果task正在執行exit操作*/     if (tsk->flags & PF_EXITING) {         task_unlock(tsk);         put_css_set(newcg);         return -ESRCH;     }     /*將tak->cgroup指向這個css_set*/     rcu_assign_pointer(tsk->cgroups, newcg);     task_unlock(tsk);     /* Update the css_set linked lists if we're using them */     /*更改task->cg_list*/     write_lock(&css_set_lock);     if (!list_empty(&tsk->cg_list)) {         list_del(&tsk->cg_list);         list_add(&tsk->cg_list, &newcg->tasks);     }     write_unlock(&css_set_lock);     /* 遍歷與hierarchy關聯的subsys       * 如果subsys定義了attach 函式,就呼叫它       */     for_each_subsys(root, ss) {         if (ss->attach)             ss->attach(ss, cgrp, oldcgrp, tsk);     }     set_bit(CGRP_RELEASABLE, &oldcgrp->flags);     synchronize_rcu();     /*減小舊指向的引用計數*/ put_css_set(cg);     return 0; } 這個函式邏輯很清楚,它就是初始化task->cgroup.然後將它和subsys相關聯.可自行參照程式碼中的註釋進行分析.這裡就不再贅述了. 在這裡,詳細分析一下find_css_set()函式,這個函式有點意思.程式碼如下: static struct css_set *find_css_set(     struct css_set *oldcg, struct cgroup *cgrp) {     struct css_set *res;     struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT];     int i;     struct list_head tmp_cg_links;     struct cg_cgroup_link *link;     struct hlist_head *hhead;     /* First see if we already have a cgroup group that matches      * the desired set */     read_lock(&css_set_lock);     /*尋找從oldcg轉換為cgrp的css_set.如果不存在,返回NULL */     res = find_existing_css_set(oldcg, cgrp, template);     /*如果css_set已經存在,增加其引用計數後退出*/     if (res)         get_css_set(res);     read_unlock(&css_set_lock);     if (res)         return res; 這一部份,先從雜湊陣列中搜索從oldcg轉換cgrp的css_set.如果不存在,返回NULL.如果在雜湊陣列中存放,增加其引用計數返回即可. Find_existing_css_set()的程式碼如下: static struct css_set *find_existing_css_set(     struct css_set *oldcg,     struct cgroup *cgrp,     struct cgroup_subsys_state *template[]) {     int i;     struct cgroupfs_root *root = cgrp->root;     struct hlist_head *hhead;     struct hlist_node *node;     struct css_set *cg;     /* Built the set of subsystem state objects that we want to      * see in the new css_set */     for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {         if (root->subsys_bits & (1UL << i)) {             /* Subsystem is in this hierarchy. So we want              * the subsystem state from the new              * cgroup */             template[i] = cgrp->subsys[i];         } else {             /* Subsystem is not in this hierarchy, so we              * don't want to change the subsystem state */             template[i] = oldcg->subsys[i];         }     }     hhead = css_set_hash(template);     hlist_for_each_entry(cg, node, hhead, hlist) {         if (!memcmp(template, cg->subsys, sizeof(cg->subsys))) {             /* All subsystems matched */             return cg;         }     }     /* No existing cgroup group matched */     return NULL; } 如果subsys與新的cgroup相關聯,那麼它指向新的cgroup->subsys[]中的對應項.否則指向舊的cgrop的對應項.這樣做主要是因為,該程序可能還被關聯在其它的hierarchy中.所以要保持它在其它hierarchy中的資訊. 最後,在css_set_table[ ]中尋找看是否有與template相等的項.有的話返回該項.如果沒有.返回NULL.     /*分配一個css_set*/     res = kmalloc(sizeof(*res), GFP_KERNEL);     if (!res)         return NULL;     /* Allocate all the cg_cgroup_link objects that we'll need */     /*分配root_count項cg_cgroup_link*/     if (allocate_cg_links(root_count, &tmp_cg_links) < 0) {         kfree(res);         return NULL;     }     /* 初始化剛分配的css_set */     atomic_set(&res->refcount, 1);     INIT_LIST_HEAD(&res->cg_links);     INIT_LIST_HEAD(&res->tasks);     INIT_HLIST_NODE(&res->hlist);     /* Copy the set of subsystem state objects generated in      * find_existing_css_set() */      /*設定css_set->subsys*/     memcpy(res->subsys, template, sizeof(res->subsys)); 執行到這裡的話.表示沒有從css_set_table[ ]中找到相應項.因此需要分配並初始化一個css_set結構.並且設定css_set的subsys域.     write_lock(&css_set_lock);     /* Add reference counts and links from the new css_set. */     /*遍歷所有的subsys以及css_set 中的subsys[ ].      *建立task所在的cgroup到css_set的引用      */     for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {         struct cgroup *cgrp = res->subsys[i]->cgroup;         struct cgroup_subsys *ss = subsys[i];         atomic_inc(&cgrp->count);         /*          * We want to add a link once per cgroup, so we          * only do it for the first subsystem in each          * hierarchy          */         if (ss->root->subsys_list.next == &ss->sibling) {             BUG_ON(list_empty(&tmp_cg_links));

相關推薦

Linux cgroup機制分析框架分析

八:cgroup中檔案的操作 接下來,就來看cgroup檔案的操作.在上面曾分析到:檔案對應的操作集為cgroup_file_operations.如下所示: static struct file_operations cgroup_file_operations = {     .read = c

學習筆記 --- LINUX網絡卡驅動框架分析

網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。 修正一下:上面第三步應該是:register_netdev 下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是

codeigniter原始碼分析框架目錄結構

原始碼SVN地址     https://svn.sinaapp.com/ci123/ codeigniter 目錄如下 CodeIgniter 根目錄 │ ├─application 應用程式 │ ├─cache 快取檔案 │ ├─config 配置檔案

編譯原理(六)自底向上分析LR分析

markdown lr分析 編譯原理 lock mar blog pre 分析法 logs 自底向上分析之LR分析法 說明:以老師PPT為標準,借鑒部分教材內容,AlvinZH學習筆記。 本節內容太多了,考完再寫了,對不起~ 引用說明 - 邵老師課堂PDF - 《編譯原

Java並發系列[1]----AbstractQueuedSynchronizer源碼分析概要分析

人的 等等 子類 運行 pos unsafe fse oid 出了 學習Java並發編程不得不去了解一下java.util.concurrent這個包,這個包下面有許多我們經常用到的並發工具類,例如:ReentrantLock, CountDownLatch, Cyclic

Android編譯系統分析make分析

已開通新的部落格,後續文字都會發到新部落格 http://www.0xfree.top Android 編譯系統解析系列文件 解析lunch的執行過程以及make執行過程中include檔案的順序 Android編譯系統分析之lunch分析 Android

Android 編譯系統分析lunch分析

已開通新的部落格,後續文字都會發到新部落格 http://www.0xfree.top 目錄 Android 編譯系統分析之lunch分析 envsetup.sh指令碼 定義函式 新增編譯引數

【搞定Java併發程式設計】第15篇:佇列同步器AQS原始碼分析概要分析

AQS系列文章: 1、佇列同步器AQS原始碼分析之概要分析 2、佇列同步器AQS原始碼分析之獨佔模式 3、佇列同步器AQS原始碼分析之共享模式 4、佇列同步器AQS原始碼分析之Condition介面、等待佇列 先推薦兩篇不錯的博文: 1、一行一行原始碼分析清楚Abstract

Java併發系列 | AbstractQueuedSynchronizer原始碼分析概要分析

學習Java併發程式設計不得不去了解一下java.util.concurrent這個包,這個包下面有許多我們經常用到的併發工具類,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等。而這些類的底層實現都依賴於AbstractQueu

顛覆大資料分析實時分析的應用

顛覆大資料分析之實時分析的應用 譯者:吳京潤    購書 在這一節,我們將看到構建兩個應用的步驟:一個工業日誌分類系統和一個網際網路流量過濾應用。 工業日誌分類 隨新舊生產工程系統的自動化以及電子工程的發展,大量的機器之間(M2M)的資料正在被生成出來。機器之間的資料可以來自多個不同的源頭,包

MapReduce原始碼分析InputSplit分析

前言 MapReduce的原始碼分析是基於Hadoop1.2.1基礎上進行的程式碼分析。 什麼是InputSplit        InputSplit是指分片,在MapReduce當中作業中,作為map task最小輸入單位。分片是基於檔案基礎上出來的而來的概念,通俗的理

【編譯原理】自下而上的語法分析LR分析

LR分析器是一種由下而上(bottom-up)的上下文無關語法分析器。LR意指由左(Left)至右處理輸入字串,並以最右邊優先派生(Right derivation)的推導順序(相對於LL分析器)建構語法樹。能以此方式分析的語法稱為LR語法。而在LR(k)這樣的名稱中,

空間分析距離分析

繼續總結下距離分析。如下是ArcGIS 10.x中,距離分析相關的工具: ArcGIS中,主要可以通過如下的幾種方式進行距離分析: 1) 歐氏距離分析 2) 成本加權距離分析 3) 用於垂直移

Oracle Spatial 空間分析緩衝區分析

緩衝區分析需要主要使用Oracle Spatial 空間分析操作函式,下面會對其各個引數進行詳細講解,資料有來源於網上與書上。 SDO_GEOM.SDO_WITHIN_DISTANCE(sd

語義分析情感分析

紙上得來終覺淺,一直苦於沒有小專案來看看鍛鍊下自己,相信很多初學程式設計的同學也一樣,那就是不知道自己到底學的怎麼樣,而且也覺得沒有一個實際的專案來幫助提高自己的實際動手能力,理論總是美好的,在實際的專案中會碰到這樣那樣的小問題,而且每一個問題都不是書上全部講到的,就比如我將

MapReduce原始碼分析MapTask分析(二)

SpillThread分析 為什麼需要Spill          記憶體大小總是有效,因此在Mapper在處理過程中,資料持續輸出到記憶體中時,必然需要有機制能將記憶體中的資料換出,合理的刷出到磁碟上。SpillThread就是用來完成這部分工作。          Sp

效能分析IO分析-jbd2引起的IO高

 背景:請事假在外中,聽平臺組同事反饋了一個問題,在往生產資料庫中匯入部分資料時會造成客戶端的訪問超時,初步定位是因為伺服器磁碟佔用IO過高,導資料時IO會飆升到100%,因此引起了不少資料庫的慢查詢操作導致客戶端響應超時,無奈只好暫時停止了匯入資料的指令碼,同時也延誤了針對

LDD3原始碼分析poll分析

作者:劉昊昱 編譯環境:Ubuntu 10.10 核心版本:2.6.32-38-generic-pae LDD3原始碼路徑:examples/scull/pipe.c  examples/scull/main.c 本文分析LDD3第6章的poll(輪詢)操作。要理解驅動程式中poll函式的作用和實現,

5.dubbo原始碼分析 SPI分析

如果大家看過之前的 dubbo 核心 SPI 實現 – 2.dubbo原始碼分析 之 核心SPI實現, 有可能還是一頭霧水,下面我講一下dubbo的具體應用。最典型的應用就是 Protocol 介面。 Protocol 屬於 dubbo 十層結構 中的遠端呼叫

影象分析直方圖分析

  - created by gloomyfish 影象分析之強度直方圖分析 直方圖介紹 強度直方圖圖形化顯示不同的畫素值在不同的強度值上的出現頻率,對於灰度影象來說強度 範圍為[0~255]之間,對於RGB的彩色影象可以獨立顯示三種顏色的強度直方圖。強度直方