gogo专业大尺度亚洲高清人体,美女张开双腿让男生桶,亚洲av无码一区二区三区鸳鸯影院,久久久久国产精品人妻

當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > linux中斷編程、中斷編程詳解

linux中斷編程、中斷編程詳解 時(shí)間:2018-07-27      來源:未知

Linux中斷處理驅(qū)動程序編寫

中斷處理是操作系統(tǒng)必須具備的上要功能之一,下面我們一起來探討一下Linux中的中斷處理。

1. 什么是中斷

中斷就是CPU正常運(yùn)行期間,由于內(nèi)、外部事件引起的CPU暫時(shí)停止正在運(yùn)行的程序,去執(zhí)行該內(nèi)部事件或外部事件的引起的服務(wù)中去,服務(wù)執(zhí)行完畢后再返回?cái)帱c(diǎn)處繼續(xù)執(zhí)行的情形。這樣的中斷機(jī)制極大的提高了CPU運(yùn)行效率。

1.1. 中斷的分類:

1) 根據(jù)中斷的來源可分為內(nèi)部中斷和外部中斷,內(nèi)部中斷的中斷源來自于CPU內(nèi)部(軟件中斷指令、溢出、除法錯誤等),例如操作系統(tǒng)從用戶態(tài)切換到內(nèi)核態(tài)需要借助CPU內(nèi)部的軟件中斷,外部中斷的中斷源來自于CPU外部,由外設(shè)觸發(fā)。

2) 根據(jù)中斷是否可以被屏蔽,中斷可分為可屏蔽中斷和不可屏蔽中斷,可屏蔽中斷可以通過設(shè)置中斷控制器寄存器等方法被屏蔽,屏蔽后,該中斷不再得到響應(yīng),而不可屏蔽中斷不能被屏蔽。

3) 根據(jù)中斷入口跳轉(zhuǎn)方式的不同,中斷可分為向量中斷和非向量中斷。采用向量中斷的CPU通常為不同的中斷分配不同的中斷號,當(dāng)檢測到中斷的中斷號到來時(shí),就自動跳轉(zhuǎn)到該中斷對應(yīng)的地址處去執(zhí)行程序。不同的中斷號對應(yīng)不同的中斷入口地址。非向量中斷的多個(gè)中斷共享一個(gè)入口程序處理入口地址,中斷程序跳轉(zhuǎn)到該入口地址執(zhí)行時(shí),再通過中斷程序來判斷中斷標(biāo)志來識別具體是哪一個(gè)中斷,也就是說向量中斷由硬件提供中斷服務(wù)程序入口地址,非向量中斷由軟件提供中斷服務(wù)程序入口地址。

4) 非向量中斷處理流程:

/*典型的非向量中斷首先會判斷中斷源,然后調(diào)用不同中斷源的中斷處理程序*/

irq_handler()

{

...

int int_src = read_int_status();/*讀硬件的中斷相關(guān)寄存器*/

switch(int_src)

{

//判斷中斷標(biāo)志

case DEV_A:

dev_a_handler();

break;

case DEV_B:

dev_b_handler();

break;

...

default:

break;

}

...

}

2. linux中斷頂部、底部概念

為保證系統(tǒng)實(shí)時(shí)性,中斷服務(wù)程序必須足夠簡短,但實(shí)際應(yīng)用中某些時(shí)候發(fā)生中斷時(shí)必須處理大量的工作,這時(shí)候如果都在中斷服務(wù)程序中完成,則會嚴(yán)重降低中斷的實(shí)時(shí)性,基于這個(gè)原因,linux系統(tǒng)提出了一個(gè)概念:把中斷服務(wù)程序分為兩部分:頂半部、底半部。

2.1. 頂半部

完成盡可能少的比較急的功能,它往往只是簡單的讀取寄存器的中斷狀態(tài),并清除中斷標(biāo)志后就進(jìn)行“中斷標(biāo)記”(也就是把底半部處理程序掛到設(shè)備的底半部執(zhí)行隊(duì)列中)的工作。特點(diǎn)是響應(yīng)速度快。

