首页 | 资讯动态 | linux基础 | 系统管理 | 网络管理 | 编程开发 | linux数据库 | linux相关 | linux认证 | 下载中心 | 专题
oklinux
 系统管理:中文环境 系统管理 桌面应用 内核技术 Linux基础:安装配置 常用命令 经验技巧 软件应用 Linux数据库:Mysql POSTGRE
 网络管理:网络安全 网络应用 Linux服务器 编程开发:PHP CC++ Python Perl SHELL 嵌入式开发 | PHP基础 PHP技巧 PHP应用 PHP文摘
 首页 linux资讯动态 Linux专题 | 其他Unix Linux解决方案 硬件相关 Linux认证 企业应用 Apache | 相关下载:资料 参考手册 服务器
 → 当前位置:首页>系统管理>内核技术>正文

Linux系统内核编程之实现调度任务

OKLinux www.oklinux.cn 2007-02-12 来源:oklinux收集整理 会员收藏 游客收藏

经常地,我们有必须定时做或者经常做的“家务事”。如果这个任务由一个进程完成,我们可以把通过把它放入crontab文件而做到。如果这个任务由一个Linux内核模块完成,我们有两种可能的选择。第一种是把一个进程放入crontab文件,它将在必要的时候通过一个系统调用唤醒模块,比如打开一个文件。然而,这样做时非常低效的,我们需要运行一个crontab外的新进程,把一个新的执行表读入内存,而所有这些只是为了唤醒一个内存中的内核模块。

我们不需要这样做。我们可以创建一个函数,在每个时间中断时被调用。方法是创建一个任务,包含在一个结构体tq_struct里,在此结构中包含一个指向函数入口地址的指针。然后,我们使用queue_task把这个任务放入一个叫做tq_timer的任务列表中,这是一个在下次时间中断时要执行的任务列表。因为我们希望这个函数被持续执行,我们需要在每次调用厚把它放回tq_timer中以备下次时间中断。

这里还有一点需要记住。当一个模块被rmmod删除时,首先他的索引计数器被检查。如果是0,就调用module_cleanup。然后,这个模块以及它的所有函数都从内存中删除。没有人检查是否时钟的任务列表仍然包含指向这些函数的指针,而现在已不可用。很久以后(从计算机看来,在人的眼睛里是很短的,可能是百分之一秒),内核有了一个时钟中断,试图调用任务列表中的所有函数。不幸的是,这个函数已不存在。在多数情况下,它所在的内存还未被使用,而你得到了一个极端错误的信息。但是,如果有别的代码出在相同的地址,情况会非常糟糕。不幸的是,我们没有一个从任务列表中注销一个任务的方法。

既然cleanup_module函数不能返回一个错误马(它是void型函数),那么解决方法是就不要让它返回。而是调用sleep_on或module_sleep_on(注10.1)把rmmod进程挂起。在此之前,它设置一个变量通知在时钟中断时调用的函数停止附加自己。那么,在下次时钟中断时,rmmod进程将被唤醒,而我们的函数已经不在队列中,这样就可以很安全的删除模块。

下面是具体的实现代码:

ex sched.c 

/* sched.c - scheduale a function to be called on 
* every timer interrupt. */ 



/* Copyright (C) 1998 by Ori Pomerantz */ 


/* The necessary header files */ 

/* Standard in kernel modules */ 
#include /* Were doing kernel work */ 
#include /* Specifically, a module */ 

/* Deal with CONFIG_MODVERSIONS */ 
#if CONFIG_MODVERSIONS==1 
#define MODVERSIONS 
#include 
#endif 

/* Necessary because we use the proc fs */ 
#include 

/* We scheduale tasks here */ 
#include 

/* We also need the ability to put ourselves to sleep 
* and wake up later */ 
#include 

/* In 2.2.3 /usr/include/linux/version.h includes a 
* macro for this, but 2.0.35 doesnt - so I add it 
* here if necessary. */ 
#ifndef KERNEL_VERSION 
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) 
#endif 



/* The number of times the timer interrupt has been 
* called so far */ 
static int TimerIntrpt = 0; 


/* This is used by cleanup, to prevent the module from 
* being unloaded while intrpt_routine is still in 
* the task queue */ 
static struct wait_queue *WaitQ = NULL; 

static void intrpt_routine(void *); 


/* The task queue structure for this task, from tqueue.h */ 
static struct tq_struct Task = { 
NULL, /* Next item in list - queue_task will do 
* this for us */ 
0, /* A flag meaning we havent been inserted 
* into a task queue yet */ 
intrpt_routine, /* The function to run */ 
NULL /* The void* parameter for that function */ 
}; 



/* This function will be called on every timer 
* interrupt. Notice the void* pointer - task functions 
* can be used for more than one purpose, each time 
* getting a different parameter. */ 
static void intrpt_routine(void *irrelevant) 
{ 
/* Increment the counter */ 
TimerIntrpt++; 

/* If cleanup wants us to die */ 
if (WaitQ != NULL) 
wake_up(&WaitQ); /* Now cleanup_module can return */ 
else 
/* Put ourselves back in the task queue */ 
queue_task(&Task, &tq_timer); 
} 




