<nobr id="eeyoh"><big id="eeyoh"></big></nobr>

  • <button id="eeyoh"><acronym id="eeyoh"><cite id="eeyoh"></cite></acronym></button>

    <dd id="eeyoh"></dd>
    1. <dd id="eeyoh"><center id="eeyoh"></center></dd>

      <th id="eeyoh"></th>
      1. <span id="eeyoh"></span>
        更多課程 選擇中心

        嵌入式培訓
        達內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號 達內時代科技集團有限公司 版權所有

        選擇城市和中心
        黑龍江省

        吉林省

        河北省

        湖南省

        貴州省

        云南省

        廣西省

        海南省

        中文第一社区天天射干20191024香蕉视频1024香蕉视频操逼逼网 百度 好搜 搜狗
        <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>