2.2. 底半部

中斷處理的大部分工作都在底半部,它幾乎做了中斷處理程序的所有事情。 特點(diǎn):處理相對來說不是非常緊急的事件 ,底半部機(jī)制主要有:tasklet、工作隊(duì)列和軟中斷。

Linux中查看/proc/interrupts文件可以獲得系統(tǒng)中斷的統(tǒng)計(jì)信息:

中斷編程

3. Linux中斷編程

3.1. 申請和釋放中斷

3.1.1. 申請中斷:

int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id);

參數(shù)介紹:irq是要申請的硬件中斷號。

Handler:是向系統(tǒng)登記的中斷處理程序(頂半部),是一個(gè)回調(diào)函數(shù),中斷發(fā)生時(shí),系統(tǒng)調(diào)用它,將dev_id參數(shù)傳遞給它。

irqflags:是中斷處理的屬性,可以指定中斷的觸發(fā)方式和處理方式:

觸發(fā)方式:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW,處理方式:IRQF_DISABLE表明中斷處理程序是快速處理程序,快速處理程序被調(diào)用時(shí)屏蔽所有中斷,IRQF_SHARED表示多個(gè)設(shè)備共享中斷,dev_id在中斷共享時(shí)會用到,一般設(shè)置為NULL。

返回值:為0表示成功,返回-EINVAL表示中斷號無效,返回-EBUSY表示中斷已經(jīng)被占用,且不能共享。

頂半部的handler的類型irq_handler_t定義為:

typedef irqreturn_t (*irq_handler_t)(int,void*);

typedef int irqreturn_t;

3.1.2. 釋放IRQ

有請求當(dāng)然就有釋放。中斷的釋放函數(shù)為:

void free_irq(unsigned int irq,void *dev_id);

參數(shù)定義與request_irq類似。

3.1.3. 中斷的使能和屏蔽

void disable_irq(int irq);//等待目前中斷處理完成(最好別在頂板部使用,你懂得)

void disable_irq_nosync(int irq);//立即返回

void enable_irq(int irq);//

3.1.4. 屏蔽本CPU內(nèi)所有中斷:

#define local_irq_save(flags)...//禁止中斷并保存狀態(tài)。

void local_irq_disable(void); //禁止中斷,不保存狀態(tài)。

下面來分別介紹一下頂半部和底半部的實(shí)現(xiàn)機(jī)制

3.1.5. 底半部機(jī)制:

簡介:底半部機(jī)制主要有tasklet、工作隊(duì)列和軟中斷

3.1.5.1. 底半部實(shí)現(xiàn)方法之一tasklet

(1) 我們需要定義tasklet機(jī)器處理器并將兩者關(guān)聯(lián),例如:

void my_tasklet_func(unsigned long);/*定義一個(gè)處理函數(shù)*/

DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);

/*上述代碼定義了名為my_tasklet的tasklet并將其與my_tasklet_func()函數(shù)綁定,傳入的參數(shù)為data*/

(2)調(diào)度

tasklet_schedule(&my_tasklet);

//使用此函數(shù)就能在是當(dāng)?shù)臅r(shí)候進(jìn)行調(diào)度運(yùn)行

(3)tasklet使用模板:

/*定義tasklet和底半部函數(shù)并關(guān)聯(lián)*/

void xxx_do_tasklet(unsigned long);

DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet,0);

/*中斷處理底半部*/

void xxx_do_tasklet(unsigned long)

{

...

}

/*中斷處理頂半部*/

irqreturn_t xxx_interrupt(int irq,void *dev_id)

{

...

tasklet_schedule(&xxx_tasklet);//調(diào)度地半部

...

}

/*設(shè)備驅(qū)動模塊加載函數(shù)*/

int __init xxx_init(void)

