內核鏈表點滴--list_for_each_safe使用

自己是個初學者,一直在學習國嵌的教程,本來是在本子上做的筆記,今天學習到內核鏈表了,老師留了個小問題,自己做了一下,算是寫下自己的心得吧.
大牛們一定不要見笑...
源程序如下:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/list.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Xie");
MODULE_DESCRIPTION("List Module");
MODULE_ALIAS("List module");
struct student {
    char name[100];
    int num;
    struct list_head list;
};
struct student *pstudent;
struct student *tmp_student;
struct list_head student_list;
struct list_head *pos, *node; //add node
int mylist_init() {
    int i=0;
    INIT_LIST_HEAD(&student_list);

    pstudent = kmalloc(sizeof(struct student)*5, GFP_KERNEL);
    memset(pstudent, 0, sizeof(struct student)*5);
    
    for (i=0; i<5; i++) {
        sprintf(pstudent[i].name, "Student%d", i+1);
        pstudent[i].num = i+1;
        list_add(&(pstudent[i].list), &student_list);
    }

    list_for_each(pos, &student_list) {
        tmp_student = list_entry(pos, struct student, list);
        printk("<0>student %d name: %s\n", tmp_student->num, tmp_student->name);
    }
    
    return 0;
}

void mylist_exit() {
    int i;
    /*實驗: 將for換成list_for_each來遍歷刪除節點,觀察要發生的現象,並考慮解決辦法 */
    for (i=0; i<5; i++) {
        list_del(&(pstudent[i].list));
    
    kfree(pstudent);
}

module_init(mylist_init);
module_exit(mylist_exit);

makefile文件內容如下//順便複習模塊的基本知識,linux的東西真多呀,看來嵌入式之路漫漫呀...

ifneq ($(KERNELRELEASE),)
    obj-m := mylist.o
else
    KDIR := /lib/modules/2.6.18-53.el5/build
    all:
        make -C $(KDIR) M=$(PWD) modules
    clean:
        rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif

按老師的留的思考題:
如果是換成list_for_each遍歷刪除節點,我開始是如下修改的
void mylist_exit()

 list_for_each(pos,&student_list);   //這裏寫的真是亂改,反正出現的情況就是死機,
    list_del(&student_list);            //這裏的格式都不對,list_for_list原型是for,後來語句是{}的,這裏
 kfree(pstudent);                    // 都沒有做,另外還要用list_entry提到數據後才能遍歷操作...
}
總之,一直是死機,後來也修改了一些格式,還是死機,問了羣裏的朋友,說是要用到list_for_list_safe才能安全刪除
於是就G了一下,發現:
函數原型分析

#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)

由定義可知,list_del(pos)(將pos的前後指針指向undefined state)panic,list_del_init(pos)(將pos前後指針指向自身)導致死循環.--當刪除時,鏈表指針變爲特殊類型,所以死機.

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)

由定義可知,safe函數首先將pos的後指針緩存到n,處理一個流程後再賦回pos,避免了這種情況的發生。
因此只遍歷鏈表不刪除節點時可以使用前者,若有刪除節點的操作,則要使用後者。

由safe的說明可知,是專門爲刪除節點時準備的:iterate over a list safe against removal of list entry。
其他帶safe的處理也基本源於這個原因。

下面進行了更改
修改程序爲:
先定義一個和pos同樣類型的node指針用以保存pos的後的指針
然後修改 exit_mylist函數,修改後如下:

void mylist_exit()
{ 
     //int i ;
     /* 實驗:將for換成list_for_each來遍歷刪除結點,觀察要發生的現象,並考慮解決辦法 */
     //for(i=0;i<5;i++)
     list_for_each_safe(pos,node,&student_list)
     {
        //list_del(&(pstudent[i].list));
        tmp_student = list_entry(pos,struct student,list);//提取數據,我開始這個也不知道,哎...
        list_del(&(tmp_student->list));
        printk("<0>student %d name: %s is being deleted.\n",tmp_student->num,tmp_student->name);
     }
 
     kfree(pstudent);
}

這樣,就不會死機了...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章