/* Put data into the proc fs file. */ 
int procfile_read(char *buffer, 
char **buffer_location, off_t offset, 
int buffer_length, int zero) 
{ 
int len; /* The number of bytes actually used */ 

/* This is static so it will still be in memory 
* when we leave this function */ 
static char my_buffer[80]; 

static int count = 1; 

/* We give all of our information in one go, so if 
* the anybody asks us if we have more information 
* the answer should always be no. 
*/ 
if (offset > 0) 
return 0; 

/* Fill the buffer and get its length */ 
len = sprintf(my_buffer, 
""Timer was called %d times so far "", 
TimerIntrpt); 
count++; 

/* Tell the function which called us where the 
* buffer is */ 
*buffer_location = my_buffer; 

/* Return the length */ 
return len; 
} 


struct proc_dir_entry Our_Proc_File = 
{ 
0, /* Inode number - ignore, it will be filled by 
* proc_register_dynamic */ 
5, /* Length of the file name */ 
""sched"", /* The file name */ 
S_IFREG | S_IRUGO, 
/* File mode - this is a regular file which can 
* be read by its owner, its group, and everybody 
* else */ 
1, /* Number of links (directories where 
* the file is referenced) */ 
0, 0, /* The uid and gid for the file - we give 
* it to root */ 
80, /* The size of the file reported by ls. */ 
NULL, /* functions which can be done on the 
* inode (linking, removing, etc.) - we dont 
* support any. */ 
procfile_read, 
/* The read function for this file, the function called 
* when somebody tries to read something from it. */ 
NULL 
/* We could have here a function to fill the 
* files inode, to enable us to play with 
* permissions, ownership, etc. */ 
}; 


/* Initialize the module - register the proc file */ 
int init_module() 
{ 
/* Put the task in the tq_timer task queue, so it 
* will be executed at next timer interrupt */ 
queue_task(&Task, &tq_timer); 

/* Success if proc_register_dynamic is a success, 
* failure otherwise */ 
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0) 
return proc_register(&proc_root, &Our_Proc_File); 
#else 
return proc_register_dynamic(&proc_root, &Our_Proc_File); 
#endif 
} 


/* Cleanup */ 
void cleanup_module() 
{ 
/* Unregister our /proc file */ 
proc_unregister(&proc_root, Our_Proc_File.low_ino); 

/* Sleep until intrpt_routine is called one last 
* time. This is necessary, because otherwise well 
* deallocate the memory holding intrpt_routine and 
* Task while tq_timer still references them. 
* Notice that here we dont allow signals to 
* interrupt us. 
* 
* Since WaitQ is now not NULL, this automatically 
* tells the interrupt routine its time to die. */ 
sleep_on(&WaitQ); 
}
上一篇:借助异常表解决Linux内核态缺页异常   下一篇:改变Linux系统内核驱动初始化顺序

收藏于收藏夹】 【评论】 【推荐】 【投稿】 【打印】 【关闭

相关文章
·借助异常表解决Linux内核态缺页异常
·改变Linux系统内核驱动初始化顺序
·关于Linux系统内核抢占补丁的原理说明
·vmware下将RH9内核升级至2.6
·内核编译过程之前
·RedHat Linux中三个重要内核文件
·教菜鸟编译自己的内核2.6.19
·ubuntu 6.10 下编译2.6.19内核并安装NVIDIA
·Linux内存中Swap和Buffer Cache机制
·linux2.6内核启动传递命令行分析
发表评论
密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。)
站内搜索
阅读排行榜
·基于S3C44B0微处理器的uCl
· 深入理解LINUX内核中文版
·Kernel command using Lin
·Linux操作系统的内核编译
·Process priority and con
·Linux 2.6内核如何武装Fed
·通过振动向Linux ThinkPad
·主流嵌入式Linux系统下GUI
·用命令行加挂Linux的文件
·推荐:Linux用户态与内核
最新文章
·用Tftp向目标板烧写Linux
·交叉编译Linux内核(2.6.22
·UNIX操作系统的加锁解锁:
·基于2.6.9内核小型Linux系
·Linux系统中使用GCC CPU参
·Linux系统的引导过程详细
·Linux系统的内核初始化过
·Linux系统高手之路 内核编
·Linux高手之内核编译过程
·Linux系统内核模块和驱动
·Linux系统下编译支持NTFS
·Linux系统中用内核KHTTPD
·Linux系统内核分析 使用GD
·Linux操作系统的内核解读
·通过Linux系统的内核观察/

设为首页 - 加入收藏 - 版权声明 - 广告服务 - 关于我们 - 联系我们 - 友情连接
Copyright © 2007 All rights reserved OKLinux.Cn 版权所有