新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux驅動總結3

        Linux驅動總結3

        作者: 時間:2016-12-01 來源:網絡 收藏
        文件操作支持的集合如下:
        /*添加該模塊的基本文件操作支持*/
        static const struct file_operations mem_fops =
        {
        /*結尾不是分號,注意其中的差別*/
        .owner = THIS_MODULE,
        .llseek = mem_llseek,
        .read = mem_read,
        .write = mem_write,
        .open = mem_open,
        .release = mem_release,
        /*添加新的操作支持*/
        .unlocked_ioctl = mem_ioctl,
        };
        需要注意不是ioctl,而是unlocked_ioctl。
        二、設備的堵塞讀寫方式實現,通常采用等待隊列。
        設備的堵塞讀寫方式,默認情況下的讀寫操作都是堵塞型的,具體的就是如果需要讀數據,當設備中沒有數據可讀的時候應該等待設備中有設備再讀,當往設備中寫數據時,如果上一次的數據還沒有被讀完成,則不應該寫入數據,就會導致進程的堵塞,等待數據可讀寫。但是在應用程序中也可以采用非堵塞型的方式進行讀寫。只要在打開文件的時候添加一個O_NONBLOCK,這樣在不能讀寫的時候就會直接返回,而不會等待。
        因此我們在實際設計驅動設備的同時需要考慮讀寫操作的堵塞方式。堵塞方式的設計主要是通過等待隊列實現,通常是將等待隊列(實質就是一個鏈表)的頭作為設備數據結構的一部分。在設備初始化過程中初始化等待隊列的頭。最后在設備讀寫操作的實現添加相應的等待隊列節點,并進行相應的控制。
        等待隊列的操作基本如下:
        1、等待隊列的頭定義并初始化的過程如下:
        方法一:
        struct wait_queue_head_t mywaitqueue;
        init_waitqueue_head(&mywaitqueue);
        方法二:
        DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);
        以上的兩種都能實現定義和初始化等待隊列頭。
        2、創建、移除一個等待隊列的節點,并添加、移除相應的隊列。
        定義一個等待隊列的節點:DECLARE_WAITQUEUE(wait,tsk)
        其中tsk表示一個進程,可以采用current當前的進程。
        添加到定義好的等待隊列頭中。
        add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
        即:add_wait_queue(&mywaitqueue,&wait);
        移除等待節點
        remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
        即:remove_wait_queue(&mywaitqueue,&wait);
        3、等待事件
        wait_event(queue,condition);當condition為真時,等待隊列頭queue對應的隊列被喚醒,否則繼續堵塞。這種情況下不能被信號打斷。
        wait_event_interruptible(queue,condition);當condition為真時,等待隊列頭queue對應的隊列被喚醒,否則繼續堵塞。這種情況下能被信號打斷。
        4、喚醒等待隊列
        wait_up(wait_queue_head_t *q),喚醒該等待隊列頭對應的所有等待。
        wait_up_interruptible(wait_queue_head_t *q)喚醒處于TASK_INTERRUPTIBLE的等待進程。
        應該成對的使用。即wait_event于wait_up,而wait_event_interruptible與wait_up_interruptible。
        wait_event和wait_event_interruptible的實現都是采用宏的方式,都是一個重新調度的過程,如下所示:
        #define wait_event_interruptible(wq, condition)
        ({
        int __ret = 0;
        if (!(condition))
        __wait_event_interruptible(wq, condition, __ret);
        __ret;
        })
        #define __wait_event_interruptible(wq, condition, ret)
        do {
        /*此處存在一個聲明等待隊列的語句,因此不需要再重新定義一個等待隊列節點*/
        DEFINE_WAIT(__wait);
        for (;;) {
        /*此處就相當于add_wait_queue()操作,具體參看代碼如下所示*/
        prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
        if (condition)
        break;
        if (!signal_pending(current)) {
        /*此處是調度,丟失CPU,因此需要wake_up函數喚醒當前的進程
        根據定義可知,如果條件不滿足,進程就失去CPU,能夠跳出for循環的出口只有
        1、當條件滿足時2、當signal_pending(current)=1時。
        1、就是滿足條件,也就是說wake_up函數只是退出了schedule函數,
        而真正退出函數還需要滿足條件
        2、說明進程可以被信號喚醒。也就是信號可能導致沒有滿足條件時就喚醒當前的進程。
        這也是后面的代碼采用while判斷的原因.防止被信號喚醒。
        */
        schedule();
        continue;
        }
        ret = -ERESTARTSYS;
        break;
        }
        finish_wait(&wq, &__wait);
        } while (0)
        #define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
        #define DEFINE_WAIT_FUNC(name, function)
        wait_queue_t name = {
        .private = current,
        .func = function,
        .task_list = LIST_HEAD_INIT((name).task_list),
        }
        void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
        {
        unsigned long flags;
        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        if (list_empty(&wait->task_list))
        /*添加節點到等待隊列*/
        __add_wait_queue(q, wait);
        set_current_state(state);
        spin_unlock_irqrestore(&q->lock, flags);
        }
        喚醒的操作也是類似的。
        #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
        void __wake_up(wait_queue_head_t *q, unsigned int mode,
        int nr_exclusive, void *key)
        {
        unsigned long flags;
        spin_lock_irqsave(&q->lock, flags);
        __wake_up_common(q, mode, nr_exclusive, 0, key);
        spin_unlock_irqrestore(&q->lock, flags);
        }
        static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
        int nr_exclusive, int wake_flags, void *key)
        {
        wait_queue_t *curr, *next;
        list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
        unsigned flags = curr->flags;
        if (curr->func(curr, mode, wake_flags, key) &&
        (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
        break;
        }
        }
        等待隊列通常用在驅動程序設計中的堵塞讀寫操作,并不需要手動的添加節點到隊列中,直接調用即可實現,具體的實現方法如下:
        1、在設備結構體中添加等待隊列頭,由于讀寫都需要堵塞,所以添加兩個隊列頭,分別用來堵塞寫操作,寫操作。
        #include
        struct mem_dev
        {
        char *data;
        unsigned long size;
        /*添加一個并行機制*/
        spinlock_t lock;
        /*添加一個等待隊列t頭*/
        wait_queue_head_t rdqueue;
        wait_queue_head_t wrqueue;
        };
        2、然后在模塊初始化中初始化隊列頭:
        /*初始化函數*/
        static int memdev_init(void)
        {
        ....
        for(i = 0; i < MEMDEV_NR_DEVS; i)
        {
        mem_devp[i].size = MEMDEV_SIZE;
        /*對設備的數據空間分配空間*/
        mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
        /*問題,沒有進行錯誤的控制*/
        memset(mem_devp[i].data,0,MEMDEV_SIZE);
        /*初始化定義的互信息量*/
        //初始化定義的自旋鎖ua
        spin_lock_init(&(mem_devp[i].lock));
        /*初始化兩個等待隊列頭,需要注意必須用括號包含起來,使得優先級正確*/
        init_waitqueue_head(&(mem_devp[i].rdqueue));
        init_waitqueue_head(&(mem_devp[i].wrqueue));
        }

        關鍵詞: Linux驅動總

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 改则县| 天祝| 磴口县| 古丈县| 尼玛县| 嵊州市| 精河县| 宿州市| 哈密市| 宿迁市| 平阴县| 儋州市| 卓资县| 霍林郭勒市| 彭州市| 阳朔县| 甘泉县| 忻州市| 西乌珠穆沁旗| 合江县| 彭州市| 枝江市| 闸北区| 灵台县| 三江| 博白县| 舒城县| 宁夏| 大埔县| 奉贤区| 娄底市| 高碑店市| 梁河县| 舟山市| 布拖县| 营口市| 平舆县| 云龙县| 如东县| 林口县| 扶绥县|