经常地,我们有必须定时做或经常做的“家务事”。假如这个任务由一个进程完成,我们能够把通过把他放入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);

            }

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!