国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn))

這篇具有很好參考價值的文章主要介紹了【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn))。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

上一節(jié)獲取按鍵狀態(tài)時,是在應用層以循環(huán)的方式不斷讀取按鍵狀態(tài),但是我們實際關注的只是當按鍵被按下時發(fā)生的情況,所以大多數(shù)時間拿到的狀態(tài)都是我們不需要的結果。

【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn)),Linux驅動(I.MX6ULL),linux,運維,服務器

對此,當按鍵被釋放時,讓 read 接口處于阻塞狀態(tài),等按鍵被按下再解除阻塞。

一、等待隊列API

要使用等待隊列涉及到兩個概念:等待隊列頭、等待項

等待隊列通常使用鏈表實現(xiàn),等待隊列頭便是鏈表的頭節(jié)點,在Linux內核中使用?wait_queue_head_t 類型來表示等待隊列頭;等待項是等待隊列中的一個子節(jié)點,通常是以線程為單位,將等待項加入到等待隊列,相當于讓線程處于休眠狀態(tài),在Linux內核中使用?wait_queue_t 類型來表示。相關API聲明在 <linux/wait.h> 文件中。

1、初始化等待隊列

在使用等待隊列之前,一般需要“聲明 + 初始化”,接口原型如下(本質是宏)

/**
 * @param q 等待隊列
 */
void init_waitqueue_head(wait_queue_head_t *q);

2、向等待隊列添加等待項

向等待隊列添加等待項之前需要先初始化等待項,等待項的初始化比較特殊,無需事先聲明變量,使用的接口原型如下

/**
 * @param name 等待項的名字
 * @param tsk  當前所屬任務(進程),一般填current,current 是一個全局變量,表示當前進程
 */
DECLARE_WAITQUEUE(name, tsk);

// 示例
// wait     自己擬定的變量名(無需事先聲明),可以認為此處便是在“聲明+定義”一個等待項
// current  Linux內核的全局變量,表示當前進程
DECLARE_WAITQUEUE(wait, current);

向等待隊列添加等待項,add_wait_queue 接口函數(shù)不會主動陷入阻塞,需要我們手動設置進程狀態(tài);當?shù)却棻粏拘褧r,會自動從等待隊列移除。接口原型如下:

/**
 * @param q     等待隊列頭
 * @param wait  等待項,需要和前面 DECLARE_WAITQUEUE 的第一個參數(shù)保持一致
 */
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

拓展:wait_event_interruptible 在符合等待條件的時候,會自動進入到等待隊列掛起,被喚醒時從掛起的位置開始繼續(xù)運行。但由于無法確定等待條件何時觸發(fā),也就不知道在哪個位置掛起,這里推薦使用?add_wait_queue。

3、喚醒等待隊列中的等待項

所謂喚醒,其實是將線程/進程由休眠態(tài)轉變?yōu)榫途w態(tài),換句話說是將等待項(進程)從等待隊列移到運行隊列。

  • 喚醒:先移出等待隊列,再移入運行隊列
  • 移除:移出等待隊列

喚醒等待項的接口原型如下:

/**
 * @param q     等待隊列頭
 */
void wake_up(wait_queue_head_t *q);
void wake_up_interruptible(wait_queue_head_t *q);

注意:wake_up 函數(shù)可以喚醒處于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 狀態(tài)的進 程,而 wake_up_interruptible 函數(shù)只能喚醒處于 TASK_INTERRUPTIBLE 狀態(tài)的進程。所以在將等待項加入到等待隊列以后務必要設置進程的狀態(tài)。

4、從等待隊列移除等待項

當遇到一些異常情況導致進程終止時,我們需要主動將等待項從等待隊列移除。接口原型如下:

/**
 * @param q     等待隊列頭
 * @param wait  等待項
 */
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

二、內核API

1、設置進程狀態(tài)

