Linux 内核驱动中申请大块内存

在linux的内核模块中要申请内存,一般都是使用kmalloc的方式申请一块指定大小的内存,但这个函数有一定的限制,一般只能申请到几M到十几M的内存空间,申请内存太多将会失败。这样的限制是好的,保证具有完全权限的内核程序不会无意地过度浪费内存资源(例如通过变量a进行kmalloc(a),但因为程序员的问题,导致a是一个非常大的数字,如果不限制就会因为内存不足就导致内核崩溃了。),如果要进行DMA操作,使用几M的内存已经能达到很好的效率。

但是肯定有在内核中使用超大块内存的情况,例如vmware,它可以预分配虚拟机的所有内存。这时候,就应该自己去按页分配并自己进行页管理。

这里有一个我以前写的内核中大型内存数组管理函数,提供给大家参考参考。没有写太多的注释,仅供有兴趣的参考哈

kvector.h

#ifndef _HOVERLEES_KVECTOR_H
#define _HOVERLEES_KVECTOR_H
#include <linux/slab.h>

#define ELEMENT_LENGTH	8

#define INDEX_LINK_EACH_NUM (PAGE_SIZE/sizeof(void*)-1)
#define ELEMENTS_PER_PAGE PAGE_SIZE/ELEMENT_LENGTH

typedef struct _index_link_node{
	void* data_pages[INDEX_LINK_EACH_NUM];
	struct _index_link_node * next;
}index_link_node;

typedef struct _kvector{
	int size;
	int current_index_pos;
	int total_index_elements;
	int index_node_num;
	index_link_node* index;
}kvector;

typedef int (*KVECTOR_EACH_PAGE_CB)(void* p,void* param);

/*初始化数组*/
int kvector_init(kvector* vector,int init_size);
/*销毁数组*/
void kvector_destroy(kvector* vector);
/*数组大小更改*/
int kvector_resize(kvector* vector,int new_size);
/*设置数组中的第index个元素*/
int kvector_set(kvector* vector,int index,void* mem);
/*取得数组中的第index个元素*/
int kvector_get(kvector* vector,int index,void* mem);

void kvector_foreach_page(kvector* vector,KVECTOR_EACH_PAGE_CB callback,void* param);

#endif


kvector.c

#include "kvector.h"

void kvector_destroy(kvector* vector);

void* kvector_get_free_page(void){
	void* r=(void*) get_zeroed_page(GFP_KERNEL);
	return r;
}
void kvector_free_page(void* p){
	free_page((unsigned long)p);
}
int kvector_index_add_link_node(kvector* vector){
	index_link_node* p;
	index_link_node* r=(index_link_node*)kvector_get_free_page();
	if(!r) return 0;
	r->next=NULL;
	if(vector->index==NULL){
		vector->index=r;
	}
	else{
		p=vector->index;
		while(p->next!=NULL){
			p=p->next;
		}
		p->next=r;
	}
	vector->total_index_elements+=INDEX_LINK_EACH_NUM;
	vector->index_node_num++;
	return 1;
}
int kvector_add_data_page(kvector* vector){
	index_link_node* node;
	int r,i;
	void* page;
	int cpage=vector->current_index_pos/INDEX_LINK_EACH_NUM;
	if(vector->current_index_pos>=vector->total_index_elements){
		r=kvector_index_add_link_node(vector);
		if(!r) return 0;
	}
	page=kvector_get_free_page();
	if(!page) return 0;
	node=vector->index;
	for(i=0;i<cpage;i++){
		node=node->next;
	}
	i=vector->current_index_pos%INDEX_LINK_EACH_NUM;
	node->data_pages[i]=page;
	vector->current_index_pos++;
	return 1;
}
int kvector_init(kvector* vector,int init_size){
	int r;
	int i;
	int init_page_num;
	init_page_num=init_size/(ELEMENTS_PER_PAGE)+(init_size%(ELEMENTS_PER_PAGE)==0 ? 0:1);
	vector->size=init_size;
	vector->current_index_pos=0;
	vector->total_index_elements=0;
	vector->index_node_num=0;
	vector->index=NULL;
	for(i=0;i<init_page_num;i++){
		r=kvector_add_data_page(vector);
		if(!r){
			kvector_destroy(vector);
			return 0;
		}
	}
	return 1;
}
int kvector_resize(kvector* vector,int new_size){
	int add_page_num;
	int i,r;
	if(new_size<vector->size) return vector->size;
	add_page_num=(new_size/(ELEMENTS_PER_PAGE)+(new_size%(ELEMENTS_PER_PAGE)==0 ? 0:1))-vector->current_index_pos;
	for(i=0;i<add_page_num;i++){
		r=kvector_add_data_page(vector);
		if(!r){
			return vector->size;
		}
		vector->size+=(ELEMENTS_PER_PAGE);
	}
	return vector->size;
}
void kvector_destroy(kvector* vector){
	index_link_node *node,*t;
	int i,j;
	i=0;
	node=vector->index;
	while(node!=NULL){
		for(j=0;j<INDEX_LINK_EACH_NUM;j++){
			if(i>=vector->current_index_pos) break;
			kvector_free_page(node->data_pages[j]);
			i++;
		}
		t=node;
		node=node->next;
		kvector_free_page(t);
	}
}
void kvector_foreach_page(kvector* vector,KVECTOR_EACH_PAGE_CB callback,void* param){
	index_link_node *node;
	int i,j;
	i=0;
	node=vector->index;
	while(node!=NULL){
		for(j=0;j<INDEX_LINK_EACH_NUM;j++){
			if(i>=vector->current_index_pos) break;
			if(!callback(node->data_pages[j],param)) return;
			i++;
		}
		node=node->next;
	}
}
int kvector_set(kvector* vector,int index,void* mem){
	index_link_node* node;
	int page_index;
	int page_mem_index;
	int node_index;
	int node_page_index;
	char* page_mem;
	int i;
	if(index>vector->size) return 0;
	page_index=index/(ELEMENTS_PER_PAGE);
	page_mem_index=index%(ELEMENTS_PER_PAGE);
	node_index=page_index/INDEX_LINK_EACH_NUM;
	node_page_index=page_index%INDEX_LINK_EACH_NUM;
	
	node=vector->index;
	for(i=0;i<node_index;i++){
		node=node->next;
	}
	page_mem=(char*)node->data_pages[node_page_index];
	page_mem+=(page_mem_index*ELEMENT_LENGTH);
	memcpy(page_mem,mem,ELEMENT_LENGTH);
	return 1;
}
int kvector_get(kvector* vector,int index,void* mem){
	index_link_node* node;
	int page_index;
	int page_mem_index;
	int node_index;
	int node_page_index;
	char* page_mem;
	int i;
	if(index>vector->size) return 0;
	page_index=index/(ELEMENTS_PER_PAGE);
	page_mem_index=index%(ELEMENTS_PER_PAGE);
	node_index=page_index/INDEX_LINK_EACH_NUM;
	node_page_index=page_index%INDEX_LINK_EACH_NUM;
	
	node=vector->index;
	for(i=0;i<node_index;i++){
		node=node->next;
	}
	page_mem=(char*)node->data_pages[node_page_index];
	page_mem+=(page_mem_index*ELEMENT_LENGTH);
	memcpy(mem,page_mem,ELEMENT_LENGTH);
	return 1;
}

Leave a comment

Your email address will not be published. Required fields are marked *