(十三)linux中斷底半部分處理機制

這篇文章介紹一下linux中斷的底半部分的tasklet和workquene兩種處理機制,其中tasklet中不能有延時函數,workquene的處理函數可以加入延時操作

目錄

      • (一)tasklet小任務處理機制
          • (1)tasklet相關函數接口
          • (2)tasklet使用流程
          • (3)tasklet實例代碼
      • (二)workquene工作隊列處理機制
          • (1)workqueue相關函數接口
          • (2)共享工作隊列使用流程
          • (3)自定義工作隊列使用流程
          • (4)共享workqueue實例代碼

在Linux中為了提高系統的響應速度及并發能力,將Linux的中斷劃分為頂半部和底半部兩部分。
頂半部(top half):做中斷的登記操作,當然也可以做不耗時的中斷處理(內核中會創建一個中斷登記表)。
頂半部完成的一般是緊急的硬件操作,一般包括讀取寄存的中斷狀態,清除中斷標志,將底半部處理程序掛到底半部的執行隊列中去,此過程不可被打斷
底半部(bottom half):處理耗時操作,把耗時的操作放入底半部執行,這個過程可以被打斷,耗時操作推后執行

(一)tasklet小任務處理機制

內核中關于tasklet的介紹:

/* Tasklets --- multithreaded analogue of BHs.Main feature differing them of generic softirqs: taskletis running only on one CPU simultaneously.Main feature differing them of BHs: different taskletsmay be run simultaneously on different CPUs.Properties:* If tasklet_schedule() is called, then tasklet is guaranteedto be executed on some cpu at least once after this.* If the tasklet is already scheduled, but its execution is still notstarted, it will be executed only once.* If this tasklet is already running on another CPU (or schedule is calledfrom tasklet itself), it is rescheduled for later.* Tasklet is strictly serialized wrt itself, but notwrt another tasklets. If client needs some intertask synchronization,he makes it with spinlocks.
(1)tasklet相關函數接口

小任務機制相關的數據結構:

struct tasklet_struct
{struct tasklet_struct *next;	//用來實現多個tasklet_struct結構鏈表unsigned long state;			//當前這個tasklet是否已經被調度atomic_t count;					//值為0的時候用戶才可以調度/*原子變量操作:指的是操作過程中不允許被打斷機制typedef struct {int counter;} atomic_t;	*/void (*func)(unsigned long);	//指向tasklet綁定的函數指針unsigned long data;				//傳向tasklet綁定的函數的參數
};

小任務數據結構創建:

#define DECLARE_TASKLET(name, func, data) \        //靜態初始化,默認為使能
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }#define DECLARE_TASKLET_DISABLED(name, func, data) \      //靜態初始化,默認為失能
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

初始化小任務:

extern void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);

小任務加鎖解鎖:

//嘗試加鎖
static inline int tasklet_trylock(struct tasklet_struct *t)
{return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}//解鎖
static inline void tasklet_unlock(struct tasklet_struct *t)
{smp_mb__before_clear_bit(); clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
/*** test_and_set_bit - Set a bit and return its old value* @nr: Bit to set* @addr: Address to count from** This operation is atomic and cannot be reordered.* It may be reordered on other architectures than x86.* It also implies a memory barrier.*/
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
{unsigned long mask = BIT_MASK(nr);unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);unsigned long old;unsigned long flags;_atomic_spin_lock_irqsave(p, flags);old = *p;*p = old | mask;_atomic_spin_unlock_irqrestore(p, flags);return (old & mask) != 0;
}

小任務登記:

static inline void tasklet_schedule(struct tasklet_struct *t)
{if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))__tasklet_schedule(t);
}

小任務失能:

static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{atomic_inc(&t->count);smp_mb__after_atomic_inc();
}static inline void tasklet_disable(struct tasklet_struct *t)
{tasklet_disable_nosync(t);tasklet_unlock_wait(t);smp_mb();
}

小任務使能:

static inline void tasklet_enable(struct tasklet_struct *t)
{smp_mb__before_atomic_dec();atomic_dec(&t->count);		//單純的將count減一操作
}

結束小任務:

extern void tasklet_kill(struct tasklet_struct *t);
extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);
(2)tasklet使用流程
  1. 定義結構體并初始化
struct tasklet_struct task;
tasklet_init(&task,自定義函數功能名,函數形參);
  1. 在合適的地方(一般在中斷里)對tasklet登記
   tasklet_schedule(&task);
(3)tasklet實例代碼

chrdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>struct tasklet_struct task;void tasklet_fun(unsigned long data)
{printk("this is tasklet test\n");
}static int __init  tasklet_module_init(void)
{tasklet_init(&task,tasklet_fun,(unsigned long)10);//tasklet_disable(&task);//失能后不能卸載該tasklettasklet_schedule(&task);return 0;
}static void __exit  tasklet_module_cleanup(void)
{tasklet_kill(&task);
}
module_init(tasklet_module_init);
module_exit(tasklet_module_cleanup);
MODULE_LICENSE("GPL");