如果使用?wake_up_interruptible 來喚醒等待項(進程),需要在將等待項(進程)加入到等待隊列后,設置進程的狀態(tài)為?TASK_INTERRUPTIBLE。在內核中使用?__set_current_state 來設置進程狀態(tài)。比如要設為?TASK_INTERRUPTIBLE

__set_current_state(TASK_INTERRUPTIBLE);

當進程進入到執(zhí)行隊列,此時要將進程狀態(tài)設為運行狀態(tài)。

__set_current_state(TASK_RUNNING);

2、重新調度進程

調用 schedule?函數(shù)會主動觸發(fā)調度器,讓內核選擇下一個要運行的進程。在調用 schedule?函數(shù)之前,會先調用 __set_current_state?將進程置于睡眠態(tài)或等待條件的狀態(tài)。這樣的話進程會在當前位置阻塞,下一次進程被喚醒時,會從阻塞的地方繼續(xù)向下執(zhí)行。

schedule();

三、驅動實現(xiàn)

1、定義等待隊列

在字符設備的結構體中聲明一個等待隊列的頭節(jié)點,附加一個設備狀態(tài)

typedef enum {
	Pressed = 0U,
	Released = 1U
}key_status;

struct chrdev_t 
{
    // ... ...
    key_status			status;				/* 設備狀態(tài) */
	wait_queue_head_t 	wait_head;			/* 等待隊列的頭節(jié)點 */
};
static struct chrdev_t chrdev;

2、初始化等待隊列

在驅動入口函數(shù)中初始化該等待隊列,初始化設備狀態(tài)變量,因為我們計劃在按鍵處于釋放狀態(tài)時,新建一個等待項并添加到等待隊列,所以需要一個變量來保存按鍵狀態(tài)。

/* 驅動入口函數(shù) */
static int __init xxx_init(void)
{
	/* 初始化等待隊列頭 */
	init_waitqueue_head(&chrdev.wait_head);
    chrdev.status = Released;
   
    // ... 
}

3、向等待隊列添加等待項

每當應用層調用 read 接口,在 read 操作函數(shù)中,判斷按鍵狀態(tài),如果為釋放狀態(tài)則添加到等待隊列,同時將當前進程設為休眠態(tài);等待隊列中的等待項被喚醒時,再重新設為運行態(tài)。

static ssize_t chrdev_read(struct file *pfile, char __user * pbuf, size_t size, loff_t * poff)
{
    // open 操作函數(shù)中 pfile->private_data = &chrdev;
	struct chrdev_t* pdev = pfile->private_data;

	if (pdev->status == Released)
	{
		/* 初始化等待項 */
		DECLARE_WAITQUEUE(wait_item, current);

		printk("加入等待隊列\(zhòng)n");
		add_wait_queue(&pdev->wait_head, &wait_item);   // 添加到等待隊列
		__set_current_state(TASK_INTERRUPTIBLE);        // 當前進程設為休眠態(tài)
		schedule();                                     // 重新調度進程(此時會阻塞,等待被喚醒)
		__set_current_state(TASK_RUNNING);              // 被喚醒后,設為運行態(tài)  
	}
	printk("等待項被喚醒\n");

    // ...
}

4、喚醒等待隊列

當按鍵任務處理完畢,此時需要喚醒等待隊列中的等待項。這里是搭配定時器實現(xiàn)了按鍵消抖,真正的處理邏輯在定時器的回調函數(shù)中。

static irqreturn_t key0_handler(int irq, void * dev)
{
	mod_timer(&timer, timer_delay(50));

	return IRQ_RETVAL(IRQ_HANDLED);
}

/* 定時器回調函數(shù) */
void timer_callback(unsigned long arg)
{
	struct chrdev_t* pdev = (struct chrdev_t*)arg;
	pdev->status = Pressed;

	// 按鍵處理邏輯

	pdev->status = Released;
	// 喚醒等待項
	wake_up_interruptible(&pdev->wait_head);
}

四、應用測試

