漫谈Linux内核哈希表(2)
2016-07-24 11:00
375 查看
对照前面介绍过的内核通知链、链表,本章我们将要介绍的哈希表的初始化和定义也是如出一辙的:
点击(此处)折叠或打开
定义并初始化一个名为name的哈希链表表头
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
初始化一个已经定义好的哈希链表,其中ptr指向哈希表头的地址
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
其中,HLIST_HEAD_INIT一般这么用:
点击(此处)折叠或打开
struct hlist_head myhlist;
HLIST_HEAD_INIT(&myhlist);
对于哈希表中的每一个hlist_node节点,通常情况下都要调用初始化函数INIT_HLIST_NODE()来初始化:
点击(此处)折叠或打开
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
一个给定的哈希节点,判断它是否已经被插入到某条哈希链表里hlist_unhashed():
点击(此处)折叠或打开
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
这里我们可以看到,hlist_node里的pprev完成了这个功能,即如果一个hlist_node的pprev为NULL,则说明该节点目前并未加入任何哈希链表。
下面这个接口就没啥好说的,用于判断是一个给定哈希表是否为空(即不包含任何哈希节点)。注意,该接口入参为hlist_head类型而非hlist_node类型:
点击(此处)折叠或打开
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
剩下的其他接口,也都非常简单,这里不再一一赘述。下面我们看几个宏定义:
点击(此处)折叠或打开
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
该宏和前面介绍过的list_entry()的实现、作用完全一样
#define list_entry(ptr, type, member) container_of(ptr,type,member)
对照list的学习过程,可想而知,下面这几组结构,其作用也就不言而喻了:
区别在于最后两个宏的入参上有些小区别。由于哈希链表,表头和表节点是不同的数据结构,所以才会有这个差异。还是对照着list_for_each_*的学习过程:
点击(此处)折叠或打开
hlist_for_each_entry(tpos, pos, head, member)
其中tpos,是hlist_node所属宿主结构体类型的指针,pos是hlist_node类型的指针,tpos和pos都充当的游标的作用。例如:
点击(此处)折叠或打开
typedef struct student
{
char m_name[MAX_STRING_LEN];
char m_sex;
int m_age;
struct list_head m_list; /*把我们的学生对象组织成双向链表,就靠该节点了*/
struct hlist_node m_hlist; /*把我们的学生对象组织成哈希链表,就靠该节点了*/
}Student;
HLIST_HEAD(myhlist);
Student *st;
struct hlist_node *i;
hlist_for_each_entry(st, i, &myhlist, m_hlist)
{
//To do something here…
//通常情况,开发者在这里仅需要关注、使用st变量就可以,不需要关心i
}
同样地,在使用hlist_for_each_entry_safe(tpos, pos, n, head,
member)时,tpos也是宿主结构体类型的一个指针变量,当游标使用,n是一个hlist_node类型的另一个指针,这个指针指向pos所在元素的下一个元素,它由hlist_for_each_entry_safe()本身进行维护,开发者不用修改它:
点击(此处)折叠或打开
HLIST_HEAD(myhlist);
Student *st;
struct hlist_node *i,*j;
hlist_for_each_entry_safe(st, i, j, &myhlist, m_hlist)
{
//To do something here…
//i和j都不需要开发者关注,仅使用st就可以了
}
另外,还有一组宏:
点击(此处)折叠或打开
hlist_for_each_entry_continue(tpos, pos, member)
hlist_for_each_entry_from(tpos, pos, member)
其参数tpos和pos意义和类型与前面介绍过的一致,这两个宏的作用分别是:
hlist_for_each_entry_continue():从pos节点开始(不包含pos),往后依次遍历所有节点;
hlist_for_each_entry_from(): 从pos节点开始(包含pos),依次往后遍历所有节点;
这一组宏是“不安全”的,意思是,在它们里面你只能执行查找遍历的任务、不能插入或者删除节点,因为它们脑门上没有那个“safe”的关键字。
最后,还是老生常谈,实际操练一把。把链表章节我们介绍过的学历管理系统拿来,添加一个需求:“按照男、女的分类原则,将所有学生进行分类”。很明显,这里我们就可以用到哈希链表了。怎么实现呢?其实非常简单,前面我们已经见过对Student结构体的改造了。最终的完整代码如下所示:
头文件修改:
点击(此处)折叠或打开
/*student.h*/
#ifndef __STUDENT_H_
#define __STUDENT_H_
#include <linux/list.h>
#define MAX_STRING_LEN 32
#define MAX_HLIST_COUNT 2 //只有“男”、“女”两条哈希链表
typedef struct student
{
char m_name[MAX_STRING_LEN];
char m_sex;
int m_age;
struct list_head m_list; /*把我们的学生对象组织成双向链表,就靠该节点了*/
struct hlist_node m_hlist; /*把我们的学生对象组织成哈希链表,就靠该节点了*/
}Student;
#endif
源文件修改:
点击(此处)折叠或打开
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include "student.h"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Koorey Wung");
static int dbg_flg = 0;
LIST_HEAD(g_student_list);
// 其中g_stu_hlist[0]代表男生;g_stu_hlist[1]代表女生
struct hlist_head g_stu_hlist[MAX_HLIST_COUNT];
//初始化男、女学生的哈希链表
static void init_hlists(void)
{
int i = 0;
for(i=0;i< MAX_HLIST_COUNT;i++){
INIT_HLIST_HEAD(&g_stu_hlist[i]);
}
}
static int add_stu(char* name,char sex,int age)
{
Student *stu,*cur_stu;
list_for_each_entry(cur_stu,&g_student_list,m_list){ //仅遍历是否有同名学生,所以用该接口
if(0 == strcmp(cur_stu->m_name,name))
{
printk("Error:the name confict!\n");
return -1;
}
}
stu = kmalloc(sizeof(Student), GFP_KERNEL);
if(!stu)
{
printk("kmalloc mem error!\n");
return -1;
}
memset(stu,0,sizeof(Student));
strncpy(stu->m_name,name,strlen(name));
stu->m_sex = sex;
stu->m_age = age;
INIT_LIST_HEAD(&stu->m_list); //初始化宿主结构里的双向链表节点m_list
INIT_HLIST_NODE(&stu->m_hlist); //初始化宿主结构里的哈希节点m_hlist
if(dbg_flg)
printk("(Add)name:[%s],\tsex:[%c],\tage:[%d]\n",stu->m_name,stu->m_sex,stu->m_age);
list_add_tail(&stu->m_list,&g_student_list); //将新学生插入到链表尾部,很简单吧
return 0;
}
EXPORT_SYMBOL(add_stu); //导出该函数,后面我们要在其他模块里调用,为了便于测试,下面其他几个接口类似
static int del_stu(char *name)
{
Student *cur,*next;
int ret = -1;
list_for_each_entry_safe(cur,next,&g_student_list,m_list){ //因为要删除链表的节点,所以必须有带有“safe”的宏接口
if(0 == strcmp(name,cur->m_name))
{
list_del(&cur->m_list);
printk("(Del)name:[%s],\tsex:[%c],\tage:[%d]\n",cur->m_name,\
cur->m_sex,cur->m_age);
kfree(cur);
cur = NULL;
ret = 0;
break;
}
}
return ret;
}
EXPORT_SYMBOL(del_stu);
static void dump_students(void)
{
Student *stu;
int i = 1;
printk("===================Student List================\n");
list_for_each_entry(stu,&g_student_list,m_list){ //同样,也仅遍历链表而已
printk("(%d)name:[%s],\tsex:[%c],\tage:[%d]\n",i++,stu->m_name,\
stu->m_sex,stu->m_age);
}
printk("===============================================\n");
}
EXPORT_SYMBOL(dump_students);
static void dump_hlist(int id)
{
Student *stu;
struct hlist_node *i;
struct hlist_head *head;
int count = 1;
if(!(id>=0 && id< MAX_HLIST_COUNT)){
printk("Invalid id[%d] !\n",id);
return;
}
head = &g_stu_hlist[id];
printk("===================%s List===================\n",((id == 0)?"Boy":"Girl"));
//因为该接口只遍历哈希表,并不会插入、删除节点,所以用hlist_for_each_entry(),注意四个入参的类型、作用和意义
hlist_for_each_entry(stu, i, head,m_hlist){
printk("(%d)name:[%s],\tsex:[%c],\tage:[%d]\n",count++,stu->m_name,\
stu->m_sex,stu->m_age);
}
printk("==============================================\n");
}
EXPORT_SYMBOL(dump_hlist);
//分别打印男女学生,各自哈希链表上的情况
static void dump_hlists(void)
{
dump_hlist(0);
dump_hlist(1);
}
EXPORT_SYMBOL(dump_hlists);
//按照性别对学生进行分类
static void classify_stu(void)
{
Student *cur,*next;
int id = 0;
list_for_each_entry_safe(cur,next,&g_student_list,m_list){
//将从cur从g_student_list链表上移下来,但并不会释放cur学生的内存空间,同时对其m_list成员重新初始化
list_del_init(&cur->m_list);
if('m' == cur->m_sex){
id = 0;
}
else if('f' == cur->m_sex){
id = 1;
}
else{
printk("Get error!\n");
return;
}
//根据id,以m_hlist将学生按性别组织成哈希表
hlist_add_head(&(cur->m_hlist),&(g_stu_hlist[id]));
}
printk("Finished!\n");
}
EXPORT_SYMBOL(classify_stu);
static void init_system(void)
{
//初始化男、女学生哈希链表头
init_hlists();
/*系统启动初始化时,向链表g_student_list里添加6个学生*/
add_stu("Tom",'m',18);
add_stu("Jerry",'f',17);
add_stu("Alex",'m',18);
add_stu("Conory",'f',18);
add_stu("Frank",'m',17);
add_stu("Marry",'f',17);
}
/*释放所有哈希链表上的内存空间*/
static void clean_up_hlist(void)
{
int i;
Student *stu;
struct hlist_node *cur,*next;
for(i=0;i< MAX_HLIST_COUNT;i++){
printk("===================%s List================\n",((i == 0)?"Boy":"Girl"));
hlist_for_each_entry_safe(stu, cur, next, &(g_stu_hlist[i]), m_hlist){
hlist_del(&(stu->m_hlist));
printk("Destroy [%s]\n",stu->m_name);
kfree(stu);
}
printk("===========================================\n");
}
}
/*释放双向表上的内存空间*/
static void clean_up_list(void)
{
Student *stu,*next;
printk("===========Unclassified Student List===========\n");
list_for_each_entry_safe(stu,next,&g_student_list,m_list){
list_del(&stu->m_list);
printk("Destroy [%s]\n",stu->m_name);
kfree(stu);
}
printk("===============================================\n");
}
/*因为没有数据库,所以当我们的模块退出时,需要释放内存。*/
static void clean_up(void)
{
clean_up_list();
clean_up_hlist();
}
/*模块初始化接口*/
static int student_mgt_init(void)
{
printk("Student Managment System,Initializing...\n");
init_system();
dbg_flg = 1; //从此以后,再调用add_stu()时,都会有有内核打印信息,详见实例训练
dump_students();
return 0;
}
static void student_mgt_exit(void)
{
clean_up();
printk("System Terminated!\n");
}
module_init(student_mgt_init);
module_exit(student_mgt_exit);
验证结果如下:
我们每调用此classify_stu()就会将目前自由双向链表g_student_list里的学生按照性别进行分类,男生存储到哈希链表g_stu_hlist[0]里,女生存储到哈希链表g_stu_hlist[1]里。而调用add_stu()则是向g_student_list链表里添加学生,以便为后面调用classify_stu()做准备:
其实可以看到,哈希链表的用法也是蛮简单的。其实内核里诸如通知链、链表、哈希表等等这些基础数据结构,掌握了原理后使用起来都不难。
未完,待续....
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(4306) | 评论(3) | 转发(7) |
0
上一篇:漫谈Linux内核哈希表(1)
下一篇:将博客搬至CSDN
相关热门文章
浅析usb转serial串口设备在lin...
内核头文件的使用
Linux内核模块LKM编译-自制Mak...
Linux设备驱动程序学习(7)-...
关于内核模块的挂载后的最终虚...
linux 常见服务端口
xmanager 2.0 for linux配置
【ROOTFS搭建】busybox的httpd...
openwrt中luci学习笔记
什么是shell
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
wddpeakking2015-08-11 14:20:44
博主,你的博文写的真不错,拜读了,这里有个网站:被坑导航-程序员专用导航 http://peakking.com 这上面也有很多好的文章地址,咱们可以共同学习!
回复 | 举报
helenxu0012015-05-22 08:47:23
详细!
回复 | 举报
jkercs2015-05-10 14:20:14
good to read
回复 | 举报
评论热议
点击(此处)折叠或打开
定义并初始化一个名为name的哈希链表表头
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
初始化一个已经定义好的哈希链表,其中ptr指向哈希表头的地址
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
其中,HLIST_HEAD_INIT一般这么用:
点击(此处)折叠或打开
struct hlist_head myhlist;
HLIST_HEAD_INIT(&myhlist);
对于哈希表中的每一个hlist_node节点,通常情况下都要调用初始化函数INIT_HLIST_NODE()来初始化:
点击(此处)折叠或打开
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
一个给定的哈希节点,判断它是否已经被插入到某条哈希链表里hlist_unhashed():
点击(此处)折叠或打开
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
这里我们可以看到,hlist_node里的pprev完成了这个功能,即如果一个hlist_node的pprev为NULL,则说明该节点目前并未加入任何哈希链表。
下面这个接口就没啥好说的,用于判断是一个给定哈希表是否为空(即不包含任何哈希节点)。注意,该接口入参为hlist_head类型而非hlist_node类型:
点击(此处)折叠或打开
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
剩下的其他接口,也都非常简单,这里不再一一赘述。下面我们看几个宏定义:
点击(此处)折叠或打开
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
该宏和前面介绍过的list_entry()的实现、作用完全一样
#define list_entry(ptr, type, member) container_of(ptr,type,member)
对照list的学习过程,可想而知,下面这几组结构,其作用也就不言而喻了:
哈希表 | 链表 |
hlist_for_each(pos, head) | list_for_each(pos, head) |
hlist_for_each_safe(pos, n, head) | list_for_each_safe(pos, n, head) |
hlist_for_each_entry(tpos, pos, head, member) | list_for_each_entry(pos, head, member) |
hlist_for_each_entry_safe(tpos, pos, n, head, member) | list_for_each_entry_safe(pos, n, head, member) |
点击(此处)折叠或打开
hlist_for_each_entry(tpos, pos, head, member)
其中tpos,是hlist_node所属宿主结构体类型的指针,pos是hlist_node类型的指针,tpos和pos都充当的游标的作用。例如:
点击(此处)折叠或打开
typedef struct student
{
char m_name[MAX_STRING_LEN];
char m_sex;
int m_age;
struct list_head m_list; /*把我们的学生对象组织成双向链表,就靠该节点了*/
struct hlist_node m_hlist; /*把我们的学生对象组织成哈希链表,就靠该节点了*/
}Student;
HLIST_HEAD(myhlist);
Student *st;
struct hlist_node *i;
hlist_for_each_entry(st, i, &myhlist, m_hlist)
{
//To do something here…
//通常情况,开发者在这里仅需要关注、使用st变量就可以,不需要关心i
}
同样地,在使用hlist_for_each_entry_safe(tpos, pos, n, head,
member)时,tpos也是宿主结构体类型的一个指针变量,当游标使用,n是一个hlist_node类型的另一个指针,这个指针指向pos所在元素的下一个元素,它由hlist_for_each_entry_safe()本身进行维护,开发者不用修改它:
点击(此处)折叠或打开
HLIST_HEAD(myhlist);
Student *st;
struct hlist_node *i,*j;
hlist_for_each_entry_safe(st, i, j, &myhlist, m_hlist)
{
//To do something here…
//i和j都不需要开发者关注,仅使用st就可以了
}
另外,还有一组宏:
点击(此处)折叠或打开
hlist_for_each_entry_continue(tpos, pos, member)
hlist_for_each_entry_from(tpos, pos, member)
其参数tpos和pos意义和类型与前面介绍过的一致,这两个宏的作用分别是:
hlist_for_each_entry_continue():从pos节点开始(不包含pos),往后依次遍历所有节点;
hlist_for_each_entry_from(): 从pos节点开始(包含pos),依次往后遍历所有节点;
这一组宏是“不安全”的,意思是,在它们里面你只能执行查找遍历的任务、不能插入或者删除节点,因为它们脑门上没有那个“safe”的关键字。
最后,还是老生常谈,实际操练一把。把链表章节我们介绍过的学历管理系统拿来,添加一个需求:“按照男、女的分类原则,将所有学生进行分类”。很明显,这里我们就可以用到哈希链表了。怎么实现呢?其实非常简单,前面我们已经见过对Student结构体的改造了。最终的完整代码如下所示:
头文件修改:
点击(此处)折叠或打开
/*student.h*/
#ifndef __STUDENT_H_
#define __STUDENT_H_
#include <linux/list.h>
#define MAX_STRING_LEN 32
#define MAX_HLIST_COUNT 2 //只有“男”、“女”两条哈希链表
typedef struct student
{
char m_name[MAX_STRING_LEN];
char m_sex;
int m_age;
struct list_head m_list; /*把我们的学生对象组织成双向链表,就靠该节点了*/
struct hlist_node m_hlist; /*把我们的学生对象组织成哈希链表,就靠该节点了*/
}Student;
#endif
源文件修改:
点击(此处)折叠或打开
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include "student.h"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Koorey Wung");
static int dbg_flg = 0;
LIST_HEAD(g_student_list);
// 其中g_stu_hlist[0]代表男生;g_stu_hlist[1]代表女生
struct hlist_head g_stu_hlist[MAX_HLIST_COUNT];
//初始化男、女学生的哈希链表
static void init_hlists(void)
{
int i = 0;
for(i=0;i< MAX_HLIST_COUNT;i++){
INIT_HLIST_HEAD(&g_stu_hlist[i]);
}
}
static int add_stu(char* name,char sex,int age)
{
Student *stu,*cur_stu;
list_for_each_entry(cur_stu,&g_student_list,m_list){ //仅遍历是否有同名学生,所以用该接口
if(0 == strcmp(cur_stu->m_name,name))
{
printk("Error:the name confict!\n");
return -1;
}
}
stu = kmalloc(sizeof(Student), GFP_KERNEL);
if(!stu)
{
printk("kmalloc mem error!\n");
return -1;
}
memset(stu,0,sizeof(Student));
strncpy(stu->m_name,name,strlen(name));
stu->m_sex = sex;
stu->m_age = age;
INIT_LIST_HEAD(&stu->m_list); //初始化宿主结构里的双向链表节点m_list
INIT_HLIST_NODE(&stu->m_hlist); //初始化宿主结构里的哈希节点m_hlist
if(dbg_flg)
printk("(Add)name:[%s],\tsex:[%c],\tage:[%d]\n",stu->m_name,stu->m_sex,stu->m_age);
list_add_tail(&stu->m_list,&g_student_list); //将新学生插入到链表尾部,很简单吧
return 0;
}
EXPORT_SYMBOL(add_stu); //导出该函数,后面我们要在其他模块里调用,为了便于测试,下面其他几个接口类似
static int del_stu(char *name)
{
Student *cur,*next;
int ret = -1;
list_for_each_entry_safe(cur,next,&g_student_list,m_list){ //因为要删除链表的节点,所以必须有带有“safe”的宏接口
if(0 == strcmp(name,cur->m_name))
{
list_del(&cur->m_list);
printk("(Del)name:[%s],\tsex:[%c],\tage:[%d]\n",cur->m_name,\
cur->m_sex,cur->m_age);
kfree(cur);
cur = NULL;
ret = 0;
break;
}
}
return ret;
}
EXPORT_SYMBOL(del_stu);
static void dump_students(void)
{
Student *stu;
int i = 1;
printk("===================Student List================\n");
list_for_each_entry(stu,&g_student_list,m_list){ //同样,也仅遍历链表而已
printk("(%d)name:[%s],\tsex:[%c],\tage:[%d]\n",i++,stu->m_name,\
stu->m_sex,stu->m_age);
}
printk("===============================================\n");
}
EXPORT_SYMBOL(dump_students);
static void dump_hlist(int id)
{
Student *stu;
struct hlist_node *i;
struct hlist_head *head;
int count = 1;
if(!(id>=0 && id< MAX_HLIST_COUNT)){
printk("Invalid id[%d] !\n",id);
return;
}
head = &g_stu_hlist[id];
printk("===================%s List===================\n",((id == 0)?"Boy":"Girl"));
//因为该接口只遍历哈希表,并不会插入、删除节点,所以用hlist_for_each_entry(),注意四个入参的类型、作用和意义
hlist_for_each_entry(stu, i, head,m_hlist){
printk("(%d)name:[%s],\tsex:[%c],\tage:[%d]\n",count++,stu->m_name,\
stu->m_sex,stu->m_age);
}
printk("==============================================\n");
}
EXPORT_SYMBOL(dump_hlist);
//分别打印男女学生,各自哈希链表上的情况
static void dump_hlists(void)
{
dump_hlist(0);
dump_hlist(1);
}
EXPORT_SYMBOL(dump_hlists);
//按照性别对学生进行分类
static void classify_stu(void)
{
Student *cur,*next;
int id = 0;
list_for_each_entry_safe(cur,next,&g_student_list,m_list){
//将从cur从g_student_list链表上移下来,但并不会释放cur学生的内存空间,同时对其m_list成员重新初始化
list_del_init(&cur->m_list);
if('m' == cur->m_sex){
id = 0;
}
else if('f' == cur->m_sex){
id = 1;
}
else{
printk("Get error!\n");
return;
}
//根据id,以m_hlist将学生按性别组织成哈希表
hlist_add_head(&(cur->m_hlist),&(g_stu_hlist[id]));
}
printk("Finished!\n");
}
EXPORT_SYMBOL(classify_stu);
static void init_system(void)
{
//初始化男、女学生哈希链表头
init_hlists();
/*系统启动初始化时,向链表g_student_list里添加6个学生*/
add_stu("Tom",'m',18);
add_stu("Jerry",'f',17);
add_stu("Alex",'m',18);
add_stu("Conory",'f',18);
add_stu("Frank",'m',17);
add_stu("Marry",'f',17);
}
/*释放所有哈希链表上的内存空间*/
static void clean_up_hlist(void)
{
int i;
Student *stu;
struct hlist_node *cur,*next;
for(i=0;i< MAX_HLIST_COUNT;i++){
printk("===================%s List================\n",((i == 0)?"Boy":"Girl"));
hlist_for_each_entry_safe(stu, cur, next, &(g_stu_hlist[i]), m_hlist){
hlist_del(&(stu->m_hlist));
printk("Destroy [%s]\n",stu->m_name);
kfree(stu);
}
printk("===========================================\n");
}
}
/*释放双向表上的内存空间*/
static void clean_up_list(void)
{
Student *stu,*next;
printk("===========Unclassified Student List===========\n");
list_for_each_entry_safe(stu,next,&g_student_list,m_list){
list_del(&stu->m_list);
printk("Destroy [%s]\n",stu->m_name);
kfree(stu);
}
printk("===============================================\n");
}
/*因为没有数据库,所以当我们的模块退出时,需要释放内存。*/
static void clean_up(void)
{
clean_up_list();
clean_up_hlist();
}
/*模块初始化接口*/
static int student_mgt_init(void)
{
printk("Student Managment System,Initializing...\n");
init_system();
dbg_flg = 1; //从此以后,再调用add_stu()时,都会有有内核打印信息,详见实例训练
dump_students();
return 0;
}
static void student_mgt_exit(void)
{
clean_up();
printk("System Terminated!\n");
}
module_init(student_mgt_init);
module_exit(student_mgt_exit);
验证结果如下:
我们每调用此classify_stu()就会将目前自由双向链表g_student_list里的学生按照性别进行分类,男生存储到哈希链表g_stu_hlist[0]里,女生存储到哈希链表g_stu_hlist[1]里。而调用add_stu()则是向g_student_list链表里添加学生,以便为后面调用classify_stu()做准备:
其实可以看到,哈希链表的用法也是蛮简单的。其实内核里诸如通知链、链表、哈希表等等这些基础数据结构,掌握了原理后使用起来都不难。
未完,待续....
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(4306) | 评论(3) | 转发(7) |
0
上一篇:漫谈Linux内核哈希表(1)
下一篇:将博客搬至CSDN
相关热门文章
浅析usb转serial串口设备在lin...
内核头文件的使用
Linux内核模块LKM编译-自制Mak...
Linux设备驱动程序学习(7)-...
关于内核模块的挂载后的最终虚...
linux 常见服务端口
xmanager 2.0 for linux配置
【ROOTFS搭建】busybox的httpd...
openwrt中luci学习笔记
什么是shell
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
wddpeakking2015-08-11 14:20:44
博主,你的博文写的真不错,拜读了,这里有个网站:被坑导航-程序员专用导航 http://peakking.com 这上面也有很多好的文章地址,咱们可以共同学习!
回复 | 举报
helenxu0012015-05-22 08:47:23
详细!
回复 | 举报
jkercs2015-05-10 14:20:14
good to read
回复 | 举报
评论热议
相关文章推荐
- 漫谈Linux内核哈希表(1)
- Linux内核【链表】整理笔记(2)
- Linux内核【链表】整理笔记(1)
- Linux 内核通知链随笔【中】
- 谈谈Linux下动态库查找路径的问题
- CentOS6.4完全安装FFmpeg手记
- 小议Linux系统下的文件系统
- (十六)洞悉linux下的Netfilter&iptables:开发自己的hook函数【实战】(下)
- (十五)洞悉linux下的Netfilter&iptables:开发自己的hook函数【实战】(上)
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十三)洞悉linux下的Netfilter&iptables:为防火墙增添功能模块【实战】
- (十二)洞悉linux下的Netfilter&iptables:iptables命令行工具源码解析【下】
- (十一)洞悉linux下的Netfilter&iptables:iptables命令行工具源码解析【上】
- (八)洞悉linux下的Netfilter&iptables:状态防火墙
- (七)洞悉linux下的Netfilter&iptables:如何理解连接跟踪机制?【下】
- (六)洞悉linux下的Netfilter&iptables:如何理解连接跟踪机制?【中】
- (五)洞悉linux下的Netfilter&iptables:如何理解连接跟踪机制?【上】
- (四)洞悉linux下的Netfilter&iptables:包过滤子系统iptable_filter
- (三)洞悉linux下的Netfilter&iptables:内核中的rule,match和target
- (二)洞悉linux下的Netfilter&iptables:内核中的ip_tables小觑