(二)workquene工作隊列處理機制

工作隊列提供了將功能推遲到下半部分的通用方法。核心是工作隊列(struct workqueue_struct),這是工作所在的結構。內核中通過work_struct結構標識要延遲的工作和要使用的延遲功能。events / X內核線程(每個CPU一個)從工作隊列中提取工作,并激活下半部處理程序之一。

工作隊列是更新的延遲機制,已在2.5 Linux內核版本中添加。工作隊列不是通用的延遲機制,不像Tasklet那樣提供一站式的延遲方案,在該機制中,工作隊列的處理函數可以休眠(在Tasklet模型中是不可能的),工作隊列的延遲可能比任務小,但包含更豐富的API以進行工作延遲,延遲之前是通過keventd任務隊列管理,現在由名為events / X的內核工作線程管理。

在這里插入圖片描述

內核中有兩種工作隊列,一種是共享工作隊列,另一種是自定義工作隊列

共享工作隊列 :內核提供,用戶可直接使用,秩序調用對應的接口即可,更多的時候選擇共享消息隊列

自定義工作隊列:需要用戶手動創建,并手動銷毀

共享工作隊列自定義工作隊列
內核啟動期間會創建一個工作全局的工作隊列,所有的驅動都可以把自己延后執行的工作函數掛到這個共享工作隊列中。當你要執行工作不希望受到其他工作的影響時,可以自己創建一個工作隊列,然后把自己的工作放在自定義的工作隊列調度。
優點:不需要自己創建工作隊列,簡單,快捷,方便。優點:不會受到其他工作的影響,工作函數執行有保障。
缺點:可能會受到其他工作的影響,前面的工作阻塞,影響到后面的工作的執缺點:造成系統巨大開銷大,如果過多創建自定義工作隊列,會嚴重影響系統實時
(1)workqueue相關函數接口

工作隊列數據結構:

struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};typedef void (*work_func_t)(struct work_struct *work);

聲明并初始化工作隊列:

 1.靜態方式:
#define DECLARE_WORK(n, f)					\struct work_struct n = __WORK_INITIALIZER(n, f)#define DECLARE_DELAYED_WORK(n, f)				\struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f)2. 動態形式初始化:
#define INIT_WORK(_work, _func)					\do {							\__INIT_WORK((_work), (_func), 0);		\} while (0)

創建自定義工作隊列的時候使用:

extern int queue_work(struct workqueue_struct *wq, struct work_struct *work);

工作隊列登記:

extern int schedule_work(struct work_struct *work);
/*** schedule_work - put work task in global workqueue* @work: job to be done** Returns zero if @work was already on the kernel-global workqueue and* non-zero otherwise.** This puts a job in the kernel-global workqueue if it was not already* queued and leaves it in the same position on the kernel-global* workqueue otherwise.*/
int schedule_work(struct work_struct *work)
{return queue_work(system_wq, work);
}

根據結構體成員找到結構體首地址:

/*** container_of - cast a member of a structure out to the containing structure* @ptr:	the pointer to the member.* @type:	the type of the container struct this is embedded in.* @member:	the name of the member within the struct.**/
#define container_of(ptr, type, member) ({			\const typeof( ((type *)0)->member ) *__mptr = (ptr);	\(type *)( (char *)__mptr - offsetof(type,member) );})

container_of使用示例:

struct mywork{int m;int n;struct work_struct works;
}test;container_of根據結構體內部的某一成員獲取結構的首地址container_of(ptr, type, member) @ptr:	指向結構體成員的指針.  如 struct work_struct *works;* @type:	the type of the container struct this is embedded in.  結構體類型 struct mywork* @member: the name of the member within the struct.  works
(2)共享工作隊列使用流程

1.定義共享工作隊列結構體并初始化

struct work_struct works;
INIT_WORK(&works,workqueue_fun);

2.在合適位置(一般為中斷)對工作隊列登記

 schedule_work(&works);
(3)自定義工作隊列使用流程

1、創建工作隊列

struct workqueue_struct  my_workqueue;
struct workqueue_struct *create_workqueue(&my_workqueue);  
//struct workqueue_struct *create_singlethread_workqueue(const char *name);  
//create_workqueue函數會在系統中的每個處理器上創建一個線程(多線程),而create_singlethread_workqueue只是創建一個單一的線程,如果單個線程足夠使用,那么應該使用create_singlethread_workqueue函數。

2、創建任務

struct work_struct works;
INIT_WORK(&works,workqueue_fun);

