歡迎您光臨本站 註冊首頁

Linux內核編程(調度任務)

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  著者:Ori Pomerantz
翻譯:徐輝

10.調度任務
經常地,我們有必須定時做或者經常做的「家務事」。如果這個任務由一個進程完成,我們可以把通過把它放入crontab文件而做到。如果這個任務由一個內核模塊完成,我們有兩種可能的選擇。第一種是把一個進程放入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\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 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內核編程(調度任務)已經有422次圍觀

http://coctec.com/docs/program/show-post-72303.html