在linux中斷編程中,需要中斷程序分成中斷頂部和中斷底部兩部分,頂部負責做中斷標誌,然後耗時的事情在中斷底部執行。頂部不能被其他進程中斷,而底部可以被其他進程中斷,也可以被硬中斷事件中斷。底部分代碼實現可以通過內核共享工作隊列實現。
1)核心結構
Workqueue.h include\Linux
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /* 工作函數指針 */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
工作函數原型:
typedef void (*work_func_t)(struct work_struct *work);
unc指針所指向的函數:一般是就需要延後執行代碼。一般使用編寫中斷底部代碼。
內核使用這個結構來描述一個工作,一個工作簡單理解就是對應於一個函數,可以通過內核調度函數來調用work_struct中func指針所指向的函數。
work_func_t函數中的參數就是指向struct work_struct 結構本身。
利用這個特徵可以實現在工作函數中訪問自定的結構中的其他成員,如:自定義一個數據結構,內部嵌入一個 struct work_struct。
struct myvar{
struct work_struct work;
int x;
int y;
}
struct myvar var;
關鍵宏:
INIT_WORK(_work, _func) :初始化一個work結構
_work: struct work_struct work結構指針
_func:用來填充work_struct work結構的fun指針(work的具體代碼)
INIT_WORK(&var.work, work_func);/* 把work放置內核共享工作隊列中*/
工作函數:
void work_func (struct work_struct *work)
{
//因爲傳遞進入的work剛剛好是位於 struct myvar 起始位置
struct myvar *p = ( struct myvar*)work;
p->x = 12;
p->y = 23;
}
調度宏:
schedule_work(_work):一個宏,對應一個函數;作用是調度一個一個工作_work。
_work:要調度工作的結構指針;
schedule_work(&work);
使用共享工作隊列步驟:
1.定義一個工作結構變量
struct work_struct work;
2.初始化工作結構—重點func成員:
先編寫一個工作函數:
void work_func(struct work_struct * dat){
……
printk(“%p:”,dat);
……
}
初始化work:
INIT_WORK(&work, work_func);
3.在適當的地方調度工作(如果工作用於中斷底部代碼,則在中斷頂部調度)
schedule_work(&work); ――>不是馬上執行,而是等待CPU空閒才執行work_func
內核共享延時工作隊列:
和上面的內核共享工作隊列一樣,可以實現中斷的底部代碼功能。和內核共享工作隊列惟一區別就是它可以指定一個延時時間,調度工作函數。
關鍵數據結構:
//延時工作隊列
struct delayed_work {
struct work_struct work; //工作結構
struct timer_list timer; //內核定時器結構
};
初始化函數:
INIT_DELAYED_WORK(_work, _func)
初始化一個delayed_work結構
_work: struct delayed_work work結構指針
_func:用來填充struct delayed_work work結構的fun指針(work的具體代碼)
調度函數:
int schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
dwork:要調度的延時工作結構指針;
delay:延後調度的時間,單位是時鐘節拍。和內核定時器定時時間單位相同,但是不是到期時間,而是定時時間。
應用延時工作隊列步驟:
- 定義一個struct delayed_work結構變量
- 初始化struct delayed_work結構
- 在適當地方調用工作。
步驟和前面的共享工作隊列完全相同。
問題:如果結構定義如下:
struct myvar{
int x;
int z ;
struct work_struct work;
int y;
}
上面的方法,就需要對 work- 8 再轉換成 struct myvar指針,纔可以訪問x,y,.(不考慮結構的內存對齊)。如果發後需要增加其他成員,代碼又要修改,此法,通用性太差。
(type*)0 :相當於在0地址定義了一個結構變量
((type *)0)->member:訪問結構成員member
typeof( ((type *)0)->member)->得成成員類型。typeof :求出變量的類型。如:int a;typeof(a) --> int
typeof( ((type *)0)->member) *__mptr = ptr -- 定義一個__mptr 和成員類型相同 的指針指向成員地址
offsetof(type,member) ---offsetof:得到 member 成員在type中爲偏移,和變量在內存位置 無關。
(char *)__mptr - offsetof(type,member) ---得到具體結構變量首地址--字節地址。
(type *)( (char *)__mptr - offsetof(type,member) ) --- 轉換成結構類型地址。
內核提供了一個宏,可以通過結構體中任何一個成員地址反推出他所在的變量結構所在的內存首地址。
如果上面的結構,只要知道 ,x,y,z,work任何一個地址,都可以知道整個結構體變量的內存首地址。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
ptr:是已知的成員指針
type:結構體類型
member:就是ptr所指的成員
使用方法:
container_of(ptr, type, member)
struct myvar{
int x;
int z ;
struct work_struct work;
int y;
} a,b ; ---》定義了兩個變量
container_of(&a.z, struct myvar, z) 結果 ---- > &a
container_of(&b.y, struct myvar, y) 結果 ---- > &b
/*
* 使用內核共享工作隊列,演示container_of用法
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
typedef struct __mydat{
int x;
int y;
struct work_struct mywork;
int z;
} mydat_t;
static void work_handler(struct work_struct *data)
{
mydat_t *p;
/* 計算第二個參數結構體首地址 */
p = container_of(data, mydat_t, mywork);
//mydat_t *p = (mydat_t *)data;
//printk(KERN_EMERG "data:%p,\n", data);
//printk(KERN_EMERG "x:%d,\n", ((mydat_t *)data)->x);
//printk(KERN_EMERG "y:%d,\n", ((mydat_t *)data)->y);
printk(KERN_EMERG "x:%d,\n", p->x);
printk(KERN_EMERG "y:%d,\n", p->y);
printk(KERN_EMERG "work handler function KKKKKKKKK.\n");
printk(KERN_EMERG "work handler function LLLLLLLLL.\n");
}
static int __init test_init(void)
{
//struct work_struct work;
static mydat_t work;
work.x = 123;
work.y = 456;
//printk(KERN_EMERG "&work:%p,\n", &work);
/* 把work放置內核共享工作隊列中*/
INIT_WORK(&work.mywork, work_handler);
/* 調用工作 */
schedule_work(&work.mywork);
return 0;
err:
return -1;
}
static void __exit test_exit(void)
{
//destroy_workqueue(queue);
printk("%s is call\r\n", __FUNCTION__);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);