{

...

/*申請中斷*/

result = request_irq(xxx_irq,xxx_interrupt, IRQF_DISABLED,"xxx",NULL);

...

return IRQ_HANDLED;

}

/*設(shè)備驅(qū)動模塊卸載函數(shù)*/

void __exit xxx_exit(void)

{

...

/*釋放中斷*/

free_irq(xxx_irq,xxx_interrupt);

...

}

3.1.5.2. 底半部實(shí)現(xiàn)方法之二---工作隊(duì)列

使用方法和tasklet類似,相關(guān)操作:

struct work_struct my_wq;/*定義一個(gè)工作隊(duì)列*/

void my_wq_func(unsigned long);/*定義一個(gè)處理函數(shù)*/

通過INIT_WORK()可以初始化這個(gè)工作隊(duì)列并將工作隊(duì)列與處理函數(shù)綁定INIT_WORK(&my_wq,(void (*)(void *))my_wq_func,NULL);/*初始化工作隊(duì)列并將其與處理函數(shù)綁定*/

schedule_work(&my_wq);/*調(diào)度工作隊(duì)列執(zhí)行*/

/*工作隊(duì)列使用模板*/

/*定義工作隊(duì)列和關(guān)聯(lián)函數(shù)*/

struct work_struct(unsigned long);

void xxx_do_work(unsigned long);

/*中斷處理底半部*/

void xxx_do_work(unsigned long)

{

...

}

/*中斷處理頂半部*/

irqreturn_t xxx_interrupt(int irq,void *dev_id)

{

...

schedule_work(&my_wq);//調(diào)度底半部

...

return IRQ_HANDLED;

}

/*設(shè)備驅(qū)動模塊加載函數(shù)*/

int xxx_init(void)

{

...

/*申請中斷*/

result = request_irq(xxx_irq,xxx_interrupt,IRQF_DISABLED,"xxx",NULL);

...

/*初始化工作隊(duì)列*/

INIT_WORK(&my_wq,(void (*)(void *))xxx_do_work,NULL);

}

/*設(shè)備驅(qū)動模塊卸載函數(shù)*/

void xxx_exit(void)

{

...

/*釋放中斷*/

free_irq(xxx_irq,xxx_interrupt);

...

}

4. 中斷共享

中斷共享是指多個(gè)設(shè)備共享一根中斷線的情況,中斷共享的使用方法:

(1).在申請中斷時(shí),使用IRQF_SHARED標(biāo)識;

(2).在中斷到來時(shí),會遍歷共享此中斷的所有中斷處理程序,直到某一個(gè)函數(shù)返回IRQ_HANDLED,在中斷處理程序頂半部中,應(yīng)迅速根據(jù)硬件寄存器中的信息參照dev_id參數(shù)判斷是否為本設(shè)備的中斷,若不是立即返回IR1_NONE

/*共享中斷編程模板*/

irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)

{

...

int status = read_int_status();/*獲知中斷源*/

if(!is_myint(dev_id,status))/*判斷是否為本設(shè)備中斷*/

return IRQ_NONE;/*不是本設(shè)備中斷,立即返回*/

/*是本設(shè)備中斷,進(jìn)行處理*/

...

return IRQ_HANDLED;/*返回IRQ_HANDLER表明中斷已經(jīng)被處理*/

}

/*設(shè)備模塊加載函數(shù)*/

int xxx_init(void)

{

...

/*申請共享中斷*/

result = request_irq(sh_irq,xxx_interrupt,IRQF_SHARE,"xxx",xxx_dev);

...

}

/*設(shè)備驅(qū)動模塊卸載函數(shù)*/

void xxx_exit()

{

...

/*釋放中斷*/

free_irq(xxx_irq,xxx_interrupt);

...

}

5. 內(nèi)核定時(shí)器

內(nèi)核定時(shí)器編程:

