linux如何实现任务调度,Linux系统内核编程之实现调度任务

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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值