科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Linux系统内核编程之实现调度任务

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

经常地,我们有必须定时做或者经常做的“家务事”。如果这个任务由一个进程完成,我们可以把通过把它放入crontab文件而做到。

作者:赛迪网技术社区 来源:赛迪网技术社区 2007年10月19日

关键字: 内核 系统 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); 
}
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章