在應用程序中,調用一次 read 函數(shù)來檢測是否加入了等待隊列,以及按鍵按下是否被喚醒。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void printHelp()
{
    printf("usage: ./xxxApp <driver_path>\n");
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        printHelp();
        return -1;
    }
    
    char* driver_path = argv[1];      
    int state = 0;
    int ret = 0;
    int fd = 0;

    fd = open(driver_path, O_RDONLY);
    if (fd < 0)
    {
        perror("open file failed");
        return -2;
    }
    ret = read(fd, &state, sizeof(state));
    if (ret < 0)
    {
        printf("read data error\n");
    }

    close(fd);
    return 0;
}

程序開始運行時,因為加入到等待隊列,一開始會阻塞

【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn)),Linux驅動(I.MX6ULL),linux,運維,服務器

當按鍵按下,等待項被喚醒,進程會從掛起的地方,也就是 schedule() 的下一行開始運行

【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn)),Linux驅動(I.MX6ULL),linux,運維,服務器文章來源地址http://www.zghlxwxcb.cn/news/detail-784836.html

到了這里,關于【Linux驅動】Linux阻塞IO —— 阻塞讀取按鍵狀態(tài)(等待隊列實現(xiàn))的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • STM32MP157驅動開發(fā)——按鍵驅動(工作隊列)

    STM32MP157驅動開發(fā)——按鍵驅動(工作隊列)

    定時器、下半部 tasklet,它們都是在中斷上下文中執(zhí)行,它們無法休眠。當要處理更復雜的事情時,往往更耗時。這些更耗時的工作放在定時器或是下半部中,會使得系統(tǒng)很卡;并且循環(huán)等待某件事情完成也太浪費CPU 資源了。如果使用線程來處理這些耗時的工作,那就可以解

    2024年02月15日
    瀏覽(25)
  • Linux搭建Web服務器(一)——阻塞與非阻塞、同步與異步、Linux五種IO模型

    Linux搭建Web服務器(一)——阻塞與非阻塞、同步與異步、Linux五種IO模型

    目錄 0x01 阻塞與非阻塞、同步與異步 阻塞與非阻塞 同步與異步 總結 0x02 Unix、Linux上的五種IO模型 阻塞(blocking) 非阻塞(non-blocking——NIO) IO復用(IO multiplexing) 信號驅動(signal-driven) 異步(asynchronous) 為了理清楚這幾個概念,我們可以從 數(shù)據(jù)就緒 以及 數(shù)據(jù)讀寫 層面

    2023年04月10日
    瀏覽(22)
  • linux 信號原理 信號處理設置signal, 信號發(fā)送kill,信號等待sigsuspend,信號阻塞sigprocmask,一網打盡信號使用

    linux 信號原理 信號處理設置signal, 信號發(fā)送kill,信號等待sigsuspend,信號阻塞sigprocmask,一網打盡信號使用

    ? 專欄內容 : postgresql內核源碼分析 手寫數(shù)據(jù)庫toadb 并發(fā)編程 個人主頁 :我的主頁 座右銘:天行健,君子以自強不息;地勢坤,君子以厚德載物. ================================ 信號是一種軟中斷的方式,讓進程陷入中斷處理調用中; linux 下信號也是一種進程間通信的手段;進

    2024年02月13日
    瀏覽(24)
  • 5.1阻塞和非阻塞、同步和異步 5.2Unix、Linux上的五種IO模型

    5.1阻塞和非阻塞、同步和異步 5.2Unix、Linux上的五種IO模型

    典型的一次IO的兩個階段是什么?數(shù)據(jù)就緒和數(shù)據(jù)讀寫 數(shù)據(jù)就緒:根據(jù)IO操作的就緒狀態(tài) 阻塞 非阻塞 數(shù)據(jù)讀寫:根據(jù)應用程序和內核的交互方式 同步 異步 陳碩:在處理IO的時候,阻塞和非阻塞都是同步IO,只有使用了特殊的API才是異步IO。 一個典型的網絡接口調用,分為兩

    2024年02月12日
    瀏覽(20)
  • Tensorflow入門(2)——深度學習框架Tesnsflow & 線程+隊列+IO操作 & 文件讀取案例

    Tensorflow入門(2)——深度學習框架Tesnsflow & 線程+隊列+IO操作 & 文件讀取案例

    Tensorflow入門(1)——深度學習框架Tesnsflow入門 環(huán)境配置 認識Tensorflow 在訓練樣本的時候,希望讀入的訓練樣本時有序的 ? tf.FIFOQueue 先進先出隊列,按順序出隊列 ? tf.RandomShuffleQueue 隨機出隊列 FIFOQueue(capacity, dtypes, name=‘fifo_queue’) 創(chuàng)建一個以先進先出的順序對元素進行排

    2024年02月17日
    瀏覽(22)
  • Linux——信號處理函數(shù)與阻塞狀態(tài)的進程

    這篇博客記錄一下我在編寫一個簡單的多進程回聲服務器的時候出現(xiàn)的問題。 這個問題就在于忽略了幾個有關于信號處理函數(shù)的基本常識: 用通俗的話講信號注冊函數(shù)(signal、sigaction)的功能:進程告訴操作系統(tǒng),當以后收到向信號注冊函數(shù)傳入的信號時,你幫我調用一下信號

    2024年02月13日
    瀏覽(22)
  • 【Linux】生產者消費者模型(阻塞隊列與環(huán)形隊列)和POSIX信號量

    【Linux】生產者消費者模型(阻塞隊列與環(huán)形隊列)和POSIX信號量

    我們這里舉一個例子,來解釋生產者消費者模型,我們學生–消費者,供應商–生產者,超市–交易場所,我們買東西只需要關系售貨架子上是否有商品即可,沒有了商品,超市從供應商進行供貨。供應商和供應商不能同時向一個貨架進行供貨,所以生產者之間是互斥的關系

    2024年02月03日
    瀏覽(21)
  • 【Linux】生產者消費者模型:基于阻塞隊列和環(huán)形隊列 | 單例模式線程池

    【Linux】生產者消費者模型:基于阻塞隊列和環(huán)形隊列 | 單例模式線程池

    死鎖是指在一組進程中的各個進程均占有不會釋放的資源,但因互相申請被其他進程所站用不會釋放的資源而處于的一種永久等待狀態(tài)。 當多線程并發(fā)執(zhí)行并都需要訪問臨界資源時,因為每個線程都是不同的執(zhí)行流,這就有可能 導致數(shù)據(jù)不一致問題 ,為了避免此問題的發(fā)生

    2024年01月24日
    瀏覽(25)
  • 【Linux】進程狀態(tài)&&僵尸進程和孤兒進程&&阻塞、掛起和運行

    【Linux】進程狀態(tài)&&僵尸進程和孤兒進程&&阻塞、掛起和運行

    個人主頁 : zxctscl 如有轉載請先通知 上一篇博客中提到 【Linux】進程初步理解,這次繼續(xù)來分享與進程有關的知識。 Linux的進程狀態(tài)就是struct task_struct內部的一個屬性。 為了弄明白正在運行的進程是什么意思,我們需要知道進程的不同狀態(tài)。一個進程可以有幾個狀態(tài)(在

    2024年04月14日
    瀏覽(23)
  • Linux之【多線程】生產者與消費者模型&BlockQueue(阻塞隊列)

    Linux之【多線程】生產者與消費者模型&BlockQueue(阻塞隊列)

    舉個例子:學生要買東西,一般情況下都會直接聯(lián)系廠商,因為買的商品不多,對于供貨商來說交易成本太高,所以有了交易場所超市這個媒介的存在。目的就是為了集中需求,分發(fā)產品。 消費者與生產者之間通過了超市進行交易。當生產者不需要的時候,廠商可以繼續(xù)生產

    2024年02月02日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包