Linux核心模組程式設計--替任務排程(轉)

worldblog發表於2007-08-10
Linux核心模組程式設計--替任務排程(轉)[@more@]

  任務排程

  常常的,我們有‘家務管理’的任務需要在某個時間做或者偶爾經常如此。如果任務由程式完成,我們可以將它放在 crontab 檔案中。如果任務由核心模組完成,我們有兩種可能。第一個是在 crontab 檔案中放置一個在必要的時候透過系統呼叫喚醒模組的程式,例如透過開啟檔案。這是非常低效的,然而--我們執行一個不在 crontab 中的新程式, 讀一個新的可執行的程式到記憶體,而所有這些只是喚醒在記憶體中的核心模組。

  替代的,我們可以建立一個對每個定時器中斷被呼叫一次的函式。我們的辦法是建立一個包含在 tq_struct結構中的任務,而該結構包含該函式的指標。然後我們使用 queue_task 將那個任務放置在被稱為tq_timer 的任務列表中,該列表是在下一個定時器中斷將被執行的任務的列表。因為我們我們想該函式在下一次定時器中斷時繼續被執行,我們需要在它被呼叫後將它放回 tq_timer。

  這還有一點我們需要記住的。當一個模組被 rmmod 移除時,它的引用計數器首先被檢查,如果它為0,module_cleanup 將被呼叫。然後模組連同它的所有函式被從記憶體中清除。沒有人去檢檢視在定時器任務列表中是否碰巧包含一個這樣的不再可見的函式的指標。一段時間後(從計算機的觀點看,而從人的觀點看它什麼也不是,它少於百分之一秒),核心有了一個定時器中斷並試圖去呼叫任務列表中的函式。不幸的,那個函式不在那兒。在大多情況下它剛才所在記憶體頁沒有被使用,而你會得到一個難看的錯誤訊息。但是如果別的某些程式碼現在位於同一個記憶體位置,事情會變得 非常 難看。不幸的,我們沒有一個簡單的辦法將一個任務從任務列表中登出。

  既然 cleanup_module 不能返回錯誤程式碼(它是一個void函式),解決的辦法是根本不讓它返回。替代的,它呼叫sleep_on 或 module_sleep_on(他們實際上是相同的。 )使 rmmod 程式睡眠。在此之前,它透過設定一個全域性變數通知在定時器中斷將被呼叫的函式停止連線自己。然後,在下一次定時器中斷, rmmod程式被喚醒,當我們的函式不再在那個佇列中時移除那個模組就是安全的了。

  範例 sched.c

  /* sched.c - 安排一個函式在每次定時器中斷時被呼叫 */

/* Copyright (C) 1998 by Ori Pomerantz */

/* 必要標頭檔案 */

/* 標準標頭檔案 */

#include /* 核心工作 */

#include /* 明確指定是模組 */

/* 處理 CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include

#endif

/* 我們使用 proc 檔案系統所必要的 */

#include

/* 我們在這兒安排任務 */

#include

/* 我們也需要睡眠和喚醒的能力 */

#include

/* 2.2.3 版/usr/include/linux/version.h 包含該宏

* 但2.0.35 版不包含 - 加入以備需要 */

#ifndef KERNEL_VERSION

#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))

#endif

/* 定時器中斷已經被呼叫的次數 */

static int TimerIntrpt = 0;

/* 這被清除模組使用,以防止當 intrpt_routine 仍在任務佇列裡時模組被清除 */

static struct wait_queue *WaitQ = NULL;

static void intrpt_routine(void *);

/* 這個任務的任務佇列結構,來自 tqueue.h */

static struct tq_struct Task = {

NULL, /* 列表的下一項 - queue_task 將為我們做這個 */

0, /* 一個標誌,意思是我們還沒有被插入任務佇列 */

intrpt_routine, /* 執行的函式 */

NULL /* 函式的void* 引數 */

};

/* 這個函式將在每次定時器中斷時被呼叫。注意 void* 指標 -

* 任務函式可以用於多個目的,每次得到不同的引數。 */

static void intrpt_routine(void *irrelevant)

{

/* 增加計數器 */

TimerIntrpt++;

/* 如果清除模組想我們死亡 */

if (WaitQ != NULL)

wake_up(&WaitQ); /* 現在 cleanup_module 可以返回 */

else

/* 將我們放回任務佇列 */

queue_task(&Task, &tq_timer);

}

/* 將資料放入proc 檔案 */

int procfile_read(char *buffer,

char **buffer_location, off_t offset,

int buffer_length, int zero)

{

int len; /* 實際使用的位元組數 */

/* 這是靜態的因此當我們離開這個函式時它仍然在記憶體中 */

static char my_buffer[80];

static int count = 1;

/* 我們將所有的資訊放在一個裡面,因此當有人問我們是否有更多 資訊時答案是否。 */

if (offset > 0)

return 0;

/* 填充緩衝區並得到它的長度 */

len = sprintf(my_buffer,

"Timer was called %d times so far ",

TimerIntrpt);

count++;

/* 告訴呼叫我們的函式緩衝區在哪兒 */

*buffer_location = my_buffer;

/* 返回長度 */

return len;

}

struct proc_dir_entry Our_Proc_File =

{

0, /* 節點數 - 忽略,它將被 proc_register_dynamic 填充*/

5, /* 檔名長度 */

"sched", /* 檔名 */

S_IFREG | S_IRUGO,

/* 檔案模式 - 這是一個可以被其擁有者,使用者組和其他任何人讀取的普通檔案 */

1, /* 連線數 (檔案被引用的目錄)*/

0, 0, /* 檔案的UID和GID - 我們將它賦予root */

80, /* 由ls報告的檔案長度 */

NULL, /* 節點函式(連線,刪除,等等) - 不支援 */

procfile_read,

/* 檔案的讀函式,當某人試圖從中讀什麼時被呼叫 */

NULL

/* 可以在這兒設定一個填充檔案節點的函式,以使我們可以修改許可權,擁有關係等。 */

};

/* 初始化模組--登記 proc 檔案 */

int init_module()

{

/* 將任務放置在 tq_timer 任務佇列,因此在下次定時器中斷時它將被執行 */

queue_task(&Task, &tq_timer);

/* proc_register_dynamic 成功則成功,否則失敗 */

#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

}

/* 清除 */

void cleanup_module()

{

/* 登出 /proc 檔案 */

proc_unregister(&proc_root, Our_Proc_File.low_ino);

/* 睡眠,直到 intrpt_routine 上次被呼叫。這是必要的,因為否則我們將釋放 intrpt_routine

* 和tq_timer仍然引用的任務佔有的記憶體。注意不允許訊號中斷。

*

* 既然 WaitQ 現在不為 NULL,這自動的告訴中斷程式它死亡的時間到了。 */

sleep_on(&WaitQ);

}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-940218/,如需轉載,請註明出處,否則將追究法律責任。

相關文章