• <tbody id="wslfv"><pre id="wslfv"></pre></tbody>
    <span id="wslfv"></span>
    <tbody id="wslfv"><pre id="wslfv"></pre></tbody>
    <th id="wslfv"><track id="wslfv"><rt id="wslfv"></rt></track></th>

    <li id="wslfv"><acronym id="wslfv"></acronym></li>
    更多課程 選擇中心

    嵌入式培訓
    達內IT學院

    400-111-8989

    Linux系統中的”隊列”是什么?

    • 發布:嵌入式培訓
    • 來源:嵌入式問答
    • 時間:2017-10-11 09:59

    學習linux內核相關的代碼的時候,經常遇到跟"隊列“相關的名詞。或許你并不是很理解這個“隊列”到時是個什么鬼,今天我們一起來看看。

    首先總結一下跟“隊列”有關的名詞:

    1:等待隊列

    2:工作隊列

    3:請求隊列

    一:等待隊列

    在內核里面,等待隊列是有很多用處的,尤其是在中斷處理、進程同步、定時等場合。

    可以使用等待隊列在實現阻塞進程的喚醒。它以隊列為基礎數據結構,與進程調度機制緊密結合,

    能夠用于實現內核中的異步事件通知機制,同步對系統資源的訪問等。

    涉及到的數據結構包括:

    struct __wait_queue {

    unsigned int flags;

    #define WQ_FLAG_EXCLUSIVE 0x01

    void *private;

    wait_queue_func_t func; //為一個函數指針typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);

    struct list_head task_list; //用來將wait_queue_func_t鏈起來

    };

    struct __wait_queue_head {

    spinlock_t lock;

    struct list_head task_list; //雙向循環鏈表,存放等待的進程。

    };

    typedef struct __wait_queue_head wait_queue_head_t; //定義了等待隊列頭

    其中

    等待隊列(wait_queue_t)和等待對列頭(wait_queue_head_t)的區別是等待隊列是等待隊列頭的成員。

    也就是說等待隊列頭的task_list域鏈接的成員就是等待隊列類型的(wait_queue_t)。

    1、定義并初始化等待隊列頭:有倆種方法

    (1)

    wait_queue_head_t my_queue;

    init_waitqueue_head(&my_queue);

    直接定義并初始化。init_waitqueue_head()函數會將自旋鎖初始化為未鎖,等待隊列初始化為空的雙向循環鏈表。

    (2)

    DECLARE_WAIT_QUEUE_HEAD(my_queue);

    定義并初始化,相當于(1)。

    2、定義等待隊列項:

    DECLARE_WAITQUEUE(name,tsk);

    其定義如下:

    #define DECLARE_WAITQUEUE(name, tsk) wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

    #define __WAITQUEUE_INITIALIZER(name, tsk) { \

    .private = tsk, \

    .func = default_wake_function, \

    .task_list = { NULL, NULL } }

    從上面的定義可以知道,DECLARE_WAITQUEUE(name,tsk)主要定義了變量name,并對private數據項進行了賦值。

    3、(從等待隊列頭中)添加/移出等待隊列項:

    (1)add_wait_queue()函數:

    void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

    函數將wait_queue_t添加到wait_queue_head_t中。

    (2)remove_wait_queue()函數:

    void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

    在等待的資源或事件滿足時,進程被喚醒,使用該函數被從等待頭中刪除。

    4、等待事件:

    (1)wait_event()宏:

    在等待隊列中睡眠直到condition為真。在等待的期間,進程會被置為TASK_UNINTERRUPTIBLE進入睡眠,直到condition變量變為真。每次進程被喚醒的時候都會檢查condition的值.

    (2)wait_event_interruptible()函數:

    和wait_event()的區別是調用該宏在等待的過程中當前進程會被設置為TASK_INTERRUPTIBLE狀態.在每次被喚醒的時候,首先檢查condition是否為真,如果為真則返回,否則檢查如果進程是被信號喚醒,會返回-ERESTARTSYS錯誤碼.如果是condition為真,則返回0.

    (3)wait_event_timeout()宏:

    也與wait_event()類似.不過如果所給的睡眠時間為負數則立即返回.如果在睡眠期間被喚醒,且condition為真則返回剩余的睡眠時間,否則繼續睡眠直到到達或超過給定的睡眠時間,然后返回0.

    (4)wait_event_interruptible_timeout()宏:

    與wait_event_timeout()類似,不過如果在睡眠期間被信號打斷則返回ERESTARTSYS錯誤碼.

    (5) wait_event_interruptible_exclusive()宏

    同樣和wait_event_interruptible()一樣,不過該睡眠的進程是一個互斥進程.

    5、喚醒隊列:

    (1)wake_up()函數:

    喚醒等待隊列.可喚醒處于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE狀態的進程,和wait_event/wait_event_timeout成對使用.

    (2)wake_up_interruptible()函數:

    #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

    和wake_up()唯一的區別是它只能喚醒TASK_INTERRUPTIBLE狀態的進程,

    與wait_event_interruptible/wait_event_interruptible_timeout/ wait_event_interruptible_exclusive成對使用.

    (3)

    #define wake_up_all(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)

    #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)

    #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)

    這些也基本都和wake_up/wake_up_interruptible一樣.

    6、在等待隊列上睡眠:

    (1)sleep_on()函數:

    (2)sleep_on_timeout()函數:

    long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)

    與sleep_on()函數的區別在于調用該函數時,如果在指定的時間內(timeout)沒有獲得等待的資源就會返回。實際上是調用schedule_timeout()函數實現的。值得注意的是如果所給的睡眠時間(timeout)小于0,則不會睡眠。該函數返回的是真正的睡眠時間。

    (3)interruptible_sleep_on()函數:

    void __sched interruptible_sleep_on(wait_queue_head_t *q)

    該函數和sleep_on()函數唯一的區別是將當前進程的狀態置為TASK_INTERRUPTINLE,這意味在睡眠如果該進程收到信號則會被喚醒。

    (4)interruptible_sleep_on_timeout()函數:

    類似于sleep_on_timeout()函數。進程在睡眠中可能在等待的時間沒有到達就被信號打斷而被喚醒,也可能是等待的時間到達而被喚醒。

    以上四個函數都是讓進程在等待隊列上睡眠,不過是小有詫異而已。

    在實際用的過程中,根據需要選擇合適的函數使用就是了。

    例如在對軟驅數據的讀寫中,如果設備沒有就緒則調用sleep_on()函數睡眠直到數據可讀(可寫),在打開串口的時候,如果串口端口處于關閉狀態則調用interruptible_sleep_on()函數嘗試等待其打開。

    在聲卡驅動中,讀取聲音數據時,如果沒有數據可讀,就會等待足夠常的時間直到可讀取。

    二:工作隊列

    在linux中斷處理中,有上半部和下半部之分,在下半部中主要來處理比較耗時的操作,其主要由工作隊列workqueue來完成。

    Linux 2.6內核使用了不少工作隊列來處理任務,他在使用上和 tasklet最大的不同是工作隊列的函數可以使用休眠,而tasklet的函數是不允許使用休眠的。

    工作隊列的使用又分兩種情況,

    一種是利用系統共享的工作隊列來添加自己的工作,這種情況處理函數不能消耗太多時間,這樣會影響共享隊列中其他任務的處理;

    另外一種是創建自己的工作隊列并添加工作。

    我們把推后執行的任務叫做工作(work),其數據結構為work_struct。

    這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct,而工作線程就是負責執行工作隊列中的工作。系統默認的工作者線程為events,自己也可以創建自己的工作者線程。

    首先來看看這倆個重要的數據結構:work_struct 和 workqueue_struct。

    struct workqueue_struct {

    struct cpu_workqueue_struct *cpu_wq;

    struct list_head list;

    const char *name;

    int singlethread;

    int freezeable; /* Freeze threads during suspend */

    int rt;

    #ifdef CONFIG_LOCKDEP

    struct lockdep_map lockdep_map;

    #endif

    };

    struct work_struct {

    atomic_long_t data;

    #define WORK_STRUCT_PENDING 0 /* T if work item pending execution */

    #define WORK_STRUCT_FLAG_MASK (3UL)

    #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)

    struct list_head entry;

    work_func_t func; //typedef void (*work_func_t)(struct work_struct *work);為函數指針

    #ifdef CONFIG_LOCKDEP

    struct lockdep_map lockdep_map;

    #endif

    };

    系統默認workqueue_struct定義如下

    static struct workqueue_struct *keventd_wq __read_mostly;

    keventd_wq = create_workqueue("events"); //名字為events

    工作隊列的創建方法:將work_struct添加到系統默認的工作隊列中,添加work_struct到自定義的隊列中

    (1):將work_struct添加到系統默認的工作隊列中

    a:聲明或編寫一個工作處理函數

    void my_func(void *data); //相當與一個任務處理函數task(),會周期性的執行。

    b:在創建工作work_struct時候,有倆種方法,即編譯時和運行時。

    編譯時

    創建名為my_work的結構體變量并把函數入口地址和參數地址賦給它;

    創建一個工作結構體變量,并將處理函數和參數的入口地址賦給這個工作結構體變量

    DECLARE_WORK(my_work,my_func,&data);

    運行時

    struct work_struct my_work; //創建一個名為my_work的結構體變量,創建后才能使用INIT_WORK()

    INIT_WORK(&my_work,my_func,&data); //初始化已經創建的my_work,其實就是往這個結構體變量中添加處理函數的入口地址和data的地址,通常在驅動的open函數中完成

    c:將工作結構體變量添加入系統的共享工作隊列

    schedule_work(&my_work); //添加my_work到keventd_wq中,一旦其所在的處理器上的工作者線程被喚醒,它就會被執行。

    或有時候并不希望工作馬上就被執行,而是希望它經過一段延遲以后再執行。

    在這種情況下,可以調度它在指定的時間執行:

    schedule_delayed_work(&my_work,tick); //延時tick個滴答后再提交工作這時,&my_work指向的work_struct直到delay指定的時鐘節拍用完以后才會執行。

    (2):創建自己的工作隊列來添加工作

    a:聲明工作處理函數和一個指向工作隊列的指針

    void my_func(void *data); //相當與一個任務處理函數task(),會周期性的執行。

    b:創建自己的工作隊列和工作結構體變量(通常在open函數中完成)

    struct workqueue_struct *p_queue;

    p_queue=create_workqueue("my_queue"); //創建一個名為my_queue的工作隊列并把工作隊列的入口地址賦給聲明的指針

    struct work_struct my_work;

    INIT_WORK(&my_work,my_func,&data); //創建一個工作結構體變量并初始化,和第一種情況的方法一樣

    c:將工作添加入自己創建的工作隊列等待執行

    queue_work(p_queue,&my_work);

    //作用與schedule_work()類似,不同的是將工作添加入p_queue指針指向的工作隊列而不是系統共享的工作隊列

    d:刪除自己的工作隊列

    destroy_workqueue(p_queue); //一般是在close函數中刪除

    其工作隊列的使用方法,可以參考linux中scsi_tgt_if.c函數中的代碼。

    static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd)

    {

    struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;

    dprintk("cmd %p %u\n", cmd, rq_data_dir(cmd->request));

    scsi_tgt_uspace_send_status(cmd, tcmd->itn_id, tcmd->tag);

    scsi_release_buffers(cmd);

    queue_work(scsi_tgtd, &tcmd->work); // scsi_tgtd = create_workqueue("scsi_tgtd");

    }

    預約申請免費試聽課

    填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

    上一篇:搞嵌入式驅動前途何在?
    下一篇:學習嵌入式過程中的一些感想和思考

    Linux系統中的”隊列”是什么?

    • 掃碼領取資料

      回復關鍵字:視頻資料

      免費領取 達內課程視頻學習資料

    • 視頻學習QQ群

      添加QQ群:1143617948

      免費領取達內課程視頻學習資料

    Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

    選擇城市和中心
    黑龍江省

    吉林省

    河北省

    湖南省

    貴州省

    云南省

    廣西省

    海南省

    欧美一级高清片,一级欧美免费大片视频,欧美三级在线电影免费 百度 好搜 搜狗
    <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>