发信人: arira(大天使)
整理人: qiaoqian(2001-12-31 00:01:56), 站内信件
|
主题:《Linux内核模块编程指南》(十一)
发信人: kevintz()
整理人: kevintz(2000-06-24 00:44:14), 站内信件
《Linux内核模块编程指南》
《Linux Kernel Module Programming Guide》
作者:Ori Pomerantz 中译者:谭志([email protected])
译者注:
1、LKMPG是一本免费的书,英文版的发行和修改遵从GPL version 2的许可。为了
节省时间,我只翻译了其中的大部分的大意,或者说这只是我学习中的一些中文
笔记吧,不能算是严格上的翻译,但我认为这已经足够了。本文也允许免费发布
,但发布前请和我联系,但不要把本文用于商业目的。鉴于本人的水平,文章中
难免有错误,请大家不吝指正。
2、本文中的例子在Linux(kernel version 2.2.10)上调试通过。你用的Linux必
须支持内核模块的加载,如果不支持,请在编译内核时选上内核模块的支持或升
级你的内核到一个支持内核模块的版本。
第十一章 任务调度
通常,我们会有一些程序是在某一个特定时间运行的。如果任务是用进程来
完成,我们可以把它放在crontab文件里,如果任务是由内核模块完成的,我们有
两种可行的方法。一是将一个进程放到crontab文件中,由这个进程在需要的时候
用系统调用唤醒模块,例如打开一个文件。这是十分低效的,我们要生成一个脱
离crontab的新进程,读入新的可执行文件到内存里,然后唤醒在内存中的内核模
块。
另一个方法是我们建立一个函数,当时钟每中断一次,它都被调用一次。这
可以通过建立一个任务(task),放进tq_struct结构体(tq_struct结构体含有这个
函数的指针)里,然后用queue_task把任务放到tq_timer的任务队列来实现。tq_
timer任务队列中的任务将会在下一次时钟中断时被执行,由于我们想一直执行该
函数,所以当该任务被调度执行时,我们要把它放回到tq_timer队列,以便下一
次中断被执行。
我们还有一点要记住。当一个模块用rmmod卸载时,先是它的参考计数器(re
ference count)被检查。如果为零,module_cleanup被调用,然后模块从内存中
移去,函数不再存在内存里。时钟任务队列tq_timer中的任务是不会检查函数指
针指向的函数是否还在内存里。很长一段时间后(这是从计算机的角度看。如果从
人的角度看,少于百分之一秒),内核产生时钟中断并尝试调用任务队列里的函数
。但函数已经不再存在,大多数情况下,函数原来使用的内存页没被使用,你会
得到一个错误。但一个新函数占据了相同的位置,事情将变得不可预测。不幸的
是,我们没有一个简单的方法移去任务列表里的任务。
由于cleanup_module不能返回错误码(它是声明为void型的函数),解决方法
不是让它就这样返回,而是调用sleep_on或module_sleep_on来使rmmod进程进入
睡眠状态。在这之前,它用一个全局变量的方法通知中断队列里的函数不再需要
与自身关联。下一次时钟中断时,rmmod被唤醒,我们的函数不再在队列里(因为
已经被调用一次,并且不再放到tq_timer中了),就可以安全地移去内核模块了。
例子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 <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
/* Necessary because we use the proc fs */
#include <linux/proc_fs.h>
/* We scheduale tasks here */
#include <linux/tqueue.h>
/* We also need the ability to put ourselves to sleep
* and wake up later */
#include <linux/sched.h>
/* 记录中断函数已被调用的次数*/
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 */
/*由本模块形成的等待队列,本例是把rmmod放到WaitQ中*/
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 */
/*队列中的下一项,由queue_task自动填充*/
0, /* A flag meaning we haven't 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)
/*有rmmod调用进程在WaitQ队列里,说明下次不再被中断*/
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\n",
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 don't
* 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
* file's 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);
/* 注册proc文件*/
return proc_register(&proc_root, &Our_Proc_File);
}
/* 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 we'll
* deallocate the memory holding intrpt_routine and
* Task while tq_timer still references them.
* Notice that here we don't allow signals to
* interrupt us.
*
* Since WaitQ is now not NULL, this automatically
* tells the interrupt routine it's time to die. */
/* 将调用本函数的当前进程放到等待队列里睡眠,*
* 这里是rmmod进程*/
sleep_on(&WaitQ);
}
编译和测试:
编译后,insmod,然后不断cat /proc/sched看结果的变化。
--
那一刹那,我开始用心去看这个世界,所有的事物真的可以看得前
所未有的那么清楚……
|
|