linux内核重要函数(kmalloc等)

内核内存分配函数

  1. 函数名 kmalloc
    头文件<linux/slab.h>

    函数原型: void *kmalloc(size_t size,int flags)
    参数:
    (1)size:需要分配的内存大小
    (2)flag:分配标志,它控制 kmalloc 的行为
    GFP_ATOMIC:用来在进程上下文中之外的代码(包含中断)中分配内存,从不睡眠
    GFP_KERNEL:在进程上下文中分配,可能会睡眠(16M-896M)
    _GFP_DMA:要求分配能够DMA的内存区(在16M以下的页帧)
    _GFP_HIGHMEM:分配内存位于高端内存(896M以上)

**按页分配:**如果模块需要分配大块内存,那么使用面向页的分配技术(一页的大小:4K)

  1. get _zeroed_page(unsigned int flags)
    返回指向新页面的指针,并将页面清零
  2. __get _free_page(unsigned int flags)
    返回指向新页面的指针,但是页面不清零
  3. __get _free_pages(unsigned int flags,unsigned int older)
    分配若干连续页面,返回指向该内存区域的指针,但不清零这段内存区域

在分配完内存后,使用完之后,需要释放所分配的内存区域

内存释放函数:

  • void free_page(unsigned long addr)
  • void free_pages(unsigned long addr,unsigned long order)

永久内存映射区

对于896MB以上的高端内存,可以使用该区域来访问
方法;

  • 使用 alloc_page(_GFP_HIGHMEM) 来分配高端内存
  • 使用 kmap 函数将分配到的高端内存,映射到该区域

内核链表

链表在C语言中对于大家来说都不陌生:

*定义
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作。

而在linux内核中也有链表的数据结构,在linux内核中使用大量的链表结构来组织来数据,他与传统链表的区别是:
在这里插入图片描述

头文件: linux/list.h

链表数据结构定义:

 struct list_head
 {
		struct list_head *next,*prev;		
 }

内核链表具有 双向链表 的功能:实际上是:双向循环链表

linux内核中提供的链表操作主要有:

  1. 初始化链表头
    INIT_LIST_HEAD(list_head *head)

  2. 插入节点
    list_add(struct list_head *new,struct list_head *head)
    list_add_tail(struct list_head *new,struct list_head *head) //尾插

  3. 删除节点
    list_del(struct list_head *entry)

  4. 提取数据结构
    list_entry(ptr,type,member) 返回指针
    参数:
    ptr : 指向 list_head 的指针
    type:类型
    member:成员名

  5. 遍历:
    list_for_each(struct list_head *pos,struct list_head *head)
    参数 head 为遍历的链表头,pos 为位置

内核链表的实现举例:

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

		MODULE_LICENSE("GPL");
		MODULE_AUTHOR("xxx");
		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;

		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(i = 0; i < 5; i++)
		        {
		                list_del(&(pstudent[i].list));
		        }
		
		        kfree(pstudent);
		}

		module_init(mylist_init);
		module_exit(mylist_exit);

然后编写相应的 Makefile

				ifneq ($(KERNELRELEASE),)

				obj-m := list.o 
				
				else
				
				KDIR := /home/zhangbin/mini6410/linux-2.6.38 
				all:
				        make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-
				clean:
				        rm -f *.ko *.o *.mod.c *.symvers
				
				endif
                                                                                            

之后把生成 .ko 文件拷贝到开发板(这里用到的开发板是 mini6410 )中,之后通过指令

		  insmod list.ko

把链表模块安装到内核中去。

演示效果如下:
在这里插入图片描述

内核定时器

介绍:内核定时器被组织成双向链表,并使用 struct timer_list 结构

定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行。内核定时器注册的处理函数只执行一次–不是循环执行。

在介绍定时器之前先介绍几个概念:

度量时间差

时钟中断由系统的定时硬件以周期性的时间间隔产生,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结构无关的常数,可以配置(50-1200),在x86平台,默认值1000

每当时钟中断发生时,全局变量 jiffies(unsigned long)加1,因此 jiffies 记录了自linux启动后时钟中断发生的次数。驱动程序常常利用jiffies来计算不同事件今年的时间间隔。

延时执行

如果对延时的精度要求不高,最简单的实现方法如下—忙等待

	  unsigned long j = jiffies + jit_delay*HZ;
	  while(jiffies < j)
	  {
			// do nothing
	  }	
		struct timer_list
		{
			struct list_head entry;   //内核使用
			unsigned long expires;   //超时的jiffies值
			void (*function)(unsigned long );  //超时处理函数
			unsigned long data;  //超时处理函数参数
			struct tvec_base *base;  //内核使用
		}

一些 相关的函数*

  1. void init_timer(struct timer_list *timer) .
    初始化定时器队列结构
  2. void add_timer(struct timer_list *timer)
    启动定时器
  3. void del_timer(struct timer_list *timer)
    在定时器超时前将它删除,定时器超时后,系统会自动将它删除

内核定时器代码实现:

		#include <linux/kernel.h>
		#include <linux/module.h>
		#include <linux/init.h>
		#include <linux/timer.h>
		#include <asm/uaccess.h>
		
		MODULE_LICENSE("GPL");
		MODULE_AUTHOR("zhangbin");
		MODULE_DESCRIPTION("Timer Module");
		MODULE_ALIAS("timer module");
		
		struct timer_list timer;
		
		void timer_function(int para)
		{
		        printk("<0>Timer Expired and para is %d !!\n",para);
		}

		int timer_init()
		{
		        init_timer(&timer);
		        timer.data = 5;
		        timer.expires = jiffies + (3 * HZ);   //2表示2s后执行处理函数
		        timer.function = timer_function;
		        add_timer(&timer);
		
		        return 0;
		}
		
		void timer_exit()
		{
		        del_timer(&timer);
		}
		
		module_init(timer_init);
		module_exit(timer_exit);

同样需要编写 Makefile 如下:

	 ifneq ($(KERNELRELEASE),)

	obj-m := timer.o 
	
	else
	
	KDIR := /home/zhangbin/mini6410/linux-2.6.38 
	all:
	        make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-
	clean:
	        rm -f *.ko *.o *.mod.c *.symvers
	
	endif

执行 make 生成 timer.ko 文件,将timer.ko 文件拷贝到开发板(mini6410)中进行测试
命令: insmod timer.ko

执行效果如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41782149/article/details/89294965