3、提交任務,要將任務提交到工作隊列中,內核提供了下面兩個API:

int queue_work(struct workqueue_struct *wq, struct work_struct *work);  
int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);  

這兩個函數都會將任務提交到工作隊列中,使用queue_delayed_work函數,則提交的任務至少延時由參數delay指定的時間才被執行。

如果要取消工作隊列中的某個任務,使用cancel_delayed_work,原型如下:

int cancel_delayed_work(struct work_struct *work);  

如果任務在被執行之前取消,那么cancel_delayed_work函數返回非零值,調用該函數之后內核會確保被取消的任務不被執行。但是返回0,則表示任務已經被執行,因此調用cancel_delayed_work函數后,任務有可能仍在運行,所以為了確保任務測地被取消,需要調用flush_workqueue函數,與方法1中的不同。

void flush_workqueue(struct workqueue_struct *wq);  

4、銷毀工作隊列, 使用完工作隊列之后,可以使用destroy_workqueue銷毀工作隊列:

void destroy_workqueue(struct workqueue_struct *wq); 
(4)共享workqueue實例代碼
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>struct work_struct works;
void workqueue_fun(struct work_struct * work)
{printk("this is workqueue test\n");
}
static int __init  workqueue_module_init(void)
{INIT_WORK(&works,workqueue_fun);//初始化共享工作隊列結構體schedule_work(&works);//將工作隊列進行登記return 0;
}
static void __exit  workqueue_module_cleanup(void)
{printk("module is exit\n");
}
module_init(workqueue_module_init);
module_exit(workqueue_module_cleanup);
MODULE_LICENSE("GPL");

本文章僅供學習交流用禁止用作商業用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作

微信公眾號:zhjj0729

微博:文藝to青年

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531405.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531405.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531405.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Codeforces Round #326 (Div. 2) B. Pasha and Phone C. Duff and Weight Lifting

B. Pasha and PhonePasha has recently bought a new phone jPager and started adding his friends phone numbers there. Each phone number consists of exactly n digits. Also Pasha has a number k and two sequences of length n?/?k (n is divisible by k) a1,?a2,?…

vmware中裝的ubuntu上不了網

本文章針對橋接方式進行講解&#xff0c;如果需要另外兩種連接方式請參考文末給出的鏈接 &#xff08;一&#xff09;問題 主機和虛擬機可以相互ping通&#xff0c;但是卻不能ping網址 &#xff08;二&#xff09;解決辦法 vmware為我們提供了三種網絡工作模式&#xff0c;…

document.getElementById()與 $()區別

document.getElementById()返回的是DOM對象&#xff0c;而$()返回的是jQuery對象 什么是jQuery對象&#xff1f; ---就是通過jQuery包裝DOM對象后產生的對象。jQuery對象是jQuery獨有的&#xff0c;其可以使用jQuery里的方法。 比如&#xff1a; $("#test").html() 意…

關于gedit的編碼問題

今天由于gedit的編碼格式導致LCD顯示屏的問題&#xff0c;開始沒有想到后來才發現&#xff0c;在這記錄一下 #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h>…

c語言表白程序代碼

雙十一要到了&#xff0c;好激動啊&#xff01;&#xff01;&#xff01; 是時候準備出手了&#xff01; 花了一天的時間寫的表白代碼。 表示自己弱弱的..... 看了網上好多都是js寫的&#xff0c;感覺碉堡了&#xff01;js用的不熟&#xff0c;前端不好&#xff0c;java&#x…

tiny4412移植tslib庫

1、將tslib-1.4.tar.gz拷貝到虛擬機某個路徑進行解壓 2、進入解壓路徑tslib 3、執行#./autogen.sh 如果提示&#xff1a;./autogen.sh: 4: ./autogen.sh: autoreconf: not found 原因&#xff1a;沒有安裝automake工具, 解決辦法:需要安裝此工具&#xff1a; apt-get instal…

移植QT到tiny4412開發板

目錄&#xff08;一&#xff09; 環境準備&#xff08;二&#xff09; Qt源代碼下載&#xff08;三&#xff09; 移植tslib庫&#xff08;四&#xff09;操作流程1.解壓qt源碼包2.配置編譯環境3.生成Makefile4.編譯安裝5.安裝一些庫用來支持 qt6. 添加以下內容到開發板目錄下的…

c++面試常用知識(sizeof計算類的大小,虛擬繼承,重載,隱藏,覆蓋)

一. sizeof計算結構體 注&#xff1a;本機機器字長為64位 1.最普通的類和普通的繼承 #include<iostream> using namespace std;class Parent{ public:void fun(){cout<<"Parent fun"<<endl;} }; class Child : public Parent{ public:void fun(){…

嵌入式面試題(一)