簡介:軟件意義上的定時(shí)器最終是依賴于硬件定時(shí)器實(shí)現(xiàn)的,內(nèi)核在時(shí)鐘中斷發(fā)生后檢測各定時(shí)器是否到期,到期后定時(shí)器處理函數(shù)作為軟中斷在底半部執(zhí)行。

Linux內(nèi)核定時(shí)器操作:

5.1. timer_list結(jié)構(gòu)體

每一個(gè)timer_list對應(yīng)一個(gè)定時(shí)器

struct timer_list{

struct list_head entry;/*定時(shí)器列表*/

unsigned long expires;/*定時(shí)器到期時(shí)間*/

void (*function)(unsigned long);/*定時(shí)器處理函數(shù)*/

unsigned long data;/*作為參數(shù)被傳遞給定時(shí)器處理函數(shù)*/

struct timer_base_s *base;

...

};

當(dāng)定時(shí)器滿的時(shí)候,定時(shí)器處理函數(shù)將被執(zhí)行

5.2. 初始化定時(shí)器

void init_timer(struct timer_list * timer);

//初始化timer_list的entry的next為NULL,并給base指針賦值。

TIMER_INITIALIZER(_function,_expires,_data);//此宏用來

//賦值定時(shí)器結(jié)構(gòu)體的function、expires、data和base成員

#define TIMER_INITIALIZER(function,_expires,_data)

{

.entry = {.prev = TIMER_ENTRY_STATIC},\

.function= (_function), \

.expires = (_expire), \

.data = (_data), \

.base = &boot_tvec_bases,\

}

DEFINE_TIMER(_name,_function,_expires,_data)//定義一個(gè)定時(shí)器結(jié)構(gòu)體變量//并為此變量取名_name

//還有一個(gè)setup_timer()函數(shù)也可以用于定時(shí)器結(jié)構(gòu)體的初始化。

5.3. 增加定時(shí)器

void add_timer(struct timer_list * timer);//注冊內(nèi)核定時(shí)器,也就是將定時(shí)器加入到內(nèi)核動態(tài)定時(shí)器鏈表當(dāng)中。

5.4. 刪除定時(shí)器

del_timer(struct timer_list *timer);

del_timer_sync()//在刪除一個(gè)定時(shí)器時(shí)等待刪除操作被處理完(不能用于中斷上下文中)

5.5. 修改定時(shí)器expires

int mod_timer(struct timer_list * timer,unsigned long expires);//修改定時(shí)器的到期時(shí)間

/*內(nèi)核定時(shí)器使用模板*/

/*xxx設(shè)備結(jié)構(gòu)體*/

struct xxx_dev

{

struct cdev cdev;

...

timer_list xxx_timer;/*設(shè)備要使用的定時(shí)器*/

};

/*xxx驅(qū)動中的某函數(shù)*/

xxx_funcl(...)

{

struct xxx_dev *dev = filp->private_data;

...

/*初始化定時(shí)器*/

init_timer(&dev->xxx_timer);

dev->xxx_timer.function = &xxx_do_timer;

dev->xxx_timer.data = (unsigned long)dev;

/*設(shè)備結(jié)構(gòu)體指針作為定時(shí)器處理函數(shù)參數(shù)*/

dev->xxx_timer.expires = jiffes + delays;

/*添加(注冊)定時(shí)器*/

add_timer(&dev->xxx_timer);

...

}

/*xxx驅(qū)動中的某函數(shù)*/

xxx_func2(...)

{

...

/*刪除定時(shí)器*/

del_timer(&dev->xxx_timer);

...

}

/*定時(shí)器處理函數(shù)*/

static void xxx_do_timer(unsigned long arg)

{

struct xxx_device *dev = (struct xxx_device *)(arg);

...

/*調(diào)度定時(shí)器再執(zhí)行*/

dev->xxx_timer.expires = jiffes + delay;

add_timer(&dev -> xxx_timer);

...

}

//定時(shí)器到期時(shí)間往往是在jiffies的基礎(chǔ)上添加一個(gè)時(shí)延,若為HZ則表示延遲一秒