目錄1 關鍵字volatile有什么含義&#xff1f;并給出三個不同的例子2. c和c中的struct有什么不同&#xff1f;3.進程和線程區別4.ARM流水線5.使用斷言6 .嵌入式系統的定義7 局部變量能否和全局變量重名&#xff1f;8 如何引用一個已經定義過的全局變量&#xff1f;9、全局變量可…

能ping通ip但無法ping通域名和localhost //ping: bad address 'www.baidu.com'

錯誤描述&#xff1a; ~ # ping localhost ping: bad address localhost原因&#xff0c;在/etc目錄下缺少hosts文件&#xff0c;將linux中的/etc hosts文件拷入即可 ~ # ping localhost PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: seq0 ttl64 tim…

eclipse導入web項目之后項目中出現小紅叉解決辦法

項目中有小紅叉我遇到的最常見的情況&#xff1a; 1、項目代碼本身有問題。&#xff08;這個就不說了&#xff0c;解決錯誤就OK&#xff09; 2、項目中的jar包丟失。&#xff08;有時候eclipse打開時會出現jar包丟失的情況&#xff0c;關閉eclipse重新打開或者重新引入jar包就O…

arm開發板通過網線連接筆記本電腦上外網

需要工具&#xff1a;arm開發板&#xff0c;網線&#xff0c;一臺雙網卡的win7筆記本電腦&#xff08;筆記本電腦一般都是雙網卡&#xff09; 一、筆記本電腦需要先連上外網&#xff0c;可以連上家里的WIFI&#xff0c;或者手機開熱點&#xff08;本人未測試過連接手機的熱點&…

windows下實現Git在局域網使用

1.首先在主機A上創建一個文件夾用于存放你要公開的版本庫。然后進入這個文件夾&#xff0c;右鍵->Git create repository here&#xff0c;彈出的窗口中勾選Make it Bare&#xff01;之后將這個文件夾完全共享&#xff08;共享都會吧&#xff1f;注意權限要讓使用這個文件夾…

解決linux下QtCreator無法輸入中文的情況

安裝了QtCreator(Qt5.3.1自帶版本)后無法輸入中文&#xff0c;確切的說是無法打開輸入法。以前使用iBus輸入法的時候沒有這個問題&#xff0c;現在使用sougou輸入法才有的這個問題。 可以查看此文 http://www.cnblogs.com/oloroso/p/5114041.html 原因 有問題就得找原因&…

lintcode 滑動窗口的最大值(雙端隊列)

題目鏈接&#xff1a;http://www.lintcode.com/zh-cn/problem/sliding-window-maximum/# 滑動窗口的最大值 給出一個可能包含重復的整數數組&#xff0c;和一個大小為 k 的滑動窗口, 從左到右在數組中滑動這個窗口&#xff0c;找到數組中每個窗口內的最大值。 樣例 給出數組 [1…

你的main函數規范嗎?

在學習c語言的時候&#xff0c;有一個函數一直被我們使用&#xff0c;那就是main函數&#xff0c;但是你知道標準里面是怎么規定它的寫法嗎&#xff1f; 平時看見的main函數有下面這幾種&#xff1a; 1.int main(void){ }2.int main(){ }3.int main(int argc, char *argv[])…

lintcode 最長上升連續子序列 II(二維最長上升連續序列)

題目鏈接&#xff1a;http://www.lintcode.com/zh-cn/problem/longest-increasing-continuous-subsequence-ii/ 最長上升連續子序列 II 給定一個整數矩陣&#xff08;其中&#xff0c;有 n 行&#xff0c; m 列&#xff09;&#xff0c;請找出矩陣中的最長上升連續子序列。&a…

適用于Linux的Windows子系統WSL

以前使用的都是在虛擬機里安裝linux&#xff0c;最近才發現在win10提供了WSL(Windows Subsystem for Linux) &#xff0c;簡單來說就是可以在win10里面直接使用Linux。 &#xff08;一&#xff09;首先打開Microsoft Store , 搜索 Linux &#xff08;二&#xff09;選擇自己需…

jsp通過易寶方式實現在線支付

項目下載地址: https://github.com/hjzgg/OnlinePayment 參考&#xff1a;http://blog.csdn.net/jadyer/article/details/7380259?utm_sourcetuicool&utm_mediumreferral 效果圖1&#xff1a;請求界面 效果圖2&#xff1a;地支付請求和易寶之間建立連接之后跳轉到相應的銀…

permission denied是什么鬼?

問題&#xff1a;在PC端編譯了一個arm芯片的測試程序&#xff0c;出現了permission denied 解決辦法&#xff1a; 1.給文件賦予可執行權限 chmod ax xxx這是一般第一反應會想到的答案 2. 有時候已經有可執行權限&#xff0c;還是提示上面的錯誤此時要注意你的交叉編譯器是否正…