5.6. 內(nèi)核中的延遲工作:

簡介:對于這種周期性的工作,Linux提供了一套封裝好的快捷機(jī)制,本質(zhì)上利用工作隊(duì)列和定時(shí)器實(shí)現(xiàn),這其中用到兩個(gè)結(jié)構(gòu)體:

(1)struct delayed_work

{

struct work_struct work;

struct timer_list timer;

};

(2) struct work_struct

{

atomic_long_t data;

...

}

相關(guān)操作:

int schedule_delay_work(struct delayed_work *work,unsigned long delay);//當(dāng)指定的delay到來時(shí)delay_work中的work成員的work_func_t類型成員func()會被執(zhí)行work_func_t類型定義如下:

typedef void (*work_func_t)(struct work_struct *work);//delay參數(shù)的單位是jiffes

mescs_to_jiffies(unsigned long mesc);//將毫秒轉(zhuǎn)化成jiffes單位

int cancel_delayed_work(struct delayed_work *work);

int cancel_delayed_work_sync(struct delayed_work *work);//等待直到刪除(不能用于中斷上下文)

內(nèi)核延遲的相關(guān)函數(shù):

短延遲:

Linux內(nèi)核提供了如下三個(gè)函數(shù)分別進(jìn)行納秒、微妙和毫秒延遲:

void ndelay(unsigned long nsecs);

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

機(jī)制:根據(jù)CPU頻率進(jìn)行一定次數(shù)的循環(huán)(忙等待)

注意:在Linux內(nèi)核中最好不要使用毫秒級的延時(shí),因?yàn)檫@樣會無謂消耗CPU的資源。

對于毫秒以上的延時(shí),Linux提供如下函數(shù):

void msleep(unsigned int millisecs);

unsigned long msleep_interruptible(unsigned int millisecs);//可以被打斷

void ssleep(unsigned int seconds);

//上述函數(shù)使得調(diào)用它的進(jìn)程睡眠指定的時(shí)間

長延遲:

機(jī)制:設(shè)置當(dāng)前jiffies加上時(shí)間間隔的jiffies,直到未來的jiffies達(dá)到目標(biāo)jiffires

/*實(shí)例:先延遲100個(gè)jiffies再延遲2s*/

unsigned long delay = jiffies + 100;

while(time_before(jiffies,delay));

/*再延遲2s*/

unsigned long delay = jiffies + 2*Hz;

while(time_before(jiffies,delay));//循環(huán)直到到達(dá)指定的時(shí)間與timer_before()相對應(yīng)的還有一個(gè)time_after

睡著延遲:

睡著延遲是比忙等待更好的一種方法

機(jī)制:在等待的時(shí)間到來之前進(jìn)程處于睡眠狀態(tài),CPU資源被其他進(jìn)程使用,實(shí)現(xiàn)函數(shù)有:

schedule_timeout()

schedule_timeout_uninterruptible()

其實(shí)在短延遲中的msleep() msleep_interruptible()

本質(zhì)上都是依賴于此函數(shù)實(shí)現(xiàn)的,下面兩個(gè)函數(shù)可以讓當(dāng)前進(jìn)程加入到等待隊(duì)列中,從而在等待隊(duì)列上睡眠,當(dāng)超時(shí)發(fā)生時(shí),進(jìn)程被喚醒

sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout);

interruptible_sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout);

上一篇:詳解格式化輸入函數(shù)scanf

下一篇:Linux應(yīng)用程序幾種參數(shù)傳遞方式

熱點(diǎn)文章推薦
華清學(xué)員就業(yè)榜單
高薪學(xué)員經(jīng)驗(yàn)分享
熱點(diǎn)新聞推薦
前臺專線:010-82525158 企業(yè)培訓(xùn)洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠(yuǎn)見科技集團(tuán)有限公司 版權(quán)所有 ,京ICP備16055225號-5京公海網(wǎng)安備11010802025203